[new] ATOM-17253 Using AtomTressFX gem as an example to inject hair passes to main pipeline at run-time (#7661)

* Change AtomTressFX passes to write to exsiting DepthLinear buffer instead of creating a new one as output.

Signed-off-by: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com>

* Added new api in RPI to apply render pipeline changes from FP

Signed-off-by: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com>

* Update AtomTressFX gem to create hair parent pass at runtime

Signed-off-by: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com>

* Change Scene::ApplyRenderPipelineChange() to TryApplyRenderPIpelineChanges().
Have TryApplyRenderPIpelineChanges() called automatically when a render pipeline is added to a Scene.
Re-apply the render pipeline change when RenderPipeline got recreated (Pass hot-reloading support)
Add AddPassBefore() and AddPassAfter() function to RenderPIpeline class.

Signed-off-by: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com>
This commit is contained in:
Qing Tao
2022-02-17 08:50:50 -08:00
committed by GitHub
parent ca3018155e
commit 846e2736a5
23 changed files with 388 additions and 254 deletions
@@ -6,6 +6,7 @@
"Name": "MainPipeline",
"MainViewTag": "MainCamera",
"RootPassTemplate": "MainPipeline",
"AllowModification": true,
"RenderSettings": {
"MultisampleState": {
"samples": 4
@@ -30,6 +30,8 @@ namespace AZ
namespace RPI
{
class RenderPipeline;
//! @class FeatureProcessor
//! @brief Interface that FeatureProcessors should derive from
//! @detail FeatureProcceors will record simulation state from the simulation job graph into a buffer that is isolated from the asynchronous rendering graph.
@@ -87,6 +89,9 @@ namespace AZ
//! Perform any necessary deactivation.
virtual void Deactivate() {}
//! Apply changes and add additional render passes to the render pipeline from the feature processors
virtual void ApplyRenderPipelineChange(RenderPipeline* ) {}
//! Allows the feature processor to expose supporting views based on
//! the main views passed in. Main views (persistent views) are views that must be
//! rendered and impacts the presentation of the application. Support
@@ -66,6 +66,7 @@ namespace AZ
//! Inserts a pass at specified position
//! If the position is invalid, the child pass won't be added, and the function returns false
bool InsertChild(const Ptr<Pass>& child, ChildPassIndex position);
bool InsertChild(const Ptr<Pass>& child, uint32_t index);
//! Searches for a child pass with the given name. Returns the child's index if found, null index otherwise
ChildPassIndex FindChildPassIndex(const Name& passName) const;
@@ -184,6 +184,23 @@ namespace AZ
//! Get draw filter mask
RHI::DrawFilterMask GetDrawFilterMask() const;
//! Get the RenderPipelineDescriptor which was used to create this RenderPipeline
const RenderPipelineDescriptor& GetDescriptor() const;
// Helper functions to modify the passes of this render pipeline
//! Find a reference pass's location and add the new pass before the reference pass
//! After the new pass was inserted, the new pass and the reference pass are siblings
bool AddPassBefore(Ptr<Pass> newPass, const AZ::Name& referencePassName);
//! Find a reference pass's location and add the new pass after the reference pass
//! After the new pass was inserted, the new pass and the reference pass are siblings
bool AddPassAfter(Ptr<Pass> newPass, const AZ::Name& referencePassName);
//! Find the first pass with matching name in the render pipeline
//! Note: to find all the passes with matching name in this render pipeline,
//! use RPI::PassSystemInterface::Get()->ForEachPass() function instead.
Ptr<Pass> FindFirstPass(const AZ::Name& passName);
private:
RenderPipeline() = default;
@@ -238,8 +255,8 @@ namespace AZ
PipelineViewTag m_mainViewTag;
// Whether the pipeline should be removed after one execution
bool m_executeOnce = false;
// Was the pipeline modified by Scene's feature processor
bool m_wasModifiedByScene = false;
// The window handle associated with this render pipeline if it's created for a window
AzFramework::NativeWindowHandle m_windowHandle = nullptr;
@@ -247,15 +264,15 @@ namespace AZ
// Render settings that can be queried by passes to setup things like render target resolution
PipelineRenderSettings m_activeRenderSettings;
// Original settings from RenderPipelineDescriptor, used to revert active render settings to original settings from RenderPipelineDescriptor
PipelineRenderSettings m_originalRenderSettings;
// A tag to filter draw items submitted by passes of this render pipeline.
// This tag is allocated when it's added to a scene. It's set to invalid when it's removed to the scene.
RHI::DrawFilterTag m_drawFilterTag;
// A mask to filter draw items submitted by passes of this render pipeline.
// This mask is created from the value of m_drawFilterTag.
RHI::DrawFilterMask m_drawFilterMask = 0;
RHI::DrawFilterMask m_drawFilterMask = 0;
// The descriptor used to created this render pipeline
RenderPipelineDescriptor m_descriptor;
};
} // namespace RPI
@@ -169,6 +169,9 @@ namespace AZ
//! This function is called every time scene's render pipelines change.
//! User may call this function explicitly if render pipelines were changed
void RebuildPipelineStatesLookup();
//! Try apply render pipeline changes from each feature processors if the pipeline allows modification and wasn't modified.
void TryApplyRenderPipelineChanges(RenderPipeline* pipeline);
protected:
// SceneFinder overrides...
@@ -192,6 +195,7 @@ namespace AZ
// This is called after PassSystem's FramePrepare so passes can still modify view srgs in its FramePrepareIntenal function before they are submitted to command list
void UpdateSrgs();
private:
Scene();
@@ -41,6 +41,9 @@ namespace AZ
//! Flag indicating if this pipeline should execute one time and then be removed.
bool m_executeOnce = false;
//! Flag indicating if this pipeline can accept modifications from scene's feature processors
bool m_allowModification = false;
};
} // namespace RPI
@@ -76,6 +76,16 @@ namespace AZ
}
bool ParentPass::InsertChild(const Ptr<Pass>& child, ChildPassIndex position)
{
if (!position.IsValid())
{
AZ_Assert(false, "Can't insert a child pass with invalid position");
return false;
}
return InsertChild(child, position.GetIndex());
}
bool ParentPass::InsertChild(const Ptr<Pass>& child, uint32_t index)
{
if (child->m_parent != nullptr)
{
@@ -83,13 +93,13 @@ namespace AZ
return false;
}
if (!position.IsValid() || position.GetIndex() > m_children.size())
if (index > m_children.size())
{
AZ_Assert(false, "Can't insert a child pass with invalid position");
return false;
}
auto insertPos = m_children.cbegin() + position.GetIndex();
auto insertPos = m_children.cbegin() + index;
m_children.insert(insertPos, child);
child->m_parent = this;
@@ -294,7 +294,7 @@ namespace AZ
void PassLibrary::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
{
// Handle pass asset reload
Data::Asset<PassAsset> passAsset = Data::static_pointer_cast<PassAsset>(asset);
Data::Asset<PassAsset> passAsset = { asset.GetAs<PassAsset>() , AZ::Data::AssetLoadBehavior::PreLoad};
if (passAsset && passAsset->GetPassTemplate())
{
LoadPassAsset(passAsset->GetPassTemplate()->m_name, passAsset, true);
@@ -370,6 +370,7 @@ namespace AZ
m_commonShaderAssetForSrgs = AssetUtils::LoadCriticalAsset<ShaderAsset>( m_descriptor.m_commonSrgsShaderAssetPath.c_str());
if (!m_commonShaderAssetForSrgs.IsReady())
{
AZ_Error("RPI system", false, "Failed to load RPI system asset %s", m_descriptor.m_commonSrgsShaderAssetPath.c_str());
return;
}
m_sceneSrgLayout = m_commonShaderAssetForSrgs->FindShaderResourceGroupLayout(SrgBindingSlot::Scene);
@@ -395,6 +396,7 @@ namespace AZ
m_passSystem.InitPassTemplates();
m_systemAssetsInitialized = true;
AZ_TracePrintf("RPI system", "System assets initialized\n");
}
bool RPISystem::IsInitialized() const
@@ -9,6 +9,7 @@
#include <Atom/RHI/DrawListTagRegistry.h>
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/PassSystem.h>
#include <Atom/RPI.Public/Pass/Specific/SwapChainPass.h>
#include <Atom/RPI.Public/RenderPipeline.h>
@@ -97,10 +98,9 @@ namespace AZ
void RenderPipeline::InitializeRenderPipeline(RenderPipeline* pipeline, const RenderPipelineDescriptor& desc)
{
pipeline->m_descriptor = desc;
pipeline->m_mainViewTag = Name(desc.m_mainViewTagName);
pipeline->m_nameId = desc.m_name.data();
pipeline->m_executeOnce = desc.m_executeOnce;
pipeline->m_originalRenderSettings = desc.m_renderSettings;
pipeline->m_activeRenderSettings = desc.m_renderSettings;
pipeline->m_rootPass->SetRenderPipeline(pipeline);
pipeline->m_rootPass->m_flags.m_isPipelineRoot = true;
@@ -337,6 +337,10 @@ namespace AZ
m_rootPass = newRoot;
passSystem->GetRootPass()->AddChild(m_rootPass);
// Re-Apply render pipeline change
m_wasModifiedByScene = false;
m_scene->TryApplyRenderPipelineChanges(this);
m_wasPassModified = true;
}
else
@@ -366,7 +370,7 @@ namespace AZ
bool RenderPipeline::IsExecuteOnce()
{
return m_executeOnce;
return m_descriptor.m_executeOnce;
}
void RenderPipeline::RemoveFromScene()
@@ -477,7 +481,7 @@ namespace AZ
void RenderPipeline::RevertRenderSettings()
{
m_activeRenderSettings = m_originalRenderSettings;
m_activeRenderSettings = m_descriptor.m_renderSettings;
}
void RenderPipeline::AddToRenderTickOnce()
@@ -527,5 +531,60 @@ namespace AZ
m_drawFilterMask = 0;
}
}
const RenderPipelineDescriptor& RenderPipeline::GetDescriptor() const
{
return m_descriptor;
}
bool RenderPipeline::AddPassBefore(Ptr<Pass> newPass, const AZ::Name& referencePassName)
{
auto foundPass = FindFirstPass(referencePassName);
if (!foundPass)
{
AZ_Warning("RenderPipeline", false, "Add pass to render pipeline failed: can't find reference pass [%s] in render pipeline [%s]",
referencePassName.GetCStr(), GetId().GetCStr());
return false;
}
// insert the pass
auto parentPass = foundPass->GetParent();
auto passIndex = parentPass->FindChildPassIndex(referencePassName);
// Note: no need to check if passIndex is valid since the pass was already found
return parentPass->InsertChild(newPass, passIndex.GetIndex());
}
bool RenderPipeline::AddPassAfter(Ptr<Pass> newPass, const AZ::Name& referencePassName)
{
auto foundPass = FindFirstPass(referencePassName);
if (!foundPass)
{
AZ_Warning("RenderPipeline", false, "Add pass to render pipeline failed: can't find reference pass [%s] in render pipeline [%s]",
referencePassName.GetCStr(), GetId().GetCStr());
return false;
}
// insert the pass
auto parentPass = foundPass->GetParent();
auto passIndex = parentPass->FindChildPassIndex(referencePassName);
// Note: no need to check if passIndex is valid since the pass was already found
return parentPass->InsertChild(newPass, passIndex.GetIndex()+1);
}
Ptr<Pass> RenderPipeline::FindFirstPass(const AZ::Name& passName)
{
auto passFilter = RPI::PassFilter::CreateWithPassHierarchy({passName});
passFilter.SetOwnerRenderPipeline(this);
RPI::Ptr<RPI::Pass> foundPass = nullptr;
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [&foundPass](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
foundPass = pass;
return RPI::PassFilterExecutionFlow::StopVisitingPasses;
});
return foundPass;
}
}
}
@@ -285,6 +285,22 @@ namespace AZ
return foundFP == AZStd::end(m_featureProcessors) ? nullptr : (*foundFP).get();
}
void Scene::TryApplyRenderPipelineChanges(RenderPipeline* pipeline)
{
// return directly if the pipeline doesn't allow modification or it was already modifed by scene
if (!pipeline->m_descriptor.m_allowModification || pipeline->m_wasModifiedByScene)
{
return;
}
pipeline->m_wasModifiedByScene = true;
for (auto& fp : m_featureProcessors)
{
fp->ApplyRenderPipelineChange(pipeline);
}
AZ::RPI::PassSystemInterface::Get()->ProcessQueuedChanges();
}
void Scene::AddRenderPipeline(RenderPipelinePtr pipeline)
{
if (pipeline->m_scene != nullptr)
@@ -311,6 +327,9 @@ namespace AZ
}
pipeline->OnAddedToScene(this);
TryApplyRenderPipelineChanges(pipeline.get());
PassSystemInterface::Get()->ProcessQueuedChanges();
pipeline->BuildPipelineViews();
@@ -25,6 +25,7 @@ namespace AZ
->Field("RootPassTemplate", &RenderPipelineDescriptor::m_rootPassTemplate)
->Field("ExecuteOnce", &RenderPipelineDescriptor::m_executeOnce)
->Field("RenderSettings", &RenderPipelineDescriptor::m_renderSettings)
->Field("AllowModification", &RenderPipelineDescriptor::m_allowModification)
;
}
}