You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
516 lines
18 KiB
C++
516 lines
18 KiB
C++
/*
|
|
* 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 <AzCore/Asset/AssetManager.h>
|
|
#include <AzCore/Asset/AssetManagerBus.h>
|
|
#include <AzCore/Component/Entity.h>
|
|
#include <AzCore/Debug/EventTrace.h>
|
|
#include <AzCore/IO/FileIO.h>
|
|
#include <AzCore/IO/SystemFile.h>
|
|
#include <AzCore/Serialization/Utils.h>
|
|
#include <AzCore/Serialization/SerializeContext.h>
|
|
#include <AzCore/std/sort.h>
|
|
#include <AzCore/Interface/Interface.h>
|
|
|
|
#include <AzCore/Serialization/Json/JsonUtils.h>
|
|
|
|
#include <Atom/RHI/CpuProfiler.h>
|
|
#include <Atom/RHI/FrameGraphBuilder.h>
|
|
|
|
#include <Atom/RPI.Public/Pass/FullscreenTrianglePass.h>
|
|
#include <Atom/RPI.Public/Pass/PassDefines.h>
|
|
#include <Atom/RPI.Public/Pass/PassFactory.h>
|
|
#include <Atom/RPI.Public/Pass/PassSystem.h>
|
|
#include <Atom/RPI.Public/Pass/PassLibrary.h>
|
|
#include <Atom/RPI.Public/Pass/Specific/SwapChainPass.h>
|
|
#include <Atom/RPI.Public/RenderPipeline.h>
|
|
|
|
#include <Atom/RPI.Reflect/Pass/ComputePassData.h>
|
|
#include <Atom/RPI.Reflect/Pass/CopyPassData.h>
|
|
#include <Atom/RPI.Reflect/Pass/DownsampleMipChainPassData.h>
|
|
#include <Atom/RPI.Reflect/Pass/FullscreenTrianglePassData.h>
|
|
#include <Atom/RPI.Reflect/Pass/EnvironmentCubeMapPassData.h>
|
|
#include <Atom/RPI.Reflect/Pass/RenderToTexturePassData.h>
|
|
#include <Atom/RPI.Reflect/Pass/PassAsset.h>
|
|
#include <Atom/RPI.Reflect/Pass/PassData.h>
|
|
#include <Atom/RPI.Reflect/Pass/PassTemplate.h>
|
|
#include <Atom/RPI.Reflect/Pass/RasterPassData.h>
|
|
#include <Atom/RPI.Reflect/Pass/RenderPassData.h>
|
|
|
|
namespace AZ
|
|
{
|
|
namespace RPI
|
|
{
|
|
|
|
PassSystemInterface* PassSystemInterface::Get()
|
|
{
|
|
return Interface<PassSystemInterface>::Get();
|
|
}
|
|
|
|
PassSystem::PassSystem()
|
|
{
|
|
}
|
|
|
|
void PassSystem::Reflect(AZ::ReflectContext* context)
|
|
{
|
|
PassAttachmentRef::Reflect(context);
|
|
PassConnection::Reflect(context);
|
|
PassFallbackConnection::Reflect(context);
|
|
PassAttachmentSizeMultipliers::Reflect(context);
|
|
PassAttachmentSizeSource::Reflect(context);
|
|
PassAttachmentDesc::Reflect(context);
|
|
PassImageAttachmentDesc::Reflect(context);
|
|
PassBufferAttachmentDesc::Reflect(context);
|
|
PassSlot::Reflect(context);
|
|
|
|
PassData::Reflect(context);
|
|
CopyPassData::Reflect(context);
|
|
RenderPassData::Reflect(context);
|
|
ComputePassData::Reflect(context);
|
|
DownsampleMipChainPassData::Reflect(context);
|
|
RasterPassData::Reflect(context);
|
|
FullscreenTrianglePassData::Reflect(context);
|
|
EnvironmentCubeMapPassData::Reflect(context);
|
|
RenderToTexturePassData::Reflect(context);
|
|
|
|
PassAsset::Reflect(context);
|
|
PassTemplate::Reflect(context);
|
|
PassRequest::Reflect(context);
|
|
}
|
|
|
|
void PassSystem::GetAssetHandlers(AssetHandlerPtrList& assetHandlers)
|
|
{
|
|
assetHandlers.emplace_back(MakeAssetHandler<PassAssetHandler>());
|
|
}
|
|
|
|
void PassSystem::Init()
|
|
{
|
|
m_state = PassSystemState::InitializingPassSystem;
|
|
|
|
Interface<PassSystemInterface>::Register(this);
|
|
m_passLibrary.Init();
|
|
m_passFactory.Init(&m_passLibrary);
|
|
m_rootPass = CreatePass<ParentPass>(Name{"Root"});
|
|
m_rootPass->m_flags.m_partOfHierarchy = true;
|
|
m_rootPass->m_flags.m_createChildren = false;
|
|
|
|
// Here you can specify the name of a pass you would like to break into during execution
|
|
// If you enable AZ_RPI_ENABLE_PASS_DEBUGGING, then any pass matching the specified name will debug
|
|
// break on any instance of the AZ_RPI_BREAK_ON_TARGET_PASS macro. See Pass::Build for an example
|
|
// m_targetedPassDebugName = "MyPassName";
|
|
|
|
m_state = PassSystemState::Idle;
|
|
}
|
|
|
|
void PassSystem::InitPassTemplates()
|
|
{
|
|
AZ_Assert(m_rootPass, "PassSystem::Init() need to be called");
|
|
m_loadTemplatesEvent.Signal();
|
|
}
|
|
|
|
bool PassSystem::LoadPassTemplateMappings(const AZStd::string& templateMappingPath)
|
|
{
|
|
return m_passLibrary.LoadPassTemplateMappings(templateMappingPath);
|
|
}
|
|
|
|
void PassSystem::WriteTemplateToFile(const PassTemplate& passTemplate, AZStd::string_view assetFilePath)
|
|
{
|
|
PassAsset passAsset;
|
|
passAsset.m_passTemplate = passTemplate.CloneUnique();
|
|
JsonSerializationUtils::SaveObjectToFile(&passAsset, assetFilePath);
|
|
}
|
|
|
|
void PassSystem::QueueForBuild(Pass* pass)
|
|
{
|
|
AZ_Assert(pass != nullptr, "Queuing nullptr pass in PassSystem::QueueForBuild");
|
|
m_buildPassList.push_back(pass);
|
|
}
|
|
|
|
void PassSystem::QueueForRemoval(Pass* pass)
|
|
{
|
|
AZ_Assert(pass != nullptr, "Queuing nullptr pass in PassSystem::QueueForRemoval");
|
|
m_removePassList.push_back(pass);
|
|
}
|
|
|
|
void PassSystem::QueueForInitialization(Pass* pass)
|
|
{
|
|
AZ_Assert(pass != nullptr, "Queuing nullptr pass in PassSystem::QueueForInitialization");
|
|
m_initializePassList.push_back(pass);
|
|
}
|
|
|
|
// Sort so passes with less depth (closer to the root) are first. Used when changes
|
|
// in the parent passes can affect the child passes, like with attachment building.
|
|
void SortPassListAscending(AZStd::vector< Ptr<Pass> >& passList)
|
|
{
|
|
AZStd::sort(passList.begin(), passList.end(),
|
|
[](const Ptr<Pass>& lhs, const Ptr<Pass>& rhs)
|
|
{
|
|
return (lhs->GetTreeDepth() < rhs->GetTreeDepth());
|
|
});
|
|
}
|
|
|
|
// Sort so passes with greater depth (further from the root) get called first. Used in the case of
|
|
// delete, as we want to avoid deleting the parent first since this invalidates the child pointer.
|
|
void SortPassListDescending(AZStd::vector< Ptr<Pass> >& passList)
|
|
{
|
|
AZStd::sort(passList.begin(), passList.end(),
|
|
[](const Ptr<Pass>& lhs, const Ptr<Pass>& rhs)
|
|
{
|
|
return (lhs->GetTreeDepth() > rhs->GetTreeDepth());
|
|
}
|
|
);
|
|
}
|
|
|
|
void PassSystem::RemovePasses()
|
|
{
|
|
m_state = PassSystemState::RemovingPasses;
|
|
AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: RemovePasses");
|
|
|
|
if (!m_removePassList.empty())
|
|
{
|
|
SortPassListDescending(m_removePassList);
|
|
|
|
for (const Ptr<Pass>& pass : m_removePassList)
|
|
{
|
|
pass->RemoveFromParent();
|
|
}
|
|
|
|
m_removePassList.clear();
|
|
}
|
|
|
|
m_state = PassSystemState::Idle;
|
|
}
|
|
|
|
void PassSystem::BuildPasses()
|
|
{
|
|
m_state = PassSystemState::BuildingPasses;
|
|
AZ_PROFILE_FUNCTION(AzRender);
|
|
AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: BuildPassAttachments");
|
|
|
|
m_passHierarchyChanged = m_passHierarchyChanged || !m_buildPassList.empty();
|
|
|
|
// While loop is for the event in which passes being built add more pass to m_buildPassList
|
|
while(!m_buildPassList.empty())
|
|
{
|
|
AZ_Assert(m_removePassList.empty(), "Passes shouldn't be queued removal during build attachment process");
|
|
|
|
AZStd::vector< Ptr<Pass> > buildListCopy = m_buildPassList;
|
|
m_buildPassList.clear();
|
|
|
|
// Erase passes which were removed from pass tree already (which parent is empty)
|
|
auto unused = AZStd::remove_if(buildListCopy.begin(), buildListCopy.end(),
|
|
[](const RHI::Ptr<Pass>& currentPass)
|
|
{
|
|
return !currentPass->m_flags.m_partOfHierarchy;
|
|
});
|
|
buildListCopy.erase(unused, buildListCopy.end());
|
|
|
|
SortPassListAscending(buildListCopy);
|
|
|
|
for (const Ptr<Pass>& pass : buildListCopy)
|
|
{
|
|
pass->Reset();
|
|
}
|
|
for (const Ptr<Pass>& pass : buildListCopy)
|
|
{
|
|
pass->Build(true);
|
|
}
|
|
}
|
|
|
|
if (m_passHierarchyChanged)
|
|
{
|
|
#if AZ_RPI_ENABLE_PASS_DEBUGGING
|
|
if (!m_isHotReloading)
|
|
{
|
|
AZ_Printf("PassSystem", "\nFinished building passes:\n");
|
|
DebugPrintPassHierarchy();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
m_state = PassSystemState::Idle;
|
|
}
|
|
|
|
void PassSystem::InitializePasses()
|
|
{
|
|
m_state = PassSystemState::InitializingPasses;
|
|
AZ_PROFILE_FUNCTION(AzRender);
|
|
AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: BuildPassAttachments");
|
|
|
|
m_passHierarchyChanged = m_passHierarchyChanged || !m_initializePassList.empty();
|
|
|
|
while (!m_initializePassList.empty())
|
|
{
|
|
AZStd::vector< Ptr<Pass> > initListCopy = m_initializePassList;
|
|
m_initializePassList.clear();
|
|
|
|
// Erase passes which were removed from pass tree already (which parent is empty)
|
|
auto unused = AZStd::remove_if(initListCopy.begin(), initListCopy.end(),
|
|
[](const RHI::Ptr<Pass>& currentPass)
|
|
{
|
|
return !currentPass->m_flags.m_partOfHierarchy;
|
|
});
|
|
initListCopy.erase(unused, initListCopy.end());
|
|
|
|
SortPassListAscending(initListCopy);
|
|
|
|
for (const Ptr<Pass>& pass : initListCopy)
|
|
{
|
|
pass->Initialize();
|
|
}
|
|
}
|
|
|
|
if (m_passHierarchyChanged)
|
|
{
|
|
// Signal all passes that we have finished initialization
|
|
m_rootPass->OnInitializationFinished();
|
|
}
|
|
|
|
m_state = PassSystemState::Idle;
|
|
}
|
|
|
|
void PassSystem::Validate()
|
|
{
|
|
m_state = PassSystemState::ValidatingPasses;
|
|
AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: Validate");
|
|
|
|
if (PassValidation::IsEnabled())
|
|
{
|
|
if (!m_passHierarchyChanged)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AZ_PROFILE_FUNCTION(AzRender);
|
|
|
|
PassValidationResults validationResults;
|
|
m_rootPass->Validate(validationResults);
|
|
validationResults.PrintValidationIfError();
|
|
}
|
|
|
|
m_state = PassSystemState::Idle;
|
|
}
|
|
|
|
void PassSystem::ProcessQueuedChanges()
|
|
{
|
|
AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: ProcessQueuedChanges");
|
|
RemovePasses();
|
|
BuildPasses();
|
|
InitializePasses();
|
|
Validate();
|
|
}
|
|
|
|
void PassSystem::FrameUpdate(RHI::FrameGraphBuilder& frameGraphBuilder)
|
|
{
|
|
AZ_PROFILE_FUNCTION(AzRender);
|
|
AZ_ATOM_PROFILE_FUNCTION("RPI", "PassSystem: FrameUpdate");
|
|
|
|
ResetFrameStatistics();
|
|
ProcessQueuedChanges();
|
|
|
|
m_state = PassSystemState::Rendering;
|
|
Pass::FramePrepareParams params{ &frameGraphBuilder };
|
|
|
|
{
|
|
AZ_ATOM_PROFILE_TIME_GROUP_REGION("RPI", "Pass: FrameBegin");
|
|
m_rootPass->FrameBegin(params);
|
|
}
|
|
}
|
|
|
|
void PassSystem::FrameEnd()
|
|
{
|
|
AZ_ATOM_PROFILE_FUNCTION("RHI", "PassSystem: FrameEnd");
|
|
|
|
m_state = PassSystemState::FrameEnd;
|
|
|
|
m_rootPass->FrameEnd();
|
|
|
|
// remove any pipelines that are marked as ExecuteOnce
|
|
// the immediate children of m_rootPass are the top-level nodes of each pipeline
|
|
for (const Ptr<Pass>& pass : m_rootPass->GetChildren())
|
|
{
|
|
RenderPipeline* pipeline = pass->m_pipeline;
|
|
if (pipeline && pipeline->IsExecuteOnce())
|
|
{
|
|
pipeline->RemoveFromScene();
|
|
}
|
|
}
|
|
|
|
m_passHierarchyChanged = false;
|
|
|
|
m_state = PassSystemState::Idle;
|
|
}
|
|
|
|
void PassSystem::Shutdown()
|
|
{
|
|
RemovePasses();
|
|
m_buildPassList.clear();
|
|
m_rootPass = nullptr;
|
|
m_passFactory.Shutdown();
|
|
m_passLibrary.Shutdown();
|
|
AZ_Assert(m_passCounter == 0, "Pass leaking has occurred! There are %d passes that have not been deleted.\n", m_passCounter);
|
|
Interface<PassSystemInterface>::Unregister(this);
|
|
}
|
|
|
|
const Ptr<ParentPass>& PassSystem::GetRootPass()
|
|
{
|
|
return m_rootPass;
|
|
}
|
|
|
|
PassSystemState PassSystem::GetState() const
|
|
{
|
|
return m_state;
|
|
}
|
|
|
|
bool PassSystem::IsHotReloading() const
|
|
{
|
|
return m_isHotReloading;
|
|
}
|
|
|
|
void PassSystem::SetHotReloading(bool hotReloading)
|
|
{
|
|
m_isHotReloading = hotReloading;
|
|
}
|
|
|
|
void PassSystem::DebugPrintPassHierarchy()
|
|
{
|
|
AZ_Printf("PassSystem", "\n------- PASS HIERARCHY -------\n");
|
|
m_rootPass->DebugPrint();
|
|
AZ_Printf("PassSystem", "\n------------------------------\n");
|
|
}
|
|
|
|
void PassSystem::SetTargetedPassDebuggingName(const AZ::Name& targetPassName)
|
|
{
|
|
m_targetedPassDebugName = targetPassName;
|
|
}
|
|
|
|
const AZ::Name& PassSystem::GetTargetedPassDebuggingName() const
|
|
{
|
|
return m_targetedPassDebugName;
|
|
}
|
|
|
|
void PassSystem::ConnectEvent(OnReadyLoadTemplatesEvent::Handler& handler)
|
|
{
|
|
handler.Connect(m_loadTemplatesEvent);
|
|
}
|
|
|
|
void PassSystem::ResetFrameStatistics()
|
|
{
|
|
m_frameStatistics.m_numRenderPassesExecuted = 0;
|
|
m_frameStatistics.m_totalDrawItemsRendered = 0;
|
|
m_frameStatistics.m_maxDrawItemsRenderedInAPass = 0;
|
|
}
|
|
|
|
PassSystemFrameStatistics PassSystem::GetFrameStatistics()
|
|
{
|
|
return m_frameStatistics;
|
|
}
|
|
|
|
void PassSystem::IncrementFrameDrawItemCount(u32 numDrawItems)
|
|
{
|
|
m_frameStatistics.m_totalDrawItemsRendered += numDrawItems;
|
|
m_frameStatistics.m_maxDrawItemsRenderedInAPass = AZStd::max(m_frameStatistics.m_maxDrawItemsRenderedInAPass, numDrawItems);
|
|
}
|
|
|
|
void PassSystem::IncrementFrameRenderPassCount()
|
|
{
|
|
++m_frameStatistics.m_numRenderPassesExecuted;
|
|
}
|
|
|
|
// --- Pass Factory Functions ---
|
|
|
|
void PassSystem::AddPassCreator(Name className, PassCreator createFunction)
|
|
{
|
|
m_passFactory.AddPassCreator(className, createFunction);
|
|
}
|
|
|
|
Ptr<Pass> PassSystem::CreatePassFromClass(Name passClassName, Name passName)
|
|
{
|
|
return m_passFactory.CreatePassFromClass(passClassName, passName);
|
|
}
|
|
|
|
Ptr<Pass> PassSystem::CreatePassFromTemplate(const AZStd::shared_ptr<PassTemplate>& passTemplate, Name passName)
|
|
{
|
|
return m_passFactory.CreatePassFromTemplate(passTemplate, passName);
|
|
}
|
|
|
|
Ptr<Pass> PassSystem::CreatePassFromTemplate(Name templateName, Name passName)
|
|
{
|
|
return m_passFactory.CreatePassFromTemplate(templateName, passName);
|
|
}
|
|
|
|
Ptr<Pass> PassSystem::CreatePassFromRequest(const PassRequest* passRequest)
|
|
{
|
|
return m_passFactory.CreatePassFromRequest(passRequest);
|
|
}
|
|
|
|
bool PassSystem::HasCreatorForClass(Name passClassName)
|
|
{
|
|
return m_passFactory.HasCreatorForClass(passClassName);
|
|
}
|
|
|
|
// --- Pass Library Functions ---
|
|
|
|
bool PassSystem::HasPassesForTemplateName(const Name& templateName) const
|
|
{
|
|
return m_passLibrary.HasPassesForTemplate(templateName);
|
|
}
|
|
|
|
const AZStd::vector<Pass*>& PassSystem::GetPassesForTemplateName(const Name& templateName) const
|
|
{
|
|
return m_passLibrary.GetPassesForTemplate(templateName);
|
|
}
|
|
|
|
bool PassSystem::AddPassTemplate(const Name& name, const AZStd::shared_ptr<PassTemplate>& passTemplate)
|
|
{
|
|
return m_passLibrary.AddPassTemplate(name, passTemplate);
|
|
}
|
|
|
|
const AZStd::shared_ptr<PassTemplate> PassSystem::GetPassTemplate(const Name& name) const
|
|
{
|
|
return m_passLibrary.GetPassTemplate(name);
|
|
}
|
|
|
|
void PassSystem::RemovePassFromLibrary(Pass* pass)
|
|
{
|
|
m_passLibrary.RemovePassFromLibrary(pass);
|
|
}
|
|
|
|
void PassSystem::RegisterPass(Pass* pass)
|
|
{
|
|
++m_passCounter;
|
|
m_passLibrary.AddPass(pass);
|
|
}
|
|
|
|
void PassSystem::UnregisterPass(Pass* pass)
|
|
{
|
|
RemovePassFromLibrary(pass);
|
|
--m_passCounter;
|
|
}
|
|
|
|
AZStd::vector<Pass*> PassSystem::FindPasses(const PassFilter& passFilter) const
|
|
{
|
|
return m_passLibrary.FindPasses(passFilter);
|
|
}
|
|
|
|
SwapChainPass* PassSystem::FindSwapChainPass(AzFramework::NativeWindowHandle windowHandle) const
|
|
{
|
|
for (const Ptr<Pass>& pass : m_rootPass->m_children)
|
|
{
|
|
SwapChainPass* swapChainPass = azrtti_cast<SwapChainPass*>(pass.get());
|
|
if (swapChainPass && swapChainPass->GetWindowHandle() == windowHandle)
|
|
{
|
|
return swapChainPass;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace RPI
|
|
} // namespace AZ
|