/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include #include #include #include #include #include #include #include #include #include namespace AZ { namespace RPI { // Initialization & Shutdown... void PassLibrary::Init() { AddCoreTemplates(); } void PassLibrary::Shutdown() { m_isShuttingDown = true; m_passNameMapping.clear(); m_templateEntries.clear(); m_templateMappingAssets.clear(); Data::AssetBus::MultiHandler::BusDisconnect(); } // Getters... PassLibrary::TemplateEntry* PassLibrary::GetEntry(const Name& templateName) { auto itr = m_templateEntries.find(templateName); if (itr != m_templateEntries.end()) { return &(itr->second); } return nullptr; } const PassLibrary::TemplateEntry* PassLibrary::GetEntry(const Name& templateName) const { auto itr = m_templateEntries.find(templateName); if (itr != m_templateEntries.end()) { return &(itr->second); } return nullptr; } const AZStd::shared_ptr PassLibrary::GetPassTemplate(const Name& templateName) const { const TemplateEntry* entry = GetEntry(templateName); return entry ? entry->m_template : nullptr; } const AZStd::vector& PassLibrary::GetPassesForTemplate(const Name& templateName) const { static AZStd::vector emptyPassList; const TemplateEntry* entry = GetEntry(templateName); return entry ? entry->m_passes : emptyPassList; } bool PassLibrary::HasTemplate(const Name& templateName) const { return m_templateEntries.find(templateName) != m_templateEntries.end(); } bool PassLibrary::HasPassesForTemplate(const Name& templateName) const { return (GetPassesForTemplate(templateName).size() > 0); } AZStd::vector PassLibrary::FindPasses(const PassFilter& passFilter) const { const Name* passName = passFilter.GetPassName(); AZStd::vector result; if (passName) { // If the pass' name is known, find passes with matching names first const auto constItr = m_passNameMapping.find(*passName); if (constItr == m_passNameMapping.end()) { return result; } const AZStd::vector& passes = constItr->second; for (Pass* pass : passes) { if (passFilter.Matches(pass)) { result.push_back(pass); } } } else { // If the filter doesn't know matching pass' name, need to go through all registered passes for (auto& namePasses : m_passNameMapping) { for (Pass* pass : namePasses.second) { if (passFilter.Matches(pass)) { result.push_back(pass); } } } } return result; } // Add Functions... void PassLibrary::AddPass(Pass* pass) { if (pass->m_template) { TemplateEntry* entry = GetEntry(pass->m_template->m_name); if (entry) { entry->m_passes.push_back(pass); } } m_passNameMapping[pass->m_name].push_back(pass); } void PassLibrary::AddCoreTemplates() { // Put calls to pass template creation functions here... AddCopyPassTemplate(); } void PassLibrary::AddCopyPassTemplate() { AZStd::shared_ptr passTemplate = AZStd::make_shared(); passTemplate->m_passClass = "CopyPass"; passTemplate->m_name = "CopyPassTemplate"; PassSlot inputSlot; inputSlot.m_name = "Input"; inputSlot.m_slotType = PassSlotType::Input; inputSlot.m_scopeAttachmentUsage = RHI::ScopeAttachmentUsage::Copy; inputSlot.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load; passTemplate->m_slots.emplace_back(inputSlot); PassSlot outputSlot; outputSlot.m_name = "Output"; outputSlot.m_slotType = PassSlotType::Output; outputSlot.m_scopeAttachmentUsage = RHI::ScopeAttachmentUsage::Copy; outputSlot.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Clear; passTemplate->m_slots.emplace_back(outputSlot); AddPassTemplate(passTemplate->m_name, std::move(passTemplate)); } bool PassLibrary::AddPassTemplate(const Name& name, const AZStd::shared_ptr& passTemplate, bool hotReloading) { // Check if template already exists (unless we're hot reloading) if (!hotReloading && GetPassTemplate(name) != nullptr) { AZ_Warning("PassLibrary", false, "Trying to add a PassTemplate that already exists in PassLibrary. Template name: %s", name.GetCStr()); return false; } if (!passTemplate) { AZ_Warning("PassLibrary", false, "Trying to add a null PassTemplate. Template name: %s", name.GetCStr()); return false; } if (passTemplate->m_name != name) { AZ_Warning("PassLibrary", false, "Pass template alias [%s] is different than its name [%s]", name.GetCStr(), passTemplate->m_name.GetCStr()); passTemplate->m_name = name; } ValidateDeviceFormats(passTemplate); m_templateEntries[name].m_template = std::move(passTemplate); return true; } void PassLibrary::RemovePassFromLibrary(Pass* pass) { if (m_isShuttingDown) { return; } // Remove from associated template if (pass->m_template) { TemplateEntry* entry = GetEntry(pass->m_template->m_name); if (entry) { [[maybe_unused]] auto iter = AZStd::remove(entry->m_passes.begin(), entry->m_passes.end(), pass); AZ_Assert((iter + 1) == entry->m_passes.end(), "Pass [%s] is being deleted but was not registered with it's PassTemlate [%s] in the PassLibrary.", pass->m_name.GetCStr(), pass->m_template->m_name.GetCStr()); // Delete the pass that is now at the end of the list entry->m_passes.pop_back(); } } // Remove pass from pass name AZ_Assert(m_passNameMapping.find(pass->GetName()) != m_passNameMapping.end(), "Pass [%s] is trying to be removed from PassLibrary but was not found in library", pass->GetName().GetCStr()); AZStd::vector& passes = m_passNameMapping[pass->GetName()]; for (auto itr = passes.begin(); itr != passes.end(); itr++) { if (*itr == pass) { passes.erase(itr); return; } } } // Pass Asset Functions... void PassLibrary::OnAssetReloaded(Data::Asset asset) { // Handle pass asset reload Data::Asset passAsset = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; if (passAsset && passAsset->GetPassTemplate()) { LoadPassAsset(passAsset->GetPassTemplate()->m_name, passAsset, true); return; } // Handle template mapping reload // Note: it's a known issue that when mapping asset got reloaded, we only handle the new entries Data::Asset templateMappings = { asset.GetAs(), AZ::Data::AssetLoadBehavior::PreLoad }; if (templateMappings) { auto itr = m_templateMappingAssets.find(asset->GetId()); if (itr != m_templateMappingAssets.end()) { LoadPassTemplateMappings(templateMappings); } } } bool PassLibrary::LoadPassAsset(const Name& name, const Data::Asset& passAsset, bool hotReloading) { if (!passAsset.IsReady()) { AZ_Error("PassAsset", false, "Failed to get pass asset. %s", passAsset.ToString().c_str()); return false; } if (!passAsset->GetPassTemplate()) { AZ_Error("PassAsset", false, "Pass asset does not contain a pass template. %s", passAsset.ToString().c_str()); return false; } AZStd::shared_ptr passTemplate = passAsset->GetPassTemplate()->Clone(); bool success = AddPassTemplate(name, std::move(passTemplate), hotReloading); if (success) { TemplateEntry& entry = m_templateEntries[name]; entry.m_asset = passAsset; if (hotReloading) { for (Pass* pass : entry.m_passes) { if (pass->m_pipeline) { pass->m_pipeline->SetPassNeedsRecreate(); } } } } return success; } bool PassLibrary::LoadPassAsset(const Name& name, const Data::AssetId& passAssetId) { Data::Asset passAsset; if (passAssetId.IsValid()) { passAsset = Data::AssetManager::Instance().GetAsset(passAssetId, AZ::Data::AssetLoadBehavior::PreLoad); passAsset.BlockUntilLoadComplete(); } bool loadSuccess = LoadPassAsset(name, passAsset); if (loadSuccess) { Data::AssetBus::MultiHandler::BusConnect(passAssetId); } return loadSuccess; } bool PassLibrary::LoadPassTemplateMappings(const AZStd::string& templateMappingPath) { Data::Asset mappingAsset = AssetUtils::LoadCriticalAsset(templateMappingPath.c_str(), AssetUtils::TraceLevel::Error); if (m_templateMappingAssets.find(mappingAsset.GetId()) != m_templateMappingAssets.end()) { AZ_Warning("PassLibrary", false, "Pass template mapping [%s] was already loaded", mappingAsset.GetHint().c_str()); return true; } bool success = LoadPassTemplateMappings(mappingAsset); if (success) { Data::AssetBus::MultiHandler::BusConnect(mappingAsset->GetId()); } return success; } bool PassLibrary::LoadPassTemplateMappings(Data::Asset mappingAsset) { if (mappingAsset.IsReady()) { const AssetAliases* mappings = GetDataFromAnyAsset(mappingAsset); if (mappings == nullptr) { AZ_Error("PassLibrary", false, "Asset [%s] doesn't have assetAliases data", mappingAsset.GetHint().c_str()); return false; } const AZStd::unordered_map& assetMapping = mappings->GetAssetMapping(); Data::AssetId mappingAssetId = mappingAsset.GetId(); m_templateEntries.reserve(m_templateEntries.size() + assetMapping.size()); for (const auto& assetInfo : assetMapping) { Name templateName = AZ::Name(assetInfo.first); if (!HasTemplate(templateName)) { bool loaded = LoadPassAsset(templateName, assetInfo.second); if (loaded) { auto& entry = m_templateEntries[templateName]; entry.m_mappingAssetId = mappingAssetId; } } else { // Report a warning if the template was setup in another mappping asset. // We won't report a warning if the template was loaded from same asset. This only happens when the asset got reloaded. if (m_templateEntries[templateName].m_mappingAssetId != mappingAssetId) { AZ_Warning("PassLibrary", false, "Template [%s] was aleady added to the library. Duplicated template from [%s]", templateName.GetCStr(), mappingAsset.ToString().c_str()); } } } m_templateMappingAssets[mappingAsset->GetId()] = mappingAsset; return true; } return false; } void PassLibrary::ValidateDeviceFormats(const AZStd::shared_ptr& passTemplate) { // Validate image attachments for (PassImageAttachmentDesc& imageAttachment : passTemplate->m_imageAttachments) { RHI::Format format = imageAttachment.m_imageDescriptor.m_format; AZStd::string formatLocation = AZStd::string::format("PassAttachmentDesc [%s] on PassTemplate [%s]", imageAttachment.m_name.GetCStr(), passTemplate->m_name.GetCStr()); imageAttachment.m_imageDescriptor.m_format = RHI::ValidateFormat(format, formatLocation.c_str(), imageAttachment.m_formatFallbacks); } // Validate slot views for (PassSlot& slot : passTemplate->m_slots) { if (slot.m_imageViewDesc) { RHI::Format format = slot.m_imageViewDesc->m_overrideFormat; AZStd::string formatLocation = AZStd::string::format("ImageViewDescriptor on Slot [%s] in PassTemplate [%s]", slot.m_name.GetCStr(), passTemplate->m_name.GetCStr()); RHI::FormatCapabilities capabilities = RHI::GetCapabilities(slot.m_scopeAttachmentUsage, slot.GetAttachmentAccess(), RHI::AttachmentType::Image); slot.m_imageViewDesc->m_overrideFormat = RHI::ValidateFormat(format, formatLocation.c_str(), slot.m_formatFallbacks, capabilities); } if (slot.m_bufferViewDesc) { RHI::Format format = slot.m_bufferViewDesc->m_elementFormat; AZStd::string formatLocation = AZStd::string::format("BufferViewDescriptor on Slot [%s] in PassTemplate [%s]", slot.m_name.GetCStr(), passTemplate->m_name.GetCStr()); RHI::FormatCapabilities capabilities = RHI::GetCapabilities(slot.m_scopeAttachmentUsage, slot.GetAttachmentAccess(), RHI::AttachmentType::Image); slot.m_bufferViewDesc->m_elementFormat = RHI::ValidateFormat(format, formatLocation.c_str(), slot.m_formatFallbacks, capabilities); } } } } // namespace RPI } // namespace AZ