Jelajahi Sumber

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 <[email protected]>
Qing Tao 3 tahun lalu
induk
melakukan
fe8dac7989
37 mengubah file dengan 787 tambahan dan 521 penghapusan
  1. 3 3
      Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h
  2. 1 6
      Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h
  3. 1 1
      Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h
  4. 32 39
      Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp
  5. 37 24
      Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp
  6. 6 6
      Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp
  7. 9 16
      Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp
  8. 19 18
      Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp
  9. 28 20
      Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp
  10. 1 1
      Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h
  11. 8 12
      Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp
  12. 15 14
      Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp
  13. 8 6
      Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp
  14. 18 33
      Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp
  15. 2 29
      Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp
  16. 0 2
      Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h
  17. 10 9
      Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp
  18. 1 1
      Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp
  19. 14 12
      Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp
  20. 20 34
      Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp
  21. 2 2
      Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h
  22. 8 5
      Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp
  23. 1 1
      Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h
  24. 0 3
      Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h
  25. 4 0
      Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h
  26. 70 80
      Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h
  27. 2 2
      Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h
  28. 2 2
      Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h
  29. 17 6
      Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h
  30. 0 23
      Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp
  31. 5 0
      Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp
  32. 205 42
      Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp
  33. 58 24
      Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp
  34. 13 7
      Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp
  35. 137 22
      Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp
  36. 28 16
      Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp
  37. 2 0
      Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h

+ 3 - 3
Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/ImGuiUtils.h

@@ -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;
             }
 

+ 1 - 6
Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ImGui/SystemBus.h

@@ -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.

+ 1 - 1
Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Utils/FrameCaptureBus.h

@@ -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

+ 32 - 39
Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp

@@ -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);
                     }

+ 37 - 24
Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseGlobalIlluminationFeatureProcessor.cpp

@@ -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

+ 6 - 6
Gems/Atom/Feature/Common/Code/Source/DiffuseGlobalIllumination/DiffuseProbeGridFeatureProcessor.cpp

@@ -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;
+                    });
             }
         }
 

+ 9 - 16
Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp

@@ -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();
         }
 

+ 19 - 18
Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp

@@ -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;
         }

+ 28 - 20
Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.cpp

@@ -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();

+ 1 - 1
Gems/Atom/Feature/Common/Code/Source/ImGui/ImGuiSystemComponent.h

@@ -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;
 

+ 8 - 12
Gems/Atom/Feature/Common/Code/Source/PostProcess/DepthOfField/DepthOfFieldSettings.cpp

@@ -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>
@@ -259,24 +260,19 @@ 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)

+ 15 - 14
Gems/Atom/Feature/Common/Code/Source/PostProcess/ExposureControl/ExposureControlSettings.cpp

@@ -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);
-            }
+            // [GFX-TODO][ATOM-13224] Remove UpdateLuminanceHeatmap and UpdateEyeAdaptationPass            
+            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()

+ 8 - 6
Gems/Atom/Feature/Common/Code/Source/PostProcessing/LookModificationCompositePass.cpp

@@ -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."

+ 18 - 33
Gems/Atom/Feature/Common/Code/Source/PostProcessing/SMAAFeatureProcessor.cpp

@@ -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)

+ 2 - 29
Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.cpp

@@ -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

+ 0 - 2
Gems/Atom/Feature/Common/Code/Source/ProfilingCaptureSystemComponent.h

@@ -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;

+ 10 - 9
Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionCopyFrameBufferPass.cpp

@@ -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();
         }

+ 1 - 1
Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.cpp

@@ -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;

+ 14 - 12
Gems/Atom/Feature/Common/Code/Source/ReflectionScreenSpace/ReflectionScreenSpaceCompositePass.cpp

@@ -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);
         }

+ 20 - 34
Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp

@@ -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()

+ 2 - 2
Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h

@@ -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();

+ 8 - 5
Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp

@@ -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());

+ 1 - 1
Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h

@@ -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;

+ 0 - 3
Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/ParentPass.h

@@ -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;
 

+ 4 - 0
Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h

@@ -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);

+ 70 - 80
Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassFilter.h

@@ -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;
+        class Scene;
+        class RenderPipeline;
 
-            //! Return this filter's info as a string
-            virtual AZStd::string ToString() const = 0;
-        };
-
-        //! 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
-        {
-            return AZStd::string::format("PassClassFilter<%s>", PassClass::RTTI_TypeName());
+        template <typename PassClass>
+        PassFilter PassFilter::CreateWithPassClass()
+        {            
+            PassFilter filter;
+            filter.m_passClassTypeId = PassClass::RTTI_Type();
+            filter.UpdateFilterOptions();
+            return filter;
         }
     }   // namespace RPI
 }   // namespace AZ

+ 2 - 2
Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassLibrary.h

@@ -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:
 

+ 2 - 2
Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystem.h

@@ -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

+ 17 - 6
Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/PassSystemInterface.h

@@ -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;
 
@@ -197,9 +201,16 @@ 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

+ 0 - 23
Gems/Atom/RPI/Code/Source/RPI.Public/Pass/ParentPass.cpp

@@ -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)

+ 5 - 0
Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp

@@ -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

+ 205 - 42
Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassFilter.cpp

@@ -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;
             }
 
-            ParentPass* parent = pass->GetParent();
+            if ((options & FilterOptions::PassName) && pass->GetName() != m_passName)
+            {
+                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::PassClass) && pass->RTTI_GetType() != m_passClassTypeId)
             {
-                const Name& parentName = m_parentNames[index];
-                while (parent)
+                return false;
+            }
+
+            if ((options & FilterOptions::OwnerRenderPipeline) && m_ownerRenderPipeline != pass->GetRenderPipeline())
+            {
+                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"
 
-                // move to next parent 
-                parent = parent->GetParent();
+                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();
+                    }
+
+                    // 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

+ 58 - 24
Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassLibrary.cpp

@@ -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
+

+ 13 - 7
Gems/Atom/RPI/Code/Source/RPI.Public/Pass/PassSystem.cpp

@@ -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);
@@ -487,10 +482,21 @@ namespace AZ
             RemovePassFromLibrary(pass);
             --m_passCounter;
         }
+                
+        void PassSystem::ForEachPass(const PassFilter& filter, AZStd::function<PassFilterExecutionFlow(Pass*)> passFunction)
+        {
+            return m_passLibrary.ForEachPass(filter, passFunction);
+        }
 
-        AZStd::vector<Pass*> PassSystem::FindPasses(const PassFilter& passFilter) const
+        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

+ 137 - 22
Gems/Atom/RPI/Code/Tests/Pass/PassTests.cpp

@@ -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);
+
+    }
 }

+ 28 - 16
Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.cpp

@@ -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());
@@ -478,8 +488,9 @@ namespace AZ
             bool HairFeatureProcessor::InitPPLLFillPass()
             {
                 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);

+ 2 - 0
Gems/AtomTressFX/Code/Rendering/HairFeatureProcessor.h

@@ -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;