ATOM-16489 Add find passes functions for Scene or RenderPipeline in PassSystemInterface (#4739)

* ATOM-16489 Add find passes functions for Scene or RenderPipeline in PassSystemInterface
Introduced new PassSystemInterface::ForEachPass() funtion to replace PassSystemInterface::FindPasses(), PassSystemInterface::GetPassesByTemplateName and ParentPass::FindPassByNameRecursive() functions.
Update all the places which were using those three functions.
The new pass finding filter support any combination of pass name, pass template name, pass class type, pass hirechary, owner scene, owner render pipeline.
Update unit tests.

Signed-off-by: Qing Tao <qingtao@amazon.com>
monroegm-disable-blank-issue-2
Qing Tao 4 years ago committed by GitHub
parent 079e684b77
commit fe8dac7989
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -50,12 +50,12 @@ namespace AZ
return scope;
}
//! Sets the active context based on the provided PassHierarchyFilter. If the filter doesn't match exactly one pass, then do nothing.
static ImGuiActiveContextScope FromPass(const RPI::PassHierarchyFilter& passHierarchyFilter)
//! Sets the active context based on the provided pass hierarchy filter. If the filter doesn't match exactly one pass, then do nothing.
static ImGuiActiveContextScope FromPass(const AZStd::vector<AZStd::string>& passHierarchy)
{
ImGuiActiveContextScope scope;
scope.ConnectToImguiNotificationBus();
ImGuiSystemRequestBus::BroadcastResult(scope.m_isEnabled, &ImGuiSystemRequests::PushActiveContextFromPass, passHierarchyFilter);
ImGuiSystemRequestBus::BroadcastResult(scope.m_isEnabled, &ImGuiSystemRequests::PushActiveContextFromPass, passHierarchy);
return scope;
}

@ -15,11 +15,6 @@
namespace AZ
{
namespace RPI
{
class PassHierarchyFilter;
}
namespace Render
{
class ImGuiPass;
@ -51,7 +46,7 @@ namespace AZ
//! Pushes whichever ImGui pass is default on the top of the active context stack. Returns true/false for success/fail.
virtual bool PushActiveContextFromDefaultPass() = 0;
//! Pushes whichever ImGui pass is provided in passHierarchy on the top of the active context stack. Returns true/false for success/fail.
virtual bool PushActiveContextFromPass(const RPI::PassHierarchyFilter& passHierarchy) = 0;
virtual bool PushActiveContextFromPass(const AZStd::vector<AZStd::string>& passHierarchy) = 0;
//! Pops the active context off the top of the active context stack. Returns true if there's a context to pop.
virtual bool PopActiveContext() = 0;
//! Gets the context at the top of the active context stack. Returns nullptr if the stack is emtpy.

@ -50,7 +50,7 @@ namespace AZ
virtual bool CaptureScreenshotWithPreview(const AZStd::string& outputFilePath) = 0;
//! Save a buffer attachment or a image attachment binded to a pass's slot to a data file.
//! @param passHierarchy For finding the pass by using PassHierarchyFilter
//! @param passHierarchy For finding the pass by using a pass hierarchy filter. Check PassFilter::CreateWithPassHierarchy() function for detail
//! @param slotName Name of the pass's slot. The attachment bound to this slot will be captured.
//! @param option Only valid for an InputOutput attachment. Use PassAttachmentReadbackOption::Input to capture the input state
//! and use PassAttachmentReadbackOption::Output to capture the output state

@ -647,54 +647,46 @@ namespace AZ
UpdateViewsOfCascadeSegments();
}
void DirectionalLightFeatureProcessor::CacheCascadedShadowmapsPass() {
const AZStd::vector<RPI::Pass*>& passes = RPI::PassSystemInterface::Get()->GetPassesForTemplateName(Name("CascadedShadowmapsTemplate"));
void DirectionalLightFeatureProcessor::CacheCascadedShadowmapsPass()
{
m_cascadedShadowmapsPasses.clear();
for (RPI::Pass* pass : passes)
{
if (RPI::RenderPipeline* pipeline = pass->GetRenderPipeline())
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("CascadedShadowmapsTemplate"), GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
RPI::RenderPipeline* pipeline = pass->GetRenderPipeline();
const RPI::RenderPipelineId pipelineId = pipeline->GetId();
// This function can be called when the pipeline is not attached to the scene.
// So we check it is attached to the scene.
if (GetParentScene()->GetRenderPipeline(pipelineId).get() == pipeline)
CascadedShadowmapsPass* shadowPass = azrtti_cast<CascadedShadowmapsPass*>(pass);
AZ_Assert(shadowPass, "It is not a CascadedShadowmapPass.");
if (pipeline->GetDefaultView())
{
CascadedShadowmapsPass* shadowPass = azrtti_cast<CascadedShadowmapsPass*>(pass);
AZ_Assert(shadowPass, "It is not a CascadedShadowmapPass.");
if (pipeline->GetDefaultView())
{
m_cascadedShadowmapsPasses[pipelineId].push_back(shadowPass);
}
m_cascadedShadowmapsPasses[pipelineId].push_back(shadowPass);
}
}
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void DirectionalLightFeatureProcessor::CacheEsmShadowmapsPass()
{
const AZStd::vector<RPI::Pass*>& passes = RPI::PassSystemInterface::Get()->GetPassesForTemplateName(Name("EsmShadowmapsTemplate"));
m_esmShadowmapsPasses.clear();
for (RPI::Pass* pass : passes)
{
if (RPI::RenderPipeline* pipeline = pass->GetRenderPipeline())
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("EsmShadowmapsTemplate"), GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
const RPI::RenderPipelineId pipelineId = pipeline->GetId();
// checking the render pipeline is just removed from the scene.
if (GetParentScene()->GetRenderPipeline(pipelineId).get() == pipeline)
const RPI::RenderPipelineId pipelineId = pass->GetRenderPipeline()->GetId();
if (m_cascadedShadowmapsPasses.find(pipelineId) != m_cascadedShadowmapsPasses.end())
{
if (m_cascadedShadowmapsPasses.find(pipelineId) != m_cascadedShadowmapsPasses.end())
EsmShadowmapsPass* esmPass = azrtti_cast<EsmShadowmapsPass*>(pass);
AZ_Assert(esmPass, "It is not an EsmShadowmapPass.");
if (esmPass->GetLightTypeName() == m_lightTypeName)
{
EsmShadowmapsPass* esmPass = azrtti_cast<EsmShadowmapsPass*>(pass);
AZ_Assert(esmPass, "It is not an EsmShadowmapPass.");
if (m_cascadedShadowmapsPasses.find(esmPass->GetRenderPipeline()->GetId()) != m_cascadedShadowmapsPasses.end() &&
esmPass->GetLightTypeName() == m_lightTypeName)
{
m_esmShadowmapsPasses[pipelineId].push_back(esmPass);
}
m_esmShadowmapsPasses[pipelineId].push_back(esmPass);
}
}
}
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void DirectionalLightFeatureProcessor::PrepareCameraViews()
@ -1063,12 +1055,13 @@ namespace AZ
// if the shadow is rendering in an EnvironmentCubeMapPass it also needs to be a ReflectiveCubeMap view,
// to filter out shadows from objects that are excluded from the cubemap
RPI::PassClassFilter<RPI::EnvironmentCubeMapPass> passFilter;
AZStd::vector<AZ::RPI::Pass*> cubeMapPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter);
if (!cubeMapPasses.empty())
{
usageFlags |= RPI::View::UsageReflectiveCubeMap;
}
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass<RPI::EnvironmentCubeMapPass>();
passFilter.SetOwenrScene(GetParentScene()); // only handles passes for this scene
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [&usageFlags]([[maybe_unused]] RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
usageFlags |= RPI::View::UsageReflectiveCubeMap;
return RPI::PassFilterExecutionFlow::StopVisitingPasses;
});
segment.m_view = RPI::View::CreateView(viewName, usageFlags);
}

@ -80,35 +80,48 @@ namespace AZ
}
// update the size multiplier on the DiffuseProbeGridDownsamplePass output
AZStd::vector<Name> downsamplePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseProbeGridDownsamplePass") };
RPI::PassHierarchyFilter downsamplePassFilter(downsamplePassHierarchy);
const AZStd::vector<RPI::Pass*>& downsamplePasses = RPI::PassSystemInterface::Get()->FindPasses(downsamplePassFilter);
for (RPI::Pass* pass : downsamplePasses)
// NOTE: The ownerScene wasn't added to both filters. This is because the passes from the non-owner scene may have invalid SRG values which could lead to
// GPU error if the scene doesn't have this feature processor enabled.
// For example, the ASV MultiScene sample may have TDR.
{
for (uint32_t outputIndex = 0; outputIndex < pass->GetOutputCount(); ++outputIndex)
{
RPI::Ptr<RPI::PassAttachment> outputAttachment = pass->GetOutputBinding(outputIndex).m_attachment;
RPI::PassAttachmentSizeMultipliers& sizeMultipliers = outputAttachment->m_sizeMultipliers;
sizeMultipliers.m_widthMultiplier = sizeMultiplier;
sizeMultipliers.m_heightMultiplier = sizeMultiplier;
}
// set the output scale on the PassSrg
RPI::FullscreenTrianglePass* downsamplePass = static_cast<RPI::FullscreenTrianglePass*>(pass);
auto constantIndex = downsamplePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_outputImageScale"));
downsamplePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast<uint32_t>(1.0f / sizeMultiplier));
AZStd::vector<Name> downsamplePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseProbeGridDownsamplePass") };
RPI::PassFilter downsamplePassFilter = RPI::PassFilter::CreateWithPassHierarchy(downsamplePassHierarchy);
RPI::PassSystemInterface::Get()->ForEachPass(
downsamplePassFilter,
[sizeMultiplier](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
for (uint32_t outputIndex = 0; outputIndex < pass->GetOutputCount(); ++outputIndex)
{
RPI::Ptr<RPI::PassAttachment> outputAttachment = pass->GetOutputBinding(outputIndex).m_attachment;
RPI::PassAttachmentSizeMultipliers& sizeMultipliers = outputAttachment->m_sizeMultipliers;
sizeMultipliers.m_widthMultiplier = sizeMultiplier;
sizeMultipliers.m_heightMultiplier = sizeMultiplier;
}
// set the output scale on the PassSrg
RPI::FullscreenTrianglePass* downsamplePass = static_cast<RPI::FullscreenTrianglePass*>(pass);
RHI::ShaderInputNameIndex outputImageScaleShaderInput = "m_outputImageScale";
downsamplePass->GetShaderResourceGroup()->SetConstant(
outputImageScaleShaderInput, aznumeric_cast<uint32_t>(1.0f / sizeMultiplier));
// handle all downsample passes
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
// update the image scale on the DiffuseComposite pass
AZStd::vector<Name> compositePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseCompositePass") };
RPI::PassHierarchyFilter compositePassFilter(compositePassHierarchy);
const AZStd::vector<RPI::Pass*>& compositePasses = RPI::PassSystemInterface::Get()->FindPasses(compositePassFilter);
for (RPI::Pass* pass : compositePasses)
{
RPI::FullscreenTrianglePass* compositePass = static_cast<RPI::FullscreenTrianglePass*>(pass);
auto constantIndex = compositePass->GetShaderResourceGroup()->FindShaderInputConstantIndex(Name("m_imageScale"));
compositePass->GetShaderResourceGroup()->SetConstant(constantIndex, aznumeric_cast<uint32_t>(1.0f / sizeMultiplier));
AZStd::vector<Name> compositePassHierarchy = { Name("DiffuseGlobalIlluminationPass"), Name("DiffuseCompositePass") };
RPI::PassFilter compositePassFilter = RPI::PassFilter::CreateWithPassHierarchy(compositePassHierarchy);
RPI::PassSystemInterface::Get()->ForEachPass(compositePassFilter, [sizeMultiplier](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
RPI::FullscreenTrianglePass* compositePass = static_cast<RPI::FullscreenTrianglePass*>(pass);
RHI::ShaderInputNameIndex imageScaleShaderInput = "m_imageScale";
compositePass->GetShaderResourceGroup()->SetConstant(imageScaleShaderInput, aznumeric_cast<uint32_t>(1.0f / sizeMultiplier));
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
}
} // namespace Render

@ -603,12 +603,12 @@ namespace AZ
RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
if (device->GetFeatures().m_rayTracing == false)
{
RPI::PassHierarchyFilter updatePassFilter(AZ::Name("DiffuseProbeGridUpdatePass"));
const AZStd::vector<RPI::Pass*>& updatePasses = RPI::PassSystemInterface::Get()->FindPasses(updatePassFilter);
for (RPI::Pass* pass : updatePasses)
{
pass->SetEnabled(false);
}
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(AZ::Name("DiffuseProbeGridUpdatePass"), GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
pass->SetEnabled(false);
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
}

@ -10,9 +10,10 @@
#include <ACES/Aces.h>
#include <Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h>
#include <Atom/RPI.Public/Pass/FullscreenTrianglePass.h>
#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/PassFactory.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Public/Pass/Specific/SwapChainPass.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/RPIUtils.h>
@ -66,22 +67,14 @@ namespace AZ
{
// Need to invalidate the CopyToSwapChain pass so that it updates the pipeline state in the event that
// the swapchain format changed (for example, moving from LDR to HDR display)
auto* passSystem = RPI::PassSystemInterface::Get();
const Name fullscreenCopyTemplateName("FullscreenCopyTemplate");
if (passSystem->HasPassesForTemplateName(fullscreenCopyTemplateName))
{
const AZStd::vector<RPI::Pass*>& passes = passSystem->GetPassesForTemplateName(fullscreenCopyTemplateName);
for (RPI::Pass* pass : passes)
const Name copyToSwapChainPassName("CopyToSwapChain");
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(copyToSwapChainPassName, GetRenderPipeline());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
RPI::FullscreenTrianglePass* fullscreenTrianglePass = azrtti_cast<RPI::FullscreenTrianglePass*>(pass);
const Name& passName = fullscreenTrianglePass->GetName();
if (passName.GetStringView() == "CopyToSwapChain")
{
fullscreenTrianglePass->QueueForInitialization();
}
}
}
pass->QueueForInitialization();
return RPI::PassFilterExecutionFlow::StopVisitingPasses;
});
ConfigureDisplayParameters();
}

@ -372,29 +372,25 @@ namespace AZ
}
m_latestCaptureInfo.clear();
// Find the pass first
RPI::PassClassFilter<RPI::ImageAttachmentPreviewPass> passFilter;
AZStd::vector<AZ::RPI::Pass*> foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter);
if (foundPasses.size() == 0)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass<RPI::ImageAttachmentPreviewPass>();
AZ::RPI::ImageAttachmentPreviewPass* previewPass = azrtti_cast<AZ::RPI::ImageAttachmentPreviewPass*>(RPI::PassSystemInterface::Get()->FindFirstPass(passFilter));
if (!previewPass)
{
AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find an ImageAttachmentPreviewPass pass ");
AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find an ImageAttachmentPreviewPass");
return false;
}
AZ::RPI::ImageAttachmentPreviewPass* previewPass = azrtti_cast<AZ::RPI::ImageAttachmentPreviewPass*>(foundPasses[0]);
bool result = previewPass->ReadbackOutput(m_readback);
if (result)
{
m_state = State::Pending;
m_result = FrameCaptureResult::None;
SystemTickBus::Handler::BusConnect();
return true;
}
else
{
AZ_Warning("FrameCaptureSystemComponent", false, "CaptureScreenshotWithPreview. Failed to readback output from the ImageAttachmentPreviewPass");;
}
return result;
AZ_Warning("FrameCaptureSystemComponent", false, "CaptureScreenshotWithPreview. Failed to readback output from the ImageAttachmentPreviewPass");
return false;
}
bool FrameCaptureSystemComponent::CapturePassAttachment(const AZStd::vector<AZStd::string>& passHierarchy, const AZStd::string& slot,
@ -405,6 +401,12 @@ namespace AZ
return false;
}
if (passHierarchy.size() == 0)
{
AZ_Warning("FrameCaptureSystemComponent", false, "Empty data in passHierarchy");
return false;
}
InitReadback();
if (m_state != State::Idle)
@ -426,17 +428,15 @@ namespace AZ
}
m_latestCaptureInfo.clear();
// Find the pass first
AZ::RPI::PassHierarchyFilter passFilter(passHierarchy);
AZStd::vector<AZ::RPI::Pass*> foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter);
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassHierarchy(passHierarchy);
RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter);
if (foundPasses.size() == 0)
if (!pass)
{
AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find pass from %s", passFilter.ToString().c_str());
AZ_Warning("FrameCaptureSystemComponent", false, "Failed to find pass from %s", passHierarchy[0].c_str());
return false;
}
AZ::RPI::Pass* pass = foundPasses[0];
if (pass->ReadbackAttachment(m_readback, Name(slot), option))
{
m_state = State::Pending;
@ -444,6 +444,7 @@ namespace AZ
SystemTickBus::Handler::BusConnect();
return true;
}
AZ_Warning("FrameCaptureSystemComponent", false, "Failed to readback the attachment bound to pass [%s] slot [%s]", pass->GetName().GetCStr(), slot.c_str());
return false;
}

@ -109,15 +109,15 @@ namespace AZ
void ImGuiSystemComponent::ForAllImGuiPasses(PassFunction func)
{
ImGuiContext* contextToRestore = ImGui::GetCurrentContext();
RPI::PassClassFilter<ImGuiPass> filter;
auto imguiPasses = RPI::PassSystemInterface::Get()->FindPasses(filter);
for (RPI::Pass* pass : imguiPasses)
{
ImGuiPass* imguiPass = azrtti_cast<ImGuiPass*>(pass);
ImGui::SetCurrentContext(imguiPass->GetContext());
func(imguiPass);
}
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass<ImGuiPass>();
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [func](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
ImGuiPass* imguiPass = azrtti_cast<ImGuiPass*>(pass);
ImGui::SetCurrentContext(imguiPass->GetContext());
func(imguiPass);
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
ImGui::SetCurrentContext(contextToRestore);
}
@ -169,29 +169,37 @@ namespace AZ
return false;
}
bool ImGuiSystemComponent::PushActiveContextFromPass(const RPI::PassHierarchyFilter& passHierarchyFilter)
bool ImGuiSystemComponent::PushActiveContextFromPass(const AZStd::vector<AZStd::string>& passHierarchyFilter)
{
AZStd::vector<AZ::RPI::Pass*> foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passHierarchyFilter);
if (passHierarchyFilter.size() == 0)
{
AZ_Warning("ImGuiSystemComponent", false, "passHierarchyFilter is empty");
return false;
}
AZStd::vector<ImGuiPass*> foundImGuiPasses;
for (RPI::Pass* pass : foundPasses)
{
ImGuiPass* imGuiPass = azrtti_cast<ImGuiPass*>(pass);
if (imGuiPass)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassHierarchy(passHierarchyFilter);
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [&foundImGuiPasses](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
foundImGuiPasses.push_back(imGuiPass);
}
}
ImGuiPass* imGuiPass = azrtti_cast<ImGuiPass*>(pass);
if (imGuiPass)
{
foundImGuiPasses.push_back(imGuiPass);
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
if (foundImGuiPasses.size() == 0)
{
AZ_Warning("ImGuiSystemComponent", false, "Failed to find ImGui pass to activate from %s", passHierarchyFilter.ToString().c_str());
AZ_Warning("ImGuiSystemComponent", false, "Failed to find ImGui pass to activate from %s", passHierarchyFilter[0].c_str());
return false;
}
if (foundImGuiPasses.size() > 1)
{
AZ_Warning("ImGuiSystemComponent", false, "Found more than one ImGui pass to activate from %s, only activating first one.", passHierarchyFilter.ToString().c_str());
AZ_Warning("ImGuiSystemComponent", false, "Found more than one ImGui pass to activate from %s, only activating first one.", passHierarchyFilter[0].c_str());
}
ImGuiContext* context = foundImGuiPasses.at(0)->GetContext();

@ -56,7 +56,7 @@ namespace AZ
ImGuiPass* GetDefaultImGuiPass() override;
bool PushActiveContextFromDefaultPass() override;
bool PushActiveContextFromPass(const RPI::PassHierarchyFilter& passHierarchy) override;
bool PushActiveContextFromPass(const AZStd::vector<AZStd::string>& passHierarchy) override;
bool PopActiveContext() override;
ImGuiContext* GetActiveContext() override;

@ -15,6 +15,7 @@
#include <AzFramework/Asset/AssetSystemBus.h>
#include <Atom/RPI.Public/Image/ImageSystemInterface.h>
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/RPIUtils.h>
@ -260,23 +261,18 @@ namespace AZ
// [GFX TODO][ATOM-3035]This function is temporary and will change with improvement to the draw list tag system
void DepthOfFieldSettings::UpdateAutoFocusDepth(bool enabled)
{
auto* passSystem = AZ::RPI::PassSystemInterface::Get();
const Name TemplateNameReadBackFocusDepth = Name("DepthOfFieldReadBackFocusDepthTemplate");
if (passSystem->HasPassesForTemplateName(TemplateNameReadBackFocusDepth))
{
const AZStd::vector<RPI::Pass*>& dofPasses = passSystem->GetPassesForTemplateName(TemplateNameReadBackFocusDepth);
for (RPI::Pass* pass : dofPasses)
// [GFX TODO][ATOM-4908] multiple camera should be distingushed.
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(TemplateNameReadBackFocusDepth, GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this, enabled](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
auto* dofPass = azrtti_cast<AZ::Render::DepthOfFieldReadBackFocusDepthPass*>(pass);
// Check this pass belongs to a render pipeline of the scene.
// [GFX TODO][ATOM-4908] multiple camera should be distingushed.
const RPI::RenderPipelineId pipelineId = dofPass->GetRenderPipeline()->GetId();
if (enabled && GetParentScene()->GetRenderPipeline(pipelineId))
if (enabled)
{
m_normalizedFocusDistanceForAutoFocus = dofPass->GetNormalizedFocusDistanceForAutoFocus();
}
}
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void DepthOfFieldSettings::SetCameraEntityId(EntityId cameraEntityId)

@ -10,6 +10,7 @@
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
@ -188,21 +189,21 @@ namespace AZ
void ExposureControlSettings::UpdateLuminanceHeatmap()
{
auto* passSystem = AZ::RPI::PassSystemInterface::Get();
// [GFX-TODO][ATOM-13194] Support multiple views for the luminance heatmap
// [GFX-TODO][ATOM-13224] Remove UpdateLuminanceHeatmap and UpdateEyeAdaptationPass
const RPI::Ptr<RPI::Pass> luminanceHeatmap = passSystem->GetRootPass()->FindPassByNameRecursive(m_luminanceHeatmapNameId);
if (luminanceHeatmap)
{
luminanceHeatmap->SetEnabled(m_heatmapEnabled);
}
const RPI::Ptr<RPI::Pass> histogramGenerator = passSystem->GetRootPass()->FindPassByNameRecursive(m_luminanceHistogramGeneratorNameId);
if (histogramGenerator)
{
histogramGenerator->SetEnabled(m_heatmapEnabled);
}
RPI::PassFilter heatmapPassFilter = RPI::PassFilter::CreateWithPassName(m_luminanceHeatmapNameId, GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(heatmapPassFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
pass->SetEnabled(m_heatmapEnabled);
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
RPI::PassFilter histogramPassFilter = RPI::PassFilter::CreateWithPassName(m_luminanceHistogramGeneratorNameId, GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(histogramPassFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
pass->SetEnabled(m_heatmapEnabled);
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void ExposureControlSettings::UpdateBuffer()

@ -31,12 +31,14 @@ namespace AZ
0,
[](const uint8_t& value)
{
auto passes = RPI::PassSystem::Get()->FindPasses(RPI::PassClassFilter<LookModificationCompositePass>());
for (auto* pass : passes)
{
LookModificationCompositePass* lookModPass = azrtti_cast<LookModificationCompositePass*>(pass);
lookModPass->SetSampleQuality(LookModificationCompositePass::SampleQuality(value));
}
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassClass<LookModificationCompositePass>();
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [value](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
LookModificationCompositePass* lookModPass = azrtti_cast<LookModificationCompositePass*>(pass);
lookModPass->SetSampleQuality(LookModificationCompositePass::SampleQuality(value));
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
},
ConsoleFunctorFlags::Null,
"This can be increased to deal with particularly tricky luts. Range (0-2). 0 (default) - Standard linear sampling. 1 - 7 tap b-spline sampling. 2 - 19 tap b-spline sampling."

@ -16,6 +16,7 @@
#include <Atom/RHI/Factory.h>
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/RPISystemInterface.h>
#include <Atom/RPI.Public/Scene.h>
@ -71,26 +72,18 @@ namespace AZ
void SMAAFeatureProcessor::UpdateConvertToPerceptualPass()
{
auto* passSystem = AZ::RPI::PassSystemInterface::Get();
if (passSystem->HasPassesForTemplateName(m_convertToPerceptualColorPassTemplateNameId))
{
const AZStd::vector<RPI::Pass*>& convertToPerceptualColorPasses = passSystem->GetPassesForTemplateName(m_convertToPerceptualColorPassTemplateNameId);
for (RPI::Pass* pass : convertToPerceptualColorPasses)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_convertToPerceptualColorPassTemplateNameId, GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
pass->SetEnabled(m_data.m_enable);
}
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void SMAAFeatureProcessor::UpdateEdgeDetectionPass()
{
auto* passSystem = AZ::RPI::PassSystemInterface::Get();
if (passSystem->HasPassesForTemplateName(m_edgeDetectioPassTemplateNameId))
{
const AZStd::vector<RPI::Pass*>& edgeDetectionPasses = passSystem->GetPassesForTemplateName(m_edgeDetectioPassTemplateNameId);
for (RPI::Pass* pass : edgeDetectionPasses)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_edgeDetectioPassTemplateNameId, GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
auto* edgeDetectionPass = azrtti_cast<AZ::Render::SMAAEdgeDetectionPass*>(pass);
@ -106,18 +99,14 @@ namespace AZ
edgeDetectionPass->SetPredicationScale(m_data.m_predicationScale);
edgeDetectionPass->SetPredicationStrength(m_data.m_predicationStrength);
}
}
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void SMAAFeatureProcessor::UpdateBlendingWeightCalculationPass()
{
auto* passSystem = AZ::RPI::PassSystemInterface::Get();
if (passSystem->HasPassesForTemplateName(m_blendingWeightCalculationPassTemplateNameId))
{
const AZStd::vector<RPI::Pass*>& blendingWeightCalculationPasses = passSystem->GetPassesForTemplateName(m_blendingWeightCalculationPassTemplateNameId);
for (RPI::Pass* pass : blendingWeightCalculationPasses)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_blendingWeightCalculationPassTemplateNameId, GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
auto* blendingWeightCalculationPass = azrtti_cast<AZ::Render::SMAABlendingWeightCalculationPass*>(pass);
@ -130,18 +119,14 @@ namespace AZ
blendingWeightCalculationPass->SetDiagonalDetectionEnable(m_data.m_enableDiagonalDetection);
blendingWeightCalculationPass->SetCornerDetectionEnable(m_data.m_enableCornerDetection);
}
}
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void SMAAFeatureProcessor::UpdateNeighborhoodBlendingPass()
{
auto* passSystem = AZ::RPI::PassSystemInterface::Get();
if (passSystem->HasPassesForTemplateName(m_neighborhoodBlendingPassTemplateNameId))
{
const AZStd::vector<RPI::Pass*>& neighborhoodBlendingPasses = passSystem->GetPassesForTemplateName(m_neighborhoodBlendingPassTemplateNameId);
for (RPI::Pass* pass : neighborhoodBlendingPasses)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(m_neighborhoodBlendingPassTemplateNameId, GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
auto* neighborhoodBlendingPass = azrtti_cast<AZ::Render::SMAANeighborhoodBlendingPass*>(pass);
@ -153,8 +138,8 @@ namespace AZ
{
neighborhoodBlendingPass->SetOutputMode(SMAAOutputMode::PassThrough);
}
}
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void SMAAFeatureProcessor::Render([[maybe_unused]] const SMAAFeatureProcessor::RenderPacket& packet)

@ -377,14 +377,7 @@ namespace AZ
bool ProfilingCaptureSystemComponent::CapturePassTimestamp(const AZStd::string& outputFilePath)
{
// Find the root pass.
AZStd::vector<RPI::Pass*> passes = FindPasses({ "Root" });
if (passes.empty())
{
return false;
}
RPI::Pass* root = passes[0];
RPI::Pass* root = AZ::RPI::PassSystemInterface::Get()->GetRootPass().get();
// Enable all the Timestamp queries in passes.
root->SetTimestampQueryEnabled(true);
@ -465,14 +458,7 @@ namespace AZ
bool ProfilingCaptureSystemComponent::CapturePassPipelineStatistics(const AZStd::string& outputFilePath)
{
// Find the root pass.
AZStd::vector<RPI::Pass*> passes = FindPasses({ "Root" });
if (passes.empty())
{
return false;
}
RPI::Pass* root = passes[0];
RPI::Pass* root = AZ::RPI::PassSystemInterface::Get()->GetRootPass().get();
// Enable all the PipelineStatistics queries in passes.
root->SetPipelineStatisticsQueryEnabled(true);
@ -572,19 +558,6 @@ namespace AZ
return passes;
}
AZStd::vector<RPI::Pass*> ProfilingCaptureSystemComponent::FindPasses(AZStd::vector<AZStd::string>&& passHierarchy) const
{
// Find the pass first.
RPI::PassHierarchyFilter passFilter(passHierarchy);
AZStd::vector<AZ::RPI::Pass*> foundPasses = AZ::RPI::PassSystemInterface::Get()->FindPasses(passFilter);
if (foundPasses.size() == 0)
{
AZ_Warning("ProfilingCaptureSystemComponent", false, "Failed to find pass from %s", passFilter.ToString().c_str());
}
return foundPasses;
}
void ProfilingCaptureSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time)
{
// Update the delayed captures

@ -78,8 +78,6 @@ namespace AZ
// Recursively collect all the passes from the root pass.
AZStd::vector<const RPI::Pass*> CollectPassesRecursively(const RPI::Pass* root) const;
AZStd::vector<AZ::RPI::Pass*> FindPasses(AZStd::vector<AZStd::string>&& passHierarchy) const;
DelayedQueryCaptureHelper m_timestampCapture;
DelayedQueryCaptureHelper m_cpuFrameTimeStatisticsCapture;
DelayedQueryCaptureHelper m_pipelineStatisticsCapture;

@ -28,16 +28,17 @@ namespace AZ
void ReflectionCopyFrameBufferPass::BuildInternal()
{
RPI::PassHierarchyFilter passFilter(AZ::Name("ReflectionScreenSpaceBlurPass"));
const AZStd::vector<RPI::Pass*>& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter);
if (!passes.empty())
{
Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast<ReflectionScreenSpaceBlurPass*>(passes.front());
Data::Instance<RPI::AttachmentImage>& frameBufferAttachment = blurPass->GetFrameBufferImageAttachment();
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(AZ::Name("ReflectionScreenSpaceBlurPass"), GetRenderPipeline());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast<ReflectionScreenSpaceBlurPass*>(pass);
Data::Instance<RPI::AttachmentImage>& frameBufferAttachment = blurPass->GetFrameBufferImageAttachment();
RPI::PassAttachmentBinding& outputBinding = GetOutputBinding(0);
AttachImageToSlot(outputBinding.m_name, frameBufferAttachment);
}
RPI::PassAttachmentBinding& outputBinding = GetOutputBinding(0);
AttachImageToSlot(outputBinding.m_name, frameBufferAttachment);
return RPI::PassFilterExecutionFlow::StopVisitingPasses;
});
FullscreenTrianglePass::BuildInternal();
}

@ -150,7 +150,7 @@ namespace AZ
auto transientImageDesc = RHI::ImageDescriptor::Create2D(imageBindFlags, mipSize.m_width, mipSize.m_height, RHI::Format::R16G16B16A16_FLOAT);
RPI::PassAttachment* transientPassAttachment = aznew RPI::PassAttachment();
AZStd::string transientAttachmentName = AZStd::string::format("ReflectionScreenSpace_BlurImage%d", mip);
AZStd::string transientAttachmentName = AZStd::string::format("%s.ReflectionScreenSpace_BlurImage%d", GetPathName().GetCStr(), mip);
transientPassAttachment->m_name = transientAttachmentName;
transientPassAttachment->m_path = transientAttachmentName;
transientPassAttachment->m_lifetime = RHI::AttachmentLifetimeType::Transient;

@ -33,20 +33,22 @@ namespace AZ
return;
}
RPI::PassHierarchyFilter passFilter(AZ::Name("ReflectionScreenSpaceBlurPass"));
const AZStd::vector<RPI::Pass*>& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter);
if (!passes.empty())
{
Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast<ReflectionScreenSpaceBlurPass*>(passes.front());
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(AZ::Name("ReflectionScreenSpaceBlurPass"), GetRenderPipeline());
// compute the max mip level based on the available mips in the previous frame image, and capping it
// to stay within a range that has reasonable data
const uint32_t MaxNumRoughnessMips = 8;
uint32_t maxMipLevel = AZStd::min(MaxNumRoughnessMips, blurPass->GetNumBlurMips()) - 1;
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
Render::ReflectionScreenSpaceBlurPass* blurPass = azrtti_cast<ReflectionScreenSpaceBlurPass*>(pass);
auto constantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_maxMipLevel"));
m_shaderResourceGroup->SetConstant(constantIndex, maxMipLevel);
}
// compute the max mip level based on the available mips in the previous frame image, and capping it
// to stay within a range that has reasonable data
const uint32_t MaxNumRoughnessMips = 8;
uint32_t maxMipLevel = AZStd::min(MaxNumRoughnessMips, blurPass->GetNumBlurMips()) - 1;
auto constantIndex = m_shaderResourceGroup->FindShaderInputConstantIndex(Name("m_maxMipLevel"));
m_shaderResourceGroup->SetConstant(constantIndex, maxMipLevel);
return RPI::PassFilterExecutionFlow::StopVisitingPasses;
});
FullscreenTrianglePass::CompileResources(context);
}

@ -313,52 +313,38 @@ namespace AZ::Render
void ProjectedShadowFeatureProcessor::CachePasses()
{
const AZStd::vector<RPI::RenderPipelineId> validPipelineIds = CacheProjectedShadowmapsPass();
CacheEsmShadowmapsPass(validPipelineIds);
CacheProjectedShadowmapsPass();
CacheEsmShadowmapsPass();
m_shadowmapPassNeedsUpdate = true;
}
AZStd::vector<RPI::RenderPipelineId> ProjectedShadowFeatureProcessor::CacheProjectedShadowmapsPass()
void ProjectedShadowFeatureProcessor::CacheProjectedShadowmapsPass()
{
const AZStd::vector<RPI::RenderPipelinePtr>& renderPipelines = GetParentScene()->GetRenderPipelines();
const auto* passSystem = RPI::PassSystemInterface::Get();;
const AZStd::vector<RPI::Pass*>& passes = passSystem->GetPassesForTemplateName(Name("ProjectedShadowmapsTemplate"));
AZStd::vector<RPI::RenderPipelineId> validPipelineIds;
m_projectedShadowmapsPasses.clear();
for (RPI::Pass* pass : passes)
{
ProjectedShadowmapsPass* shadowPass = static_cast<ProjectedShadowmapsPass*>(pass);
for (const RPI::RenderPipelinePtr& pipeline : renderPipelines)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("ProjectedShadowmapsTemplate"), GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
if (pipeline.get() == shadowPass->GetRenderPipeline())
{
m_projectedShadowmapsPasses.emplace_back(shadowPass);
validPipelineIds.push_back(shadowPass->GetRenderPipeline()->GetId());
}
}
}
return validPipelineIds;
ProjectedShadowmapsPass* shadowPass = static_cast<ProjectedShadowmapsPass*>(pass);
m_projectedShadowmapsPasses.emplace_back(shadowPass);
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void ProjectedShadowFeatureProcessor::CacheEsmShadowmapsPass(const AZStd::vector<RPI::RenderPipelineId>& validPipelineIds)
void ProjectedShadowFeatureProcessor::CacheEsmShadowmapsPass()
{
const Name LightTypeName = Name("projected");
const auto* passSystem = RPI::PassSystemInterface::Get();
const AZStd::vector<RPI::Pass*> passes = passSystem->GetPassesForTemplateName(Name("EsmShadowmapsTemplate"));
m_esmShadowmapsPasses.clear();
for (RPI::Pass* pass : passes)
{
EsmShadowmapsPass* esmPass = static_cast<EsmShadowmapsPass*>(pass);
if (esmPass->GetRenderPipeline() &&
AZStd::find(validPipelineIds.begin(), validPipelineIds.end(), esmPass->GetRenderPipeline()->GetId()) != validPipelineIds.end() &&
esmPass->GetLightTypeName() == LightTypeName)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithTemplateName(Name("EsmShadowmapsTemplate"), GetParentScene());
RPI::PassSystemInterface::Get()->ForEachPass(passFilter, [this, LightTypeName](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
{
m_esmShadowmapsPasses.emplace_back(esmPass);
}
}
EsmShadowmapsPass* esmPass = static_cast<EsmShadowmapsPass*>(pass);
if (esmPass->GetLightTypeName() == LightTypeName)
{
m_esmShadowmapsPasses.emplace_back(esmPass);
}
return RPI::PassFilterExecutionFlow::ContinueVisitingPasses;
});
}
void ProjectedShadowFeatureProcessor::UpdateFilterParameters()

@ -97,8 +97,8 @@ namespace AZ::Render
// Functions for caching the ProjectedShadowmapsPass and EsmShadowmapsPass.
void CachePasses();
AZStd::vector<RPI::RenderPipelineId> CacheProjectedShadowmapsPass();
void CacheEsmShadowmapsPass(const AZStd::vector<RPI::RenderPipelineId>& validPipelineIds);
void CacheProjectedShadowmapsPass();
void CacheEsmShadowmapsPass();
//! Functions to update the parameter of Gaussian filter used in ESM.
void UpdateFilterParameters();

@ -17,6 +17,7 @@
#include <MorphTargets/MorphTargetDispatchItem.h>
#include <Atom/RPI.Public/Model/ModelLodUtils.h>
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/Shader/Shader.h>
@ -241,12 +242,12 @@ namespace AZ
void SkinnedMeshFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr pipeline)
{
InitSkinningAndMorphPass(pipeline->GetRootPass());
InitSkinningAndMorphPass(pipeline.get());
}
void SkinnedMeshFeatureProcessor::OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline)
{
InitSkinningAndMorphPass(renderPipeline->GetRootPass());
InitSkinningAndMorphPass(renderPipeline);
}
void SkinnedMeshFeatureProcessor::OnBeginPrepareRender()
@ -289,9 +290,10 @@ namespace AZ
return false;
}
void SkinnedMeshFeatureProcessor::InitSkinningAndMorphPass(const RPI::Ptr<RPI::ParentPass> pipelineRootPass)
void SkinnedMeshFeatureProcessor::InitSkinningAndMorphPass(RPI::RenderPipeline* renderPipeline)
{
RPI::Ptr<RPI::Pass> skinningPass = pipelineRootPass->FindPassByNameRecursive(AZ::Name{ "SkinningPass" });
RPI::PassFilter skinPassFilter = RPI::PassFilter::CreateWithPassName(AZ::Name{ "SkinningPass" }, renderPipeline);
RPI::Ptr<RPI::Pass> skinningPass = RPI::PassSystemInterface::Get()->FindFirstPass(skinPassFilter);
if (skinningPass)
{
SkinnedMeshComputePass* skinnedMeshComputePass = azdynamic_cast<SkinnedMeshComputePass*>(skinningPass.get());
@ -310,7 +312,8 @@ namespace AZ
}
}
RPI::Ptr<RPI::Pass> morphTargetPass = pipelineRootPass->FindPassByNameRecursive(AZ::Name{ "MorphTargetPass" });
RPI::PassFilter morphPassFilter = RPI::PassFilter::CreateWithPassName(AZ::Name{ "MorphTargetPass" }, renderPipeline);
RPI::Ptr<RPI::Pass> morphTargetPass = RPI::PassSystemInterface::Get()->FindFirstPass(morphPassFilter);
if (morphTargetPass)
{
MorphTargetComputePass* morphTargetComputePass = azdynamic_cast<MorphTargetComputePass*>(morphTargetPass.get());

@ -66,7 +66,7 @@ namespace AZ
private:
AZ_DISABLE_COPY_MOVE(SkinnedMeshFeatureProcessor);
void InitSkinningAndMorphPass(const RPI::Ptr<RPI::ParentPass> pipelineRootPass);
void InitSkinningAndMorphPass(RPI::RenderPipeline* renderPipeline);
SkinnedMeshRenderProxyInterfaceHandle AcquireRenderProxyInterface(const SkinnedMeshRenderProxyDesc& desc) override;
bool ReleaseRenderProxyInterface(SkinnedMeshRenderProxyInterfaceHandle& handle) override;

@ -68,9 +68,6 @@ namespace AZ
template<typename PassType>
Ptr<PassType> FindChildPass() const;
//! Searches the tree for the first pass that has same pass name (Depth-first search). Return nullptr if none found.
Ptr<Pass> FindPassByNameRecursive(const Name& passName) const;
//! Gets the list of children. Useful for validating hierarchies
AZStd::array_view<Ptr<Pass>> GetChildren() const;

@ -139,6 +139,10 @@ namespace AZ
//! Returns the number of output attachment bindings
uint32_t GetOutputCount() const;
//! Returns the pass template which was used for create this pass.
//! It may return nullptr if the pass wasn't create from a template
const PassTemplate* GetPassTemplate() const;
//! Enable/disable this pass
//! If the pass is disabled, it (and any children if it's a ParentPass) won't be rendered.
void SetEnabled(bool enabled);

@ -16,95 +16,85 @@ namespace AZ
{
namespace RPI
{
// A base class for a filter which can be used to filter passes
class PassFilter
{
public:
//! Whether the input pass matches with the filter
virtual bool Matches(const Pass* pass) const = 0;
//! Return the pass' name if a pass name is used for the filter.
//! Return nullptr if the filter doesn't have pass name used for matching
virtual const Name* GetPassName() const = 0;
//! Return this filter's info as a string
virtual AZStd::string ToString() const = 0;
};
class Scene;
class RenderPipeline;
//! Filter for passes which have a matching name and also with ordered parents.
//! For example, if the filter is initialized with
//! pass name: "ShadowPass1"
//! pass parents names: "MainPipeline", "Shadow"
//! Passes with these names match the filter:
//! "Root.MainPipeline.SwapChainPass.Shadow.ShadowPass1"
//! or "Root.MainPipeline.Shadow.ShadowPass1"
//! or "MainPipeline.Shadow.Group1.ShadowPass1"
//!
//! Passes with these names wont match:
//! "MainPipeline.ShadowPass1"
//! or "Shadow.MainPipeline.ShadowPass1"
class PassHierarchyFilter
: public PassFilter
class PassFilter
{
public:
AZ_RTTI(PassHierarchyFilter, "{478F169F-BA97-4321-AC34-EDE823997159}", PassFilter);
AZ_CLASS_ALLOCATOR(PassHierarchyFilter, SystemAllocator, 0);
//! Construct filter with only pass name.
PassHierarchyFilter(const Name& passName);
virtual ~PassHierarchyFilter() = default;
//! Construct filter with pass name and its parents' names in the order of the hierarchy
//! This means k-th element is always an ancestor of the (k-1)-th element.
//! And the last element is the pass name.
PassHierarchyFilter(const AZStd::vector<Name>& passHierarchy);
PassHierarchyFilter(const AZStd::vector<AZStd::string>& passHierarchy);
// PassFilter overrides...
bool Matches(const Pass* pass) const override;
const Name* GetPassName() const override;
AZStd::string ToString() const override;
static PassFilter CreateWithPassName(Name passName, const Scene* scene);
static PassFilter CreateWithPassName(Name passName, const RenderPipeline* renderPipeline);
//! Create a PassFilter with pass hierarchy information
//! Filter for passes which have a matching name and also with ordered parents.
//! For example, if the filter is initialized with
//! pass name: "ShadowPass1"
//! pass parents names: "MainPipeline", "Shadow"
//! Passes with these names match the filter:
//! "Root.MainPipeline.SwapChainPass.Shadow.ShadowPass1"
//! or "Root.MainPipeline.Shadow.ShadowPass1"
//! or "MainPipeline.Shadow.Group1.ShadowPass1"
//!
//! Passes with these names wont match:
//! "MainPipeline.ShadowPass1"
//! or "Shadow.MainPipeline.ShadowPass1"
static PassFilter CreateWithPassHierarchy(const AZStd::vector<Name>& passHierarchy);
static PassFilter CreateWithPassHierarchy(const AZStd::vector<AZStd::string>& passHierarchy);
static PassFilter CreateWithTemplateName(Name templateName, const Scene* scene);
static PassFilter CreateWithTemplateName(Name templateName, const RenderPipeline* renderPipeline);
template <typename PassClass>
static PassFilter CreateWithPassClass();
enum FilterOptions : uint32_t
{
Empty = 0,
PassName = AZ_BIT(0),
PassTemplateName = AZ_BIT(1),
PassClass = AZ_BIT(2),
PassHierarchy = AZ_BIT(3),
OwnerScene = AZ_BIT(4),
OwnerRenderPipeline = AZ_BIT(5)
};
void SetOwenrScene(const Scene* scene);
void SetOwenrRenderPipeline(const RenderPipeline* renderPipeline);
void SetPassName(Name passName);
void SetTemplateName(Name passTemplateName);
void SetPassClass(TypeId passClassTypeId);
const Name& GetPassName() const;
const Name& GetPassTemplateName() const;
uint32_t GetEnabledFilterOptions() const;
//! Return true if the input pass matches the filter
bool Matches(const Pass* pass) const;
//! Return true if the input pass matches the filter with selected filter options
//! The input filter options should be a subset of options returned by GetEnabledFilterOptions()
//! This function is used to avoid extra checks for passes which was already filtered.
//! Check PassLibrary::ForEachPass() function's implementation for more details
bool Matches(const Pass* pass, uint32_t options) const;
private:
PassHierarchyFilter() = delete;
void UpdateFilterOptions();
AZStd::vector<Name> m_parentNames;
Name m_passName;
Name m_templateName;
TypeId m_passClassTypeId = TypeId::CreateNull();
AZStd::vector<Name> m_parentNames;
const RenderPipeline* m_ownerRenderPipeline = nullptr;
const Scene* m_ownerScene = nullptr;
uint32_t m_filterOptions = 0;
};
//! Filter for passes based on their class.
template<typename PassClass>
class PassClassFilter
: public PassFilter
{
public:
AZ_RTTI(PassClassFilter, "{AF6E3AD5-433A-462A-997A-F36D8A551D02}", PassFilter);
AZ_CLASS_ALLOCATOR(PassHierarchyFilter, SystemAllocator, 0);
PassClassFilter() = default;
// PassFilter overrides...
bool Matches(const Pass* pass) const override;
const Name* GetPassName() const override;
AZStd::string ToString() const override;
};
template<typename PassClass>
bool PassClassFilter<PassClass>::Matches(const Pass* pass) const
{
return pass->RTTI_IsTypeOf(PassClass::RTTI_Type());
}
template<typename PassClass>
const Name* PassClassFilter<PassClass>::GetPassName() const
{
return nullptr;
}
template<typename PassClass>
AZStd::string PassClassFilter<PassClass>::ToString() const
template <typename PassClass>
PassFilter PassFilter::CreateWithPassClass()
{
return AZStd::string::format("PassClassFilter<%s>", PassClass::RTTI_TypeName());
PassFilter filter;
filter.m_passClassTypeId = PassClass::RTTI_Type();
filter.UpdateFilterOptions();
return filter;
}
} // namespace RPI
} // namespace AZ

@ -84,8 +84,8 @@ namespace AZ
bool LoadPassTemplateMappings(const AZStd::string& templateMappingPath);
bool LoadPassTemplateMappings(Data::Asset<AnyAsset> mappingAsset);
//! Returns a list of passes found in the pass name mapping using the provided pass filter
AZStd::vector<Pass*> FindPasses(const PassFilter& passFilter) const;
//! Visit each pass which matches the filter
void ForEachPass(const PassFilter& passFilter, AZStd::function<PassFilterExecutionFlow(Pass*)> passFunction);
private:

@ -92,13 +92,13 @@ namespace AZ
// PassSystemInterface library related functions...
bool HasPassesForTemplateName(const Name& templateName) const override;
const AZStd::vector<Pass*>& GetPassesForTemplateName(const Name& templateName) const override;
bool AddPassTemplate(const Name& name, const AZStd::shared_ptr<PassTemplate>& passTemplate) override;
const AZStd::shared_ptr<PassTemplate> GetPassTemplate(const Name& name) const override;
void RemovePassFromLibrary(Pass* pass) override;
void RegisterPass(Pass* pass) override;
void UnregisterPass(Pass* pass) override;
AZStd::vector<Pass*> FindPasses(const PassFilter& passFilter) const override;
void ForEachPass(const PassFilter& filter, AZStd::function<PassFilterExecutionFlow(Pass*)> passFunction) override;
Pass* FindFirstPass(const PassFilter& filter) override;
private:
// Returns the root of the pass tree hierarchy

@ -75,6 +75,13 @@ namespace AZ
u32 m_maxDrawItemsRenderedInAPass = 0;
};
enum PassFilterExecutionFlow : uint8_t
{
StopVisitingPasses,
ContinueVisitingPasses,
};
class PassSystemInterface
{
friend class Pass;
@ -186,9 +193,6 @@ namespace AZ
//! Returns true if the pass factory contains passes created with the given template name
virtual bool HasPassesForTemplateName(const Name& templateName) const = 0;
//! Get the passes created with the given template name.
virtual const AZStd::vector<Pass*>& GetPassesForTemplateName(const Name& templateName) const = 0;
//! Adds a PassTemplate to the library
virtual bool AddPassTemplate(const Name& name, const AZStd::shared_ptr<PassTemplate>& passTemplate) = 0;
@ -198,8 +202,15 @@ namespace AZ
//! Removes all references to the given pass from the pass library
virtual void RemovePassFromLibrary(Pass* pass) = 0;
//! Find matching passes from registered passes with specified filter
virtual AZStd::vector<Pass*> FindPasses(const PassFilter& passFilter) const = 0;
//! Visit the matching passes from registered passes with specified filter
//! The return value of the passFunction decides if the search continues or not
//! Note: this function will find all the passes which match the pass filter even they are for render pipelines which are not added to a scene
//! This function is fast if a pass name or a pass template name is specified.
virtual void ForEachPass(const PassFilter& filter, AZStd::function<PassFilterExecutionFlow(Pass*)> passFunction) = 0;
//! Find the first matching pass from registered passes with specified filter
//! Note: this function SHOULD ONLY be used when you are certain you only need to handle the first pass found
virtual Pass* FindFirstPass(const PassFilter& filter) = 0;
private:
// These functions are only meant to be used by the Pass class

@ -149,29 +149,6 @@ namespace AZ
return index.IsValid() ? m_children[index.GetIndex()] : Ptr<Pass>(nullptr);
}
Ptr<Pass> ParentPass::FindPassByNameRecursive(const Name& passName) const
{
for (const Ptr<Pass>& child : m_children)
{
if (child->GetName() == passName)
{
return child.get();
}
ParentPass* asParent = child->AsParent();
if (asParent)
{
auto pass = asParent->FindPassByNameRecursive(passName);
if (pass)
{
return pass;
}
}
}
return nullptr;
}
const Pass* ParentPass::FindPass(RHI::DrawListTag drawListTag) const
{
if (HasDrawListTag() && GetDrawListTag() == drawListTag)

@ -238,6 +238,11 @@ namespace AZ
return m_attachmentBindings[bindingIndex];
}
const PassTemplate* Pass::GetPassTemplate() const
{
return m_template.get();
}
void Pass::AddAttachmentBinding(PassAttachmentBinding attachmentBinding)
{
// Add the index of the binding to the input, output or input/output list based on the slot type

@ -8,101 +8,264 @@
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/ParentPass.h>
#include <Atom/RPI.Public/RenderPipeline.h>
namespace AZ
{
namespace RPI
{
PassHierarchyFilter::PassHierarchyFilter(const Name& passName)
PassFilter PassFilter::CreateWithPassName(Name passName, const Scene* scene)
{
m_passName = passName;
PassFilter filter;
filter.m_passName = passName;
filter.m_ownerScene = scene;
filter.UpdateFilterOptions();
return filter;
}
PassFilter PassFilter::CreateWithPassName(Name passName, const RenderPipeline* renderPipeline)
{
PassFilter filter;
filter.m_passName = passName;
filter.m_ownerRenderPipeline = renderPipeline;
filter.UpdateFilterOptions();
return filter;
}
PassHierarchyFilter::PassHierarchyFilter(const AZStd::vector<AZStd::string>& passHierarchy)
PassFilter PassFilter::CreateWithTemplateName(Name templateName, const Scene* scene)
{
PassFilter filter;
filter.m_templateName = templateName;
filter.m_ownerScene = scene;
filter.UpdateFilterOptions();
return filter;
}
PassFilter PassFilter::CreateWithTemplateName(Name templateName, const RenderPipeline* renderPipeline)
{
PassFilter filter;
filter.m_templateName = templateName;
filter.m_ownerRenderPipeline = renderPipeline;
filter.UpdateFilterOptions();
return filter;
}
PassFilter PassFilter::CreateWithPassHierarchy(const AZStd::vector<Name>& passHierarchy)
{
PassFilter filter;
if (passHierarchy.size() == 0)
{
AZ_Assert(false, "passHierarchy should have at least one element");
return;
return filter;
}
m_passName = Name(passHierarchy.back());
filter.m_passName = passHierarchy.back();
m_parentNames.resize(passHierarchy.size() - 1);
for (uint32_t index = 0; index < m_parentNames.size(); index++)
filter.m_parentNames.resize(passHierarchy.size() - 1);
for (uint32_t index = 0; index < filter.m_parentNames.size(); index++)
{
m_parentNames[index] = Name(passHierarchy[index]);
filter.m_parentNames[index] = passHierarchy[index];
}
filter.UpdateFilterOptions();
return filter;
}
PassHierarchyFilter::PassHierarchyFilter(const AZStd::vector<Name>& passHierarchy)
PassFilter PassFilter::CreateWithPassHierarchy(const AZStd::vector<AZStd::string>& passHierarchy)
{
PassFilter filter;
if (passHierarchy.size() == 0)
{
AZ_Assert(false, "passHierarchy should have at least one element");
return;
return filter;
}
m_passName = passHierarchy.back();
filter.m_passName = Name(passHierarchy.back());
m_parentNames.resize(passHierarchy.size() - 1);
for (uint32_t index = 0; index < m_parentNames.size(); index++)
filter.m_parentNames.resize(passHierarchy.size() - 1);
for (uint32_t index = 0; index < filter.m_parentNames.size(); index++)
{
m_parentNames[index] = passHierarchy[index];
filter.m_parentNames[index] = Name(passHierarchy[index]);
}
filter.UpdateFilterOptions();
return filter;
}
void PassFilter::SetOwenrScene(const Scene* scene)
{
m_ownerScene = scene;
UpdateFilterOptions();
}
void PassFilter::SetOwenrRenderPipeline(const RenderPipeline* renderPipeline)
{
m_ownerRenderPipeline = renderPipeline;
UpdateFilterOptions();
}
bool PassHierarchyFilter::Matches(const Pass* pass) const
void PassFilter::SetPassName(Name passName)
{
if (pass->GetName() != m_passName)
m_passName = passName;
UpdateFilterOptions();
}
void PassFilter::SetTemplateName(Name passTemplateName)
{
m_templateName = passTemplateName;
UpdateFilterOptions();
}
void PassFilter::SetPassClass(TypeId passClassTypeId)
{
m_passClassTypeId = passClassTypeId;
UpdateFilterOptions();
}
const Name& PassFilter::GetPassName() const
{
return m_passName;
}
const Name& PassFilter::GetPassTemplateName() const
{
return m_templateName;
}
uint32_t PassFilter::GetEnabledFilterOptions() const
{
return m_filterOptions;
}
bool PassFilter::Matches(const Pass* pass) const
{
return Matches(pass, m_filterOptions);
}
bool PassFilter::Matches(const Pass* pass, uint32_t options) const
{
AZ_Assert( (options&m_filterOptions) == options, "options should be a subset of m_filterOptions");
// return false if the pass doesn't have a pass template or the template's name is not matching
if (options & FilterOptions::PassTemplateName && (!pass->GetPassTemplate() || pass->GetPassTemplate()->m_name != m_templateName))
{
return false;
}
if ((options & FilterOptions::PassName) && pass->GetName() != m_passName)
{
return false;
}
ParentPass* parent = pass->GetParent();
if ((options & FilterOptions::PassClass) && pass->RTTI_GetType() != m_passClassTypeId)
{
return false;
}
// search from the back of the array with the most close parent
for (int32_t index = static_cast<int32_t>(m_parentNames.size() - 1); index >= 0; index--)
if ((options & FilterOptions::OwnerRenderPipeline) && m_ownerRenderPipeline != pass->GetRenderPipeline())
{
const Name& parentName = m_parentNames[index];
while (parent)
return false;
}
// If the owner render pipeline was checked, the owner scene check can be skipped
if (options & FilterOptions::OwnerScene)
{
if (pass->GetRenderPipeline())
{
if (parent->GetName() == parentName)
// return false if the owner scene doesn't match
if (m_ownerScene != pass->GetRenderPipeline()->GetScene())
{
break;
return false;
}
parent = parent->GetParent();
}
// if parent is nullptr the it didn't find a parent has matching current parentName
if (!parent)
else
{
// return false if the pass doesn't have an owner scene
return false;
}
}
if ((options & FilterOptions::PassHierarchy))
{
// Filter for passes which have a matching name and also with ordered parents.
// For example, if the filter is initialized with
// pass name: "ShadowPass1"
// pass parents names: "MainPipeline", "Shadow"
// Passes with these names match the filter:
// "Root.MainPipeline.SwapChainPass.Shadow.ShadowPass1"
// or "Root.MainPipeline.Shadow.ShadowPass1"
// or "MainPipeline.Shadow.Group1.ShadowPass1"
//
// Passes with these names wont match:
// "MainPipeline.ShadowPass1"
// or "Shadow.MainPipeline.ShadowPass1"
ParentPass* parent = pass->GetParent();
// search from the back of the array with the most close parent
for (int32_t index = static_cast<int32_t>(m_parentNames.size() - 1); index >= 0; index--)
{
const Name& parentName = m_parentNames[index];
while (parent)
{
if (parent->GetName() == parentName)
{
break;
}
parent = parent->GetParent();
}
// move to next parent
parent = parent->GetParent();
// if parent is nullptr the it didn't find a parent has matching current parentName
if (!parent)
{
return false;
}
// move to next parent
parent = parent->GetParent();
}
}
return true;
}
const Name* PassHierarchyFilter::GetPassName() const
void PassFilter::UpdateFilterOptions()
{
return &m_passName;
}
AZStd::string PassHierarchyFilter::ToString() const
{
AZStd::string result = "PassHierarchyFilter";
for (uint32_t index = 0; index < m_parentNames.size(); index++)
m_filterOptions = FilterOptions::Empty;
if (!m_passName.IsEmpty())
{
result += AZStd::string::format(" [%s]", m_parentNames[index].GetCStr());
m_filterOptions |= FilterOptions::PassName;
}
if (!m_templateName.IsEmpty())
{
m_filterOptions |= FilterOptions::PassTemplateName;
}
if (m_parentNames.size() > 0)
{
m_filterOptions |= FilterOptions::PassHierarchy;
}
if (m_ownerRenderPipeline)
{
m_filterOptions |= FilterOptions::OwnerRenderPipeline;
}
if (m_ownerScene)
{
// If the OwnerRenderPipeline exists, we shouldn't need to filter owner scene
// Validate the owner render pipeline belongs to the owner scene
if (m_filterOptions & FilterOptions::OwnerRenderPipeline)
{
if (m_ownerRenderPipeline->GetScene() != m_ownerScene)
{
AZ_Warning("RPI", false, "The owner scene filter doesn't match owner render pipeline. It will be skipped.");
}
}
else
{
m_filterOptions |= FilterOptions::OwnerScene;
}
}
if (!m_passClassTypeId.IsNull())
{
m_filterOptions |= FilterOptions::PassClass;
}
result += AZStd::string::format(" [%s]", m_passName.GetCStr());
return result;
}
} // namespace RPI
} // namespace AZ

@ -85,47 +85,80 @@ namespace AZ
return (GetPassesForTemplate(templateName).size() > 0);
}
AZStd::vector<Pass*> PassLibrary::FindPasses(const PassFilter& passFilter) const
void PassLibrary::ForEachPass(const PassFilter& passFilter, AZStd::function<PassFilterExecutionFlow(Pass*)> passFunction)
{
const Name* passName = passFilter.GetPassName();
uint32_t filterOptions = passFilter.GetEnabledFilterOptions();
AZStd::vector<Pass*> result;
if (passName)
// A lambda function which visits each pass in a pass list, if the pass matches the pass filter, then call the pass function
auto visitList = [passFilter, passFunction](const AZStd::vector<Pass*>& passList, uint32_t options) -> PassFilterExecutionFlow
{
// If the pass' name is known, find passes with matching names first
const auto constItr = m_passNameMapping.find(*passName);
if (constItr == m_passNameMapping.end())
if (passList.size() == 0)
{
return result;
return PassFilterExecutionFlow::ContinueVisitingPasses;
}
const AZStd::vector<Pass*>& passes = constItr->second;
for (Pass* pass : passes)
// if there is not other filter options enabled, skip the filter and call pass functions directly
if (options == PassFilter::FilterOptions::Empty)
{
if (passFilter.Matches(pass))
for (Pass* pass : passList)
{
result.push_back(pass);
// If user want to skip processing, return directly.
if (passFunction(pass) == PassFilterExecutionFlow::StopVisitingPasses)
{
return PassFilterExecutionFlow::StopVisitingPasses;
}
}
return PassFilterExecutionFlow::ContinueVisitingPasses;
}
}
else
{
// If the filter doesn't know matching pass' name, need to go through all registered passes
for (auto& namePasses : m_passNameMapping)
// Check with the pass filter and call pass functions
for (Pass* pass : passList)
{
for (Pass* pass : namePasses.second)
if (passFilter.Matches(pass, options))
{
if (passFilter.Matches(pass))
if (passFunction(pass) == PassFilterExecutionFlow::StopVisitingPasses)
{
result.push_back(pass);
return PassFilterExecutionFlow::StopVisitingPasses;
}
}
}
return PassFilterExecutionFlow::ContinueVisitingPasses;
};
// Check pass template name first
if (filterOptions & PassFilter::FilterOptions::PassTemplateName)
{
auto entry = GetEntry(passFilter.GetPassTemplateName());
if (!entry)
{
return;
}
filterOptions &= ~(PassFilter::FilterOptions::PassTemplateName);
visitList(entry->m_passes, filterOptions);
return;
}
else if (filterOptions & PassFilter::FilterOptions::PassName)
{
const auto constItr = m_passNameMapping.find(passFilter.GetPassName());
if (constItr == m_passNameMapping.end())
{
return;
}
filterOptions &= ~(PassFilter::FilterOptions::PassName);
visitList(constItr->second, filterOptions);
return;
}
return result;
// check againest every passes. This might be slow
AZ_PROFILE_SCOPE(RPI, "PassLibrary::ForEachPass");
for (auto& namePasses : m_passNameMapping)
{
if (visitList(namePasses.second, filterOptions) == PassFilterExecutionFlow::StopVisitingPasses)
{
return;
}
}
}
// Add Functions...
@ -419,3 +452,4 @@ namespace AZ
} // namespace RPI
} // namespace AZ

@ -456,11 +456,6 @@ namespace AZ
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);
@ -488,9 +483,20 @@ namespace AZ
--m_passCounter;
}
AZStd::vector<Pass*> PassSystem::FindPasses(const PassFilter& passFilter) const
void PassSystem::ForEachPass(const PassFilter& filter, AZStd::function<PassFilterExecutionFlow(Pass*)> passFunction)
{
return m_passLibrary.ForEachPass(filter, passFunction);
}
Pass* PassSystem::FindFirstPass(const PassFilter& filter)
{
return m_passLibrary.FindPasses(passFilter);
Pass* foundPass = nullptr;
m_passLibrary.ForEachPass(filter, [&foundPass](RPI::Pass* pass) ->PassFilterExecutionFlow
{
foundPass = pass;
return PassFilterExecutionFlow::StopVisitingPasses;
});
return foundPass;
}
SwapChainPass* PassSystem::FindSwapChainPass(AzFramework::NativeWindowHandle windowHandle) const

@ -19,6 +19,8 @@
#include <Atom/RPI.Public/Pass/PassSystem.h>
#include <Atom/RPI.Public/Pass/RasterPass.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <Common/RPITestFixture.h>
@ -573,7 +575,7 @@ namespace UnitTest
EXPECT_TRUE(pass != nullptr);
}
TEST_F(PassTests, PassHierarchyFilter)
TEST_F(PassTests, PassFilter_PassHierarchy)
{
m_data->AddPassTemplatesToLibrary();
@ -587,62 +589,55 @@ namespace UnitTest
parent2->AsParent()->AddChild(parent1);
parent1->AsParent()->AddChild(pass);
{
// Filter with only pass name
PassHierarchyFilter filter(Name("pass1"));
EXPECT_TRUE(filter.Matches(pass.get()));
}
{
// Filter with pass hierarchy which has only one element
PassHierarchyFilter filter({ Name("pass1") });
PassFilter filter = PassFilter::CreateWithPassHierarchy({Name("pass1")});
EXPECT_TRUE(filter.Matches(pass.get()));
}
{
// Filter with empty pass hierarchy. Result one assert
// Filter with empty pass hierarchy, triggers one assert
AZ_TEST_START_TRACE_SUPPRESSION;
PassHierarchyFilter filter(AZStd::vector<Name>{});
PassFilter filter = PassFilter::CreateWithPassHierarchy(AZStd::vector<Name>{});
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
EXPECT_FALSE(filter.Matches(pass.get()));
}
{
// Filters with partial hierarchy by using string vector
AZStd::vector<AZStd::string> passHierarchy1 = { "parent1", "pass1" };
PassHierarchyFilter filter1(passHierarchy1);
PassFilter filter1 = PassFilter::CreateWithPassHierarchy(passHierarchy1);
EXPECT_TRUE(filter1.Matches(pass.get()));
AZStd::vector<AZStd::string> passHierarchy2 = { "parent2", "pass1" };
PassHierarchyFilter filter2(passHierarchy2);
PassFilter filter2 = PassFilter::CreateWithPassHierarchy(passHierarchy2);
EXPECT_TRUE(filter2.Matches(pass.get()));
AZStd::vector<AZStd::string> passHierarchy3 = { "parent3", "parent2", "pass1" };
PassHierarchyFilter filter3(passHierarchy3);
PassFilter filter3 = PassFilter::CreateWithPassHierarchy(passHierarchy3);
EXPECT_TRUE(filter3.Matches(pass.get()));
}
{
// Filters with partial hierarchy by using Name vector
AZStd::vector<Name> passHierarchy1 = { Name("parent1"), Name("pass1") };
PassHierarchyFilter filter1(passHierarchy1);
PassFilter filter1 = PassFilter::CreateWithPassHierarchy(passHierarchy1);
EXPECT_TRUE(filter1.Matches(pass.get()));
AZStd::vector<Name> passHierarchy2 = { Name("parent2"), Name("pass1")};
PassHierarchyFilter filter2(passHierarchy2);
PassFilter filter2 = PassFilter::CreateWithPassHierarchy(passHierarchy2);
EXPECT_TRUE(filter2.Matches(pass.get()));
AZStd::vector<Name> passHierarchy3 = { Name("parent3"), Name("parent2"), Name("pass1") };
PassHierarchyFilter filter3(passHierarchy3);
PassFilter filter3 = PassFilter::CreateWithPassHierarchy(passHierarchy3);
EXPECT_TRUE(filter3.Matches(pass.get()));
}
{
// Find non-leaf pass
PassHierarchyFilter filter1(AZStd::vector<AZStd::string>{"parent3", "parent1"});
PassFilter filter1 = PassFilter::CreateWithPassHierarchy(AZStd::vector<AZStd::string>{"parent3", "parent1"});
EXPECT_TRUE(filter1.Matches(parent1.get()));
PassHierarchyFilter filter2(Name("parent1"));
PassFilter filter2 = PassFilter::CreateWithPassHierarchy({ Name("parent1") });
EXPECT_TRUE(filter2.Matches(parent1.get()));
EXPECT_FALSE(filter2.Matches(pass.get()));
}
@ -650,11 +645,131 @@ namespace UnitTest
{
// Failed to find pass
// Mis-matching hierarchy
PassHierarchyFilter filter1(AZStd::vector<AZStd::string>{"Parent1", "Parent3", "pass1"});
PassFilter filter1 = PassFilter::CreateWithPassHierarchy(AZStd::vector<AZStd::string>{"Parent1", "Parent3", "pass1"});
EXPECT_FALSE(filter1.Matches(pass.get()));
// Mis-matching name
PassHierarchyFilter filter2(AZStd::vector<AZStd::string>{"Parent1", "pass1"});
PassFilter filter2 = PassFilter::CreateWithPassHierarchy(AZStd::vector<AZStd::string>{"Parent1", "pass1"});
EXPECT_FALSE(filter2.Matches(parent1.get()));
}
}
TEST_F(PassTests, PassFilter_Empty_Success)
{
m_data->AddPassTemplatesToLibrary();
// create a pass tree
Ptr<Pass> pass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1"));
Ptr<Pass> parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1"));
Ptr<Pass> parent2 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent2"));
Ptr<Pass> parent3 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent3"));
parent3->AsParent()->AddChild(parent2);
parent2->AsParent()->AddChild(parent1);
parent1->AsParent()->AddChild(pass);
PassFilter filter;
// Any pass can match an empty filter
EXPECT_TRUE(filter.Matches(pass.get()));
EXPECT_TRUE(filter.Matches(parent1.get()));
EXPECT_TRUE(filter.Matches(parent2.get()));
EXPECT_TRUE(filter.Matches(parent3.get()));
}
TEST_F(PassTests, PassFilter_PassClass_Success)
{
m_data->AddPassTemplatesToLibrary();
// create a pass tree
Ptr<Pass> pass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1"));
Ptr<Pass> depthPass = m_passSystem->CreatePassFromTemplate(Name("DepthPrePass"), Name("depthPass"));
Ptr<Pass> parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1"));
parent1->AsParent()->AddChild(pass);
parent1->AsParent()->AddChild(depthPass);
PassFilter filter1 = PassFilter::CreateWithPassClass<Pass>();
EXPECT_TRUE(filter1.Matches(pass.get()));
EXPECT_FALSE(filter1.Matches(parent1.get()));
PassFilter filter2 = PassFilter::CreateWithPassClass<ParentPass>();
EXPECT_FALSE(filter2.Matches(pass.get()));
EXPECT_TRUE(filter2.Matches(parent1.get()));
}
TEST_F(PassTests, PassFilter_PassTemplate_Success)
{
m_data->AddPassTemplatesToLibrary();
// create a pass tree
Ptr<Pass> childPass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1"));
Ptr<Pass> parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1"));
PassFilter filter1 = PassFilter::CreateWithTemplateName(Name("Pass"), (Scene*) nullptr);
// childPass doesn't have a template
EXPECT_FALSE(filter1.Matches(childPass.get()));
PassFilter filter2 = PassFilter::CreateWithTemplateName(Name("ParentPass"), (Scene*) nullptr);
EXPECT_TRUE(filter2.Matches(parent1.get()));
}
TEST_F(PassTests, ForEachPass_PassTemplateFilter_Success)
{
m_data->AddPassTemplatesToLibrary();
// create a pass tree
Ptr<Pass> pass = m_passSystem->CreatePassFromClass(Name("Pass"), Name("pass1"));
Ptr<Pass> parent1 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent1"));
Ptr<Pass> parent2 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent2"));
Ptr<Pass> parent3 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent3"));
parent3->AsParent()->AddChild(parent2);
parent2->AsParent()->AddChild(parent1);
parent1->AsParent()->AddChild(pass);
// Create render pipeline
const RPI::PipelineViewTag viewTag{ "viewTag1" };
RPI::RenderPipelineDescriptor desc;
desc.m_mainViewTagName = viewTag.GetStringView();
desc.m_name = "TestPipeline";
RPI::RenderPipelinePtr pipeline = RPI::RenderPipeline::CreateRenderPipeline(desc);
Ptr<Pass> parent4 = m_passSystem->CreatePassFromTemplate(Name("ParentPass"), Name("parent4"));
pipeline->GetRootPass()->AddChild(parent4);
Name templateName = Name("ParentPass");
PassFilter filter1 = PassFilter::CreateWithTemplateName(templateName, (RenderPipeline*)nullptr);
int count = 0;
m_passSystem->ForEachPass(filter1, [&count, templateName](RPI::Pass* pass) -> PassFilterExecutionFlow
{
EXPECT_TRUE(pass->GetPassTemplate()->m_name == templateName);
count++;
return PassFilterExecutionFlow::ContinueVisitingPasses;
});
// three from CreatePassFromTemplate() calls and one from Render Pipeline.
EXPECT_TRUE(count == 4);
count = 0;
m_passSystem->ForEachPass(filter1, [&count, templateName](RPI::Pass* pass) -> PassFilterExecutionFlow
{
EXPECT_TRUE(pass->GetPassTemplate()->m_name == templateName);
count++;
return PassFilterExecutionFlow::StopVisitingPasses;
});
EXPECT_TRUE(count == 1);
PassFilter filter2 = PassFilter::CreateWithTemplateName(templateName, pipeline.get());
count = 0;
m_passSystem->ForEachPass(filter2, [&count]([[maybe_unused]] RPI::Pass* pass) -> PassFilterExecutionFlow
{
count++;
return PassFilterExecutionFlow::ContinueVisitingPasses;
});
// only the ParentPass in the render pipeline was found
EXPECT_TRUE(count == 1);
}
}

@ -19,6 +19,7 @@
#include <Atom/RPI.Public/View.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Pass/PassFilter.h>
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/Shader/Shader.h>
@ -142,12 +143,13 @@ namespace AZ
EnablePasses(true);
}
void HairFeatureProcessor::EnablePasses([[maybe_unused]] bool enable)
void HairFeatureProcessor::EnablePasses(bool enable)
{
RPI::Ptr<RPI::Pass> desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName);
if (desiredPass)
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairParentPassName, GetParentScene());
RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter);
if (pass)
{
desiredPass->SetEnabled(enable);
pass->SetEnabled(enable);
}
}
@ -309,10 +311,17 @@ namespace AZ
m_forceClearRenderData = true;
}
bool HairFeatureProcessor::HasHairParentPass()
{
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairParentPassName, GetParentScene());
RPI::Pass* pass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter);
return pass;
}
void HairFeatureProcessor::OnRenderPipelineAdded(RPI::RenderPipelinePtr renderPipeline)
{
// Proceed only if this is the main pipeline that contains the parent pass
if (!renderPipeline.get()->GetRootPass()->FindPassByNameRecursive(HairParentPassName))
if (!HasHairParentPass())
{
return;
}
@ -323,10 +332,10 @@ namespace AZ
m_forceRebuildRenderData = true;
}
void HairFeatureProcessor::OnRenderPipelineRemoved(RPI::RenderPipeline* renderPipeline)
void HairFeatureProcessor::OnRenderPipelineRemoved([[maybe_unused]] RPI::RenderPipeline* renderPipeline)
{
// Proceed only if this is the main pipeline that contains the parent pass
if (!renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName))
if (!HasHairParentPass())
{
return;
}
@ -338,7 +347,7 @@ namespace AZ
void HairFeatureProcessor::OnRenderPipelinePassesChanged(RPI::RenderPipeline* renderPipeline)
{
// Proceed only if this is the main pipeline that contains the parent pass
if (!renderPipeline->GetRootPass()->FindPassByNameRecursive(HairParentPassName))
if (!HasHairParentPass())
{
return;
}
@ -457,7 +466,8 @@ namespace AZ
{
m_computePasses[passName] = nullptr;
RPI::Ptr<RPI::Pass> desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(passName);
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(passName, m_renderPipeline);
RPI::Ptr<RPI::Pass> desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter);
if (desiredPass)
{
m_computePasses[passName] = static_cast<HairSkinningComputePass*>(desiredPass.get());
@ -479,7 +489,8 @@ namespace AZ
{
m_hairPPLLRasterPass = nullptr; // reset it to null, just in case it fails to load the assets properly
RPI::Ptr<RPI::Pass> desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairPPLLRasterPassName);
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairPPLLRasterPassName, m_renderPipeline);
RPI::Ptr<RPI::Pass> desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter);
if (desiredPass)
{
m_hairPPLLRasterPass = static_cast<HairPPLLRasterPass*>(desiredPass.get());
@ -497,7 +508,8 @@ namespace AZ
{
m_hairPPLLResolvePass = nullptr; // reset it to null, just in case it fails to load the assets properly
RPI::Ptr<RPI::Pass> desiredPass = m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairPPLLResolvePassName);
RPI::PassFilter passFilter = RPI::PassFilter::CreateWithPassName(HairPPLLResolvePassName, m_renderPipeline);
RPI::Ptr<RPI::Pass> desiredPass = RPI::PassSystemInterface::Get()->FindFirstPass(passFilter);
if (desiredPass)
{
m_hairPPLLResolvePass = static_cast<HairPPLLResolvePass*>(desiredPass.get());
@ -518,8 +530,8 @@ namespace AZ
m_hairShortCutGeometryDepthAlphaPass = nullptr;
m_hairShortCutGeometryShadingPass = nullptr;
m_hairShortCutGeometryDepthAlphaPass = static_cast<HairShortCutGeometryDepthAlphaPass*>(
m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairShortCutGeometryDepthAlphaPassName).get());
RPI::PassFilter depthAlphaPassFilter = RPI::PassFilter::CreateWithPassName(HairShortCutGeometryDepthAlphaPassName, m_renderPipeline);
m_hairShortCutGeometryDepthAlphaPass = static_cast<HairShortCutGeometryDepthAlphaPass*>(RPI::PassSystemInterface::Get()->FindFirstPass(depthAlphaPassFilter));
if (m_hairShortCutGeometryDepthAlphaPass)
{
m_hairShortCutGeometryDepthAlphaPass->SetFeatureProcessor(this);
@ -530,8 +542,8 @@ namespace AZ
return false;
}
m_hairShortCutGeometryShadingPass = static_cast<HairShortCutGeometryShadingPass*>(
m_renderPipeline->GetRootPass()->FindPassByNameRecursive(HairShortCutGeometryShadingPassName).get());
RPI::PassFilter shaderingPassFilter = RPI::PassFilter::CreateWithPassName(HairShortCutGeometryShadingPassName, m_renderPipeline);
m_hairShortCutGeometryShadingPass = static_cast<HairShortCutGeometryShadingPass*>(RPI::PassSystemInterface::Get()->FindFirstPass(shaderingPassFilter));
if (m_hairShortCutGeometryShadingPass)
{
m_hairShortCutGeometryShadingPass->SetFeatureProcessor(this);

@ -165,6 +165,8 @@ namespace AZ
void EnablePasses(bool enable);
bool HasHairParentPass();
//! The following will serve to register the FP in the Thumbnail system
AZStd::vector<AZStd::string> m_hairFeatureProcessorRegistryName;

Loading…
Cancel
Save