ソースを参照

Merge pull request #13540 from aws-lumberyard-dev/Atom/santorac/MaterialPipelineFiltering

Made RenderPipeline filter by material pipeline tag
santorac 2 年 前
コミット
076fd1943f
47 ファイル変更755 行追加466 行削除
  1. 2 2
      Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp
  2. 1 0
      Gems/Atom/Feature/Common/Assets/Passes/LowEndRenderPipeline.azasset
  3. 1 0
      Gems/Atom/Feature/Common/Assets/Passes/MainRenderPipeline.azasset
  4. 1 0
      Gems/Atom/Feature/Common/Assets/Passes/MultiViewRenderPipeline.azasset
  5. 1 0
      Gems/Atom/Feature/Common/Assets/Passes/XRLeftRenderPipeline.azasset
  6. 1 0
      Gems/Atom/Feature/Common/Assets/Passes/XRRightRenderPipeline.azasset
  7. 11 8
      Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp
  8. 6 6
      Gems/Atom/RHI/Code/Include/Atom/RHI/DrawPacket.h
  9. 3 4
      Gems/Atom/RHI/Code/Include/Atom/RHI/DrawPacketBuilder.h
  10. 4 3
      Gems/Atom/RHI/Code/Source/RHI/DrawPacket.cpp
  11. 7 7
      Gems/Atom/RHI/Code/Source/RHI/DrawPacketBuilder.cpp
  12. 34 5
      Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h
  13. 1 1
      Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Shader/ShaderSourceData.h
  14. 7 2
      Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Material/Material.h
  15. 1 0
      Gems/Atom/RPI/Code/Include/Atom/RPI.Public/MeshDrawPacket.h
  16. 12 8
      Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h
  17. 5 0
      Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h
  18. 7 2
      Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h
  19. 12 4
      Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialFunctor.h
  20. 2 0
      Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h
  21. 18 15
      Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAsset.h
  22. 20 14
      Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h
  23. 8 0
      Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/ShaderCollection.h
  24. 3 0
      Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/System/RenderPipelineDescriptor.h
  25. 31 15
      Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialTypeBuilder.cpp
  26. 104 50
      Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp
  27. 43 23
      Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp
  28. 31 15
      Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp
  29. 22 13
      Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp
  30. 2 2
      Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp
  31. 4 4
      Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp
  32. 7 2
      Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp
  33. 43 29
      Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp
  34. 2 1
      Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyDescriptor.cpp
  35. 54 42
      Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp
  36. 67 53
      Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp
  37. 1 0
      Gems/Atom/RPI/Code/Source/RPI.Reflect/System/RenderPipelineDescriptor.cpp
  38. 32 30
      Gems/Atom/RPI/Code/Tests/Material/LuaMaterialFunctorTests.cpp
  39. 12 10
      Gems/Atom/RPI/Code/Tests/Material/MaterialFunctorTests.cpp
  40. 26 22
      Gems/Atom/RPI/Code/Tests/Material/MaterialTests.cpp
  41. 30 26
      Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp
  42. 15 11
      Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp
  43. 8 2
      Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp
  44. 1 1
      Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiMaterialDetails.inl
  45. 49 31
      Gems/AtomLyIntegration/EditorModeFeedback/Code/Source/Draw/EditorStateMeshDrawPacket.cpp
  46. 1 1
      Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp
  47. 2 2
      Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.cpp

+ 2 - 2
Gems/Atom/Asset/Shader/Code/Source/Editor/ShaderAssetBuilder.cpp

@@ -389,8 +389,8 @@ namespace AZ
             RPI::ShaderAssetCreator shaderAssetCreator;
             shaderAssetCreator.Begin(Uuid::CreateRandom());
 
-            shaderAssetCreator.SetName(AZ::Name{shaderFileName.c_str()});
-            shaderAssetCreator.SetDrawListName(Name(shaderSourceData.m_drawListName));
+            shaderAssetCreator.SetName(AZ::Name{shaderFileName});
+            shaderAssetCreator.SetDrawListName(shaderSourceData.m_drawListName);
             shaderAssetCreator.SetShaderAssetBuildTimestamp(shaderAssetBuildTimestamp);
 
             // The ShaderOptionGroupLayout must be the same across all supervariants because

+ 1 - 0
Gems/Atom/Feature/Common/Assets/Passes/LowEndRenderPipeline.azasset

@@ -5,6 +5,7 @@
     "ClassData": {
         "Name": "LowEndPipeline",
         "MainViewTag": "MainCamera",
+        "MaterialPipelineTag": "LowEndPipeline",
         "RootPassTemplate": "LowEndPipelineTemplate",
         "AllowModification": true,
         "RenderSettings": {

+ 1 - 0
Gems/Atom/Feature/Common/Assets/Passes/MainRenderPipeline.azasset

@@ -6,6 +6,7 @@
         "Name": "MainPipeline",
         "MainViewTag": "MainCamera",
         "RootPassTemplate": "MainPipeline",
+        "MaterialPipelineTag": "MainPipeline",
         "AllowModification": true,
         "RenderSettings": {
             "MultisampleState": {

+ 1 - 0
Gems/Atom/Feature/Common/Assets/Passes/MultiViewRenderPipeline.azasset

@@ -5,6 +5,7 @@
     "ClassData": {
         "Name": "MultiViewPipeline",
         "MainViewTag": "MainCamera",
+        "MaterialPipelineTag": "MultiViewPipeline",
         "RootPassTemplate": "MultiViewPipelineTemplate",
         "AllowModification": true,
         "RenderSettings": {

+ 1 - 0
Gems/Atom/Feature/Common/Assets/Passes/XRLeftRenderPipeline.azasset

@@ -5,6 +5,7 @@
     "ClassData": {
         "Name": "MultiViewXRLeftPipeline",
         "MainViewTag": "MainCamera",
+        "MaterialPipelineTag": "MultiViewPipeline",
         "RootPassTemplate": "MultiViewXRLeftPipelineTemplate",
         "AllowModification": true,
         "RenderSettings": {

+ 1 - 0
Gems/Atom/Feature/Common/Assets/Passes/XRRightRenderPipeline.azasset

@@ -5,6 +5,7 @@
     "ClassData": {
         "Name": "MultiViewXRRightPipeline",
         "MainViewTag": "MainCamera",
+        "MaterialPipelineTag": "MultiViewPipeline",
         "RootPassTemplate": "MultiViewXRRightPipelineTemplate",
         "AllowModification": true,
         "RenderSettings": {

+ 11 - 8
Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp

@@ -1710,20 +1710,23 @@ namespace AZ
             // Note: this should be changed to have the material automatically set the forwardPassIBLSpecular
             // property and look for that instead of the shader option.
             // [GFX TODO][ATOM-5040] Address Property Metadata Feedback Loop
-            for (auto& shaderItem : material->GetShaderCollection())
+            for (auto& shaderCollectionIter : material->GetShaderCollections())
             {
-                if (shaderItem.IsEnabled())
+                for (auto& shaderItem : shaderCollectionIter.second)
                 {
-                    RPI::ShaderOptionIndex index = shaderItem.GetShaderOptionGroup().GetShaderOptionLayout()->FindShaderOptionIndex(Name{ "o_materialUseForwardPassIBLSpecular" });
-                    if (index.IsValid())
+                    if (shaderItem.IsEnabled())
                     {
-                        RPI::ShaderOptionValue value = shaderItem.GetShaderOptionGroup().GetValue(Name{ "o_materialUseForwardPassIBLSpecular" });
-                        if (value.GetIndex() == 1)
+                        RPI::ShaderOptionIndex index = shaderItem.GetShaderOptionGroup().GetShaderOptionLayout()->FindShaderOptionIndex(Name{"o_materialUseForwardPassIBLSpecular"});
+                        if (index.IsValid())
                         {
-                            return true;
+                            RPI::ShaderOptionValue value = shaderItem.GetShaderOptionGroup().GetValue(Name{"o_materialUseForwardPassIBLSpecular"});
+                            if (value.GetIndex() == 1)
+                            {
+                                return true;
+                            }
                         }
-                    }
 
+                    }
                 }
             }
 

+ 6 - 6
Gems/Atom/RHI/Code/Include/Atom/RHI/DrawPacket.h

@@ -52,11 +52,11 @@ namespace AZ
             //! Returns the draw item and its properties associated with the provided index.
             DrawItemProperties GetDrawItem(size_t index) const;
 
-            //! Returns the draw list tag associated with the provided index.
+            //! Returns the draw list tag associated with the provided index, used to filter the draw item into an appropriate pass.
             DrawListTag GetDrawListTag(size_t index) const;
 
-            //! Returns the draw filter mask which applied to all the draw items.
-            DrawFilterMask GetDrawFilterMask() const;
+            //! Returns the draw filter mask associated with the provided index, used to filter the draw item into an appropriate render pipeline.
+            DrawFilterMask GetDrawFilterMask(size_t index) const;
 
             //! Overloaded operator delete for freeing a draw packet.
             void operator delete(void* p, size_t size);
@@ -71,9 +71,6 @@ namespace AZ
             // The bit-mask of all active filter tags.
             DrawListMask m_drawListMask = 0;
 
-            // The draw filter applies to each draw item
-            DrawFilterMask m_drawFilterMask = DrawFilterMaskDefaultValue;
-
             // The index buffer view used when the draw call is indexed.
             IndexBufferView m_indexBufferView;
 
@@ -94,6 +91,9 @@ namespace AZ
             // List of draw list tags associated with the draw item index.
             const DrawListTag* m_drawListTags = nullptr;
 
+            // List of draw filter masks associated with the draw item index.
+            const DrawFilterMask* m_drawFilterMasks = nullptr;
+
             // List of shader resource groups shared by all draw items.
             const ShaderResourceGroup* const* m_shaderResourceGroups = nullptr;
 

+ 3 - 4
Gems/Atom/RHI/Code/Include/Atom/RHI/DrawPacketBuilder.h

@@ -43,7 +43,9 @@ namespace AZ
                 //! The sort key assigned to this draw item.
                 DrawItemSortKey m_sortKey = 0;
 
-                //! The filter associated to this draw item. 
+                //! Mask for filtering the draw item into specific render pipelines.
+                //! We use a mask because the same item could be reused in multiple pipelines. For example, a simple
+                //! depth pre-pass could be present in multiple pipelines.
                 DrawFilterMask m_drawFilterMask = DrawFilterMaskDefaultValue;
             };
 
@@ -68,8 +70,6 @@ namespace AZ
 
             void AddShaderResourceGroup(const ShaderResourceGroup* shaderResourceGroup);
 
-            void SetDrawFilterMask(DrawFilterMask filterMask);
-
             void AddDrawItem(const DrawRequest& request);
 
             const DrawPacket* End();
@@ -80,7 +80,6 @@ namespace AZ
             IAllocator* m_allocator = nullptr;
             DrawArguments m_drawArguments;
             DrawListMask m_drawListMask = 0;
-            DrawFilterMask m_drawFilterMask = DrawFilterMaskDefaultValue;
             size_t m_streamBufferViewCount = 0;
             IndexBufferView m_indexBufferView;
             AZStd::fixed_vector<DrawRequest, DrawItemCountMax> m_drawRequests;

+ 4 - 3
Gems/Atom/RHI/Code/Source/RHI/DrawPacket.cpp

@@ -23,7 +23,7 @@ namespace AZ
         DrawItemProperties DrawPacket::GetDrawItem(size_t index) const
         {
             AZ_Assert(index < GetDrawItemCount(), "Out of bounds array access!");
-            return DrawItemProperties(&m_drawItems[index], m_drawItemSortKeys[index], m_drawFilterMask);
+            return DrawItemProperties(&m_drawItems[index], m_drawItemSortKeys[index], m_drawFilterMasks[index]);
         }
 
         DrawListTag DrawPacket::GetDrawListTag(size_t index) const
@@ -32,9 +32,10 @@ namespace AZ
             return m_drawListTags[index];
         }
 
-        DrawFilterMask DrawPacket::GetDrawFilterMask() const
+        DrawFilterMask DrawPacket::GetDrawFilterMask(size_t index) const
         {
-            return m_drawFilterMask;
+            AZ_Assert(index < GetDrawItemCount(), "Out of bounds array access!");
+            return m_drawFilterMasks[index];
         }
 
         DrawListMask DrawPacket::GetDrawListMask() const

+ 7 - 7
Gems/Atom/RHI/Code/Source/RHI/DrawPacketBuilder.cpp

@@ -78,11 +78,6 @@ namespace AZ
             }
         }
 
-        void DrawPacketBuilder::SetDrawFilterMask(DrawFilterMask filterMask)
-        {
-            m_drawFilterMask = filterMask;
-        }
-
         void DrawPacketBuilder::AddDrawItem(const DrawRequest& request)
         {
             if (request.m_listTag.IsValid())
@@ -135,6 +130,10 @@ namespace AZ
                 sizeof(DrawListTag) * m_drawRequests.size(),
                 AZStd::alignment_of<DrawListTag>::value);
 
+            const VirtualAddress drawFilterMasksOffset = linearAllocator.Allocate(
+                sizeof(DrawFilterMask) * m_drawRequests.size(),
+                AZStd::alignment_of<DrawFilterMask>::value);
+
             const VirtualAddress shaderResourceGroupsOffset = linearAllocator.Allocate(
                 sizeof(const ShaderResourceGroup*) * m_shaderResourceGroups.size(),
                 AZStd::alignment_of<const ShaderResourceGroup*>::value);
@@ -166,7 +165,6 @@ namespace AZ
             drawPacket->m_allocator = m_allocator;
             drawPacket->m_indexBufferView =  m_indexBufferView;
             drawPacket->m_drawListMask = m_drawListMask;
-            drawPacket->m_drawFilterMask = m_drawFilterMask;
 
             if (shaderResourceGroupsOffset.IsValid())
             {
@@ -220,16 +218,19 @@ namespace AZ
             auto drawItems = reinterpret_cast<DrawItem*>(allocationData + drawItemsOffset.m_ptr);
             auto drawItemSortKeys = reinterpret_cast<DrawItemSortKey*>(allocationData + drawItemSortKeysOffset.m_ptr);
             auto drawListTags = reinterpret_cast<DrawListTag*>(allocationData + drawListTagsOffset.m_ptr);
+            auto drawFilterMasks = reinterpret_cast<DrawFilterMask*>(allocationData + drawFilterMasksOffset.m_ptr);
             drawPacket->m_drawItemCount = aznumeric_caster(m_drawRequests.size());
             drawPacket->m_drawItems = drawItems;
             drawPacket->m_drawItemSortKeys = drawItemSortKeys;
             drawPacket->m_drawListTags = drawListTags;
+            drawPacket->m_drawFilterMasks = drawFilterMasks;
 
             for (size_t i = 0; i < m_drawRequests.size(); ++i)
             {
                 const DrawRequest& drawRequest = m_drawRequests[i];
 
                 drawListTags[i] = drawRequest.m_listTag;
+                drawFilterMasks[i] = drawRequest.m_drawFilterMask;
                 drawItemSortKeys[i] = drawRequest.m_sortKey;
 
                 DrawItem& drawItem = drawItems[i];
@@ -290,7 +291,6 @@ namespace AZ
             m_rootConstants = {};
             m_scissors.clear();
             m_viewports.clear();
-            m_drawFilterMask = DrawFilterMaskDefaultValue;
         }
     }
 }

+ 34 - 5
Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Material/MaterialTypeSourceData.h

@@ -233,7 +233,20 @@ namespace AZ
                 //! Collection of all available user-facing properties
                 AZStd::vector<AZStd::unique_ptr<PropertyGroup>> m_propertyGroups;
             };
-            
+
+            //! This holds data that is specific to one material pipeline. A list of these will allow
+            //! the MaterialTypeAsset to work with multiple render pipelines.
+            struct MaterialPipelineData
+            {
+                AZ_TYPE_INFO(AZ::RPI::MaterialTypeSourceData::MaterialPipelineData, "{AA4648A2-4E0A-4AAB-BC85-FE762D449CA7}");
+
+                //! A list of specific shaders that will be used to render the material.
+                AZStd::vector<ShaderVariantReferenceData> m_shaderCollection;
+
+                //! Material functors provide custom logic and calculations to configure shaders, render states, and more. See MaterialFunctor.h for details.
+                AZStd::vector<Ptr<MaterialFunctorSourceDataHolder>> m_materialFunctorSourceData;
+            };
+
             AZStd::string m_description;
 
             //! Version 1 is the default and should not contain any version update.
@@ -241,9 +254,6 @@ namespace AZ
             
             VersionUpdates m_versionUpdates;
 
-            //! A list of specific shaders that will be used to render the material.
-            AZStd::vector<ShaderVariantReferenceData> m_shaderCollection;
-
             //! This indicates the name of the lighting model that this material type uses.
             //! For example, "Standard", "Enhanced", "Skin". The actual set of available lighting models
             //! is determined by the .materialpipeline.
@@ -256,9 +266,16 @@ namespace AZ
             //! This is relevant for "abstract" material type files (see IsAbstractFormat()).
             AZStd::string m_materialShaderCode;
 
+            //! A list of specific shaders that will be used to render the material.
+            AZStd::vector<ShaderVariantReferenceData> m_shaderCollection;
+
             //! Material functors provide custom logic and calculations to configure shaders, render states, and more. See MaterialFunctor.h for details.
             AZStd::vector<Ptr<MaterialFunctorSourceDataHolder>> m_materialFunctorSourceData;
 
+            //! Contains shaders and other data for use in specific render pipelines.
+            //! To apply shaders to all render pipelines, use the @m_shaderCollection and @m_materialFunctorSourceData above.
+            AZStd::unordered_map<Name, MaterialPipelineData> m_pipelineData;
+
             //! Override names for UV input in the shaders of this material type.
             //! Using ordered map to sort names on loading.
             using UvNameMap = AZStd::map<AZStd::string, AZStd::string>;
@@ -373,7 +390,19 @@ namespace AZ
             //! Groups with the same name will be consolidated into a single entry.
             //! Operates on the old format PropertyLayout::m_groups, used for conversion to the new format.
             AZStd::vector<GroupDefinition> GetOldFormatGroupDefinitionsInDisplayOrder() const;
-            
+
+            bool AddShaders(
+                MaterialTypeAssetCreator& materialTypeAssetCreator,
+                const Name& materialPipelineName,
+                const AZStd::vector<ShaderVariantReferenceData>& shaderCollection,
+                AZStd::string_view materialTypeSourceFilePath) const;
+
+            bool AddFunctors(
+                MaterialTypeAssetCreator& materialTypeAssetCreator,
+                const Name& materialPipelineName,
+                const AZStd::vector<Ptr<MaterialFunctorSourceDataHolder>>& materialFunctorSourceData,
+                AZStd::string_view materialTypeSourceFilePath) const;
+
             PropertyLayout m_propertyLayout;
         };
         

+ 1 - 1
Gems/Atom/RPI/Code/Include/Atom/RPI.Edit/Shader/ShaderSourceData.h

@@ -70,7 +70,7 @@ namespace AZ
             // This can override the default shader option values specified in the shader code.
             ShaderOptionValuesSourceData m_shaderOptionValues;
 
-            AZStd::string m_drawListName;
+            Name m_drawListName;
 
             ProgramSettings m_programSettings;
 

+ 7 - 2
Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Material/Material.h

@@ -104,7 +104,12 @@ namespace AZ
             ChangeId GetCurrentChangeId() const;
 
             //! Return the set of shaders to be run by this material.
-            const ShaderCollection& GetShaderCollection() const;
+            const MaterialPipelineShaderCollections& GetShaderCollections() const;
+
+            //! Returns the collection of shaders that this material could run for a given pipeline.
+            //! @param forPipeline the name of the material pipeline to query for shaders. For MaterialPipelineNameCommon, 
+            //!        this returns a list of shaders that should be sent to all pipelines.
+            const ShaderCollection& GetShaderCollection(const Name& forPipeline) const;
 
             //! Attempts to set the value of a system-level shader option that is controlled by this material.
             //! This applies to all shaders in the material's ShaderCollection.
@@ -184,7 +189,7 @@ namespace AZ
             MaterialPropertyFlags m_propertyOverrideFlags;
 
             //! A copy of the MaterialAsset's ShaderCollection is stored here to allow material-specific changes to the default collection.
-            ShaderCollection m_shaderCollection;
+            MaterialPipelineShaderCollections m_shaderCollections;
 
             //! Tracks each change made to material properties.
             //! Initialized to DEFAULT_CHANGE_ID+1 to ensure that GetCurrentChangeId() will not return DEFAULT_CHANGE_ID (a value that client 

+ 1 - 0
Gems/Atom/RPI/Code/Include/Atom/RPI.Public/MeshDrawPacket.h

@@ -31,6 +31,7 @@ namespace AZ
             struct ShaderData
             {
                 Data::Instance<Shader> m_shader;
+                Name m_materialPipelineName;
                 Name m_shaderTag;
                 ShaderVariantId m_requestedShaderVariantId;
                 ShaderVariantId m_activeShaderVariantId;

+ 12 - 8
Gems/Atom/RPI/Code/Include/Atom/RPI.Public/RenderPipeline.h

@@ -9,6 +9,7 @@
 #pragma once
 
 #include <Atom/RHI/DrawList.h>
+#include <Atom/RHI/DrawFilterTagRegistry.h>
 
 #include <Atom/RPI.Public/Base.h>
 #include <Atom/RPI.Public/PipelinePassChanges.h>
@@ -203,9 +204,6 @@ namespace AZ
             //! Get current render mode
             RenderMode GetRenderMode() const;
 
-            //! Get draw filter tag
-            RHI::DrawFilterTag GetDrawFilterTag() const;
-
             //! Get draw filter mask
             RHI::DrawFilterMask GetDrawFilterMask() const;
 
@@ -279,7 +277,8 @@ namespace AZ
             // if the view already exists in map, its DrawListMask will be combined to the existing one's
             void CollectPersistentViews(AZStd::map<ViewPtr, RHI::DrawListMask>& outViewMasks) const;
 
-            void SetDrawFilterTag(RHI::DrawFilterTag);
+            void SetDrawFilterTags(RHI::DrawFilterTagRegistry* tagRegistry);
+            void ReleaseDrawFilterTags(RHI::DrawFilterTagRegistry* tagRegistry);
 
             // End of functions accessed by Scene class
             //////////////////////////////////////////////////
@@ -302,6 +301,9 @@ namespace AZ
             // RenderPipeline's name id, it will be used to identify the render pipeline when it's added to a Scene
             RenderPipelineId m_nameId;
 
+            // The name of a material pipeline (.materialpipeline file) that this RenderPipeline is associated with.
+            Name m_materialPipelineTagName;
+
             // Whether the pipeline should recreate it's pass tree, for example in the case of pass asset hot reloading.
             bool m_needsPassRecreate = false;
 
@@ -319,11 +321,13 @@ namespace AZ
             // Render settings that can be queried by passes to setup things like render target resolution
             PipelineRenderSettings m_activeRenderSettings;
 
-            // A tag to filter draw items submitted by passes of this render pipeline.
-            // This tag is allocated when it's added to a scene. It's set to invalid when it's removed to the scene.
-            RHI::DrawFilterTag m_drawFilterTag;
+            // Tags to filter draw items submitted by passes of this render pipeline.
+            // These tags are allocated when the pipeline is added to a scene. They are set to invalid when removed from the scene.
+            RHI::DrawFilterTag m_drawFilterTagForPipelineInstanceName;
+            RHI::DrawFilterTag m_drawFilterTagForMaterialPipeline;
+
             // A mask to filter draw items submitted by passes of this render pipeline.
-            // This mask is created from the value of m_drawFilterTag.
+            // This mask is created from the above DrawFilterTag(s).
             RHI::DrawFilterMask m_drawFilterMask = 0;
 
             // The descriptor used to created this render pipeline

+ 5 - 0
Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Scene.h

@@ -186,6 +186,11 @@ namespace AZ
 
             RHI::TagBitRegistry<uint32_t>& GetViewTagBitRegistry();
             
+            RHI::Ptr<RHI::DrawFilterTagRegistry> GetDrawFilterTagRegistry() const
+            {
+                return m_drawFilterTagRegistry;
+            }
+
         protected:
             // SceneFinder overrides...
             void OnSceneNotifictaionHandlerConnected(SceneNotification* handler);

+ 7 - 2
Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialAsset.h

@@ -62,8 +62,13 @@ namespace AZ
             //! Returns the MaterialTypeAsset.
             const Data::Asset<MaterialTypeAsset>& GetMaterialTypeAsset() const;
 
-            //! Returns the collection of all shaders that this material could run.
-            const ShaderCollection& GetShaderCollection() const;
+            //! Return the set of shaders to be run by this material.
+            const MaterialPipelineShaderCollections& GetShaderCollections() const;
+
+            //! Returns the collection of shaders that this material could run for a given pipeline.
+            //! @param forPipeline the name of the material pipeline to query for shaders. For MaterialPipelineNameCommon, 
+            //!        this returns a list of shaders that should be sent to all pipelines.
+            const ShaderCollection& GetShaderCollection(const Name& forPipeline) const;
 
             //! The material may contain any number of MaterialFunctors.
             //! Material functors provide custom logic and calculations to configure shaders, render states, and more.

+ 12 - 4
Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialFunctor.h

@@ -82,6 +82,13 @@ namespace AZ
 
             static void Reflect(ReflectContext* context);
 
+            //! This execution context operates at a high level, and is not specific to a particular material pipeline.
+            //! It can read material property values.
+            //! It can configure the Material ShaderResourceGroup because there is one for the entire material,
+            //! it's not specific to a material pipeline or particular shader.
+            //! It can configure shaders that are not specific to a particular material pipeline (i.e. the MaterialPipelineNameCommon ShaderCollection).
+            //! It can set shader option values (Note this does impact the material-pipeline-specific shaders in order to automatically
+            //! propagate the values to all shaders in the material).
             class RuntimeContext
             {
                 friend class LuaMaterialFunctorRuntimeContext;
@@ -100,14 +107,14 @@ namespace AZ
                 
                 MaterialPropertyPsoHandling GetMaterialPropertyPsoHandling() const { return m_psoHandling; }
 
-                //! Set the value of a shader option in all applicable shaders.
+                //! Set the value of a shader option in all applicable shaders, across all material pipelines.
                 bool SetShaderOptionValue(const Name& optionName, ShaderOptionValue value);
                 bool SetShaderOptionValue(const Name& optionName, const Name& value);
 
                 //! Get the shader resource group for editing.
                 ShaderResourceGroup* GetShaderResourceGroup();
 
-                //! Return how many shaders are in this material.
+                //! Return how many shaders are in the local ShaderCollection.
                 AZStd::size_t GetShaderCount() const;
 
                 //! Enable/disable the specific shader with the index.
@@ -135,7 +142,7 @@ namespace AZ
                 RuntimeContext(
                     const AZStd::vector<MaterialPropertyValue>& propertyValues,
                     RHI::ConstPtr<MaterialPropertiesLayout> materialPropertiesLayout,
-                    ShaderCollection* shaderCollection,
+                    MaterialPipelineShaderCollections* shaderCollections,
                     ShaderResourceGroup* shaderResourceGroup,
                     const MaterialPropertyFlags* materialPropertyDependencies,
                     MaterialPropertyPsoHandling psoHandling
@@ -149,7 +156,8 @@ namespace AZ
 
                 const AZStd::vector<MaterialPropertyValue>& m_materialPropertyValues;
                 RHI::ConstPtr<MaterialPropertiesLayout> m_materialPropertiesLayout;
-                ShaderCollection* m_shaderCollection;
+                ShaderCollection* m_commonShaderCollection;
+                MaterialPipelineShaderCollections* m_allShaderCollections;
                 ShaderResourceGroup* m_shaderResourceGroup;
                 const MaterialPropertyFlags* m_materialPropertyDependencies = nullptr;
                 MaterialPropertyPsoHandling m_psoHandling = MaterialPropertyPsoHandling::Error;

+ 2 - 0
Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h

@@ -50,6 +50,8 @@ namespace AZ
 
             MaterialPropertyOutputType m_type = MaterialPropertyOutputType::Invalid;
 
+            Name m_materialPipelineName;
+
             //! For m_type==ShaderOption,  this is the index of a specific ShaderAsset (see MaterialTypeSourceData's ShaderCollection). 
             //! For m_type==ShaderEnabled, this is the index of a specific ShaderAsset (see MaterialTypeSourceData's ShaderCollection). 
             //! For m_type==ShaderInput,   this field is not used (because there is only one material ShaderResourceGroup in a MaterialAsset).

+ 18 - 15
Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAsset.h

@@ -69,8 +69,13 @@ namespace AZ
 
             virtual ~MaterialTypeAsset();
 
-            //! Returns the collection of all shaders that this material could run.
-            const ShaderCollection& GetShaderCollection() const;
+            //! Return the set of shaders to be run by this material.
+            const MaterialPipelineShaderCollections& GetShaderCollections() const;
+
+            //! Returns the collection of shaders that this material could run for a given pipeline.
+            //! @param forPipeline the name of the material pipeline to query for shaders. For MaterialPipelineNameCommon, 
+            //!        this returns a list of shaders that should be sent to all pipelines.
+            const ShaderCollection& GetShaderCollection(const Name& forPipeline) const;
 
             //! The material may contain any number of MaterialFunctors.
             //! Material functors provide custom logic and calculations to configure shaders, render states, and more.
@@ -85,7 +90,7 @@ namespace AZ
 
             //! Same as above but accepts the supervariant name. There's a minor penalty when using this function
             //! because it will discover the index from the name.  
-            const RHI::Ptr<RHI::ShaderResourceGroupLayout>& GetMaterialSrgLayout(const AZ::Name& supervariantName) const;
+            const RHI::Ptr<RHI::ShaderResourceGroupLayout>& GetMaterialSrgLayout(const Name& supervariantName) const;
 
             //! Just like the original GetMaterialSrgLayout() where it uses the index of the default supervariant.
             //! See the definition of DefaultSupervariantIndex.
@@ -102,7 +107,7 @@ namespace AZ
 
             //! Same as above but accepts the supervariant name. There's a minor penalty when using this function
             //! because it will discover the index from the name.  
-            const RHI::Ptr<RHI::ShaderResourceGroupLayout>& GetObjectSrgLayout(const AZ::Name& supervariantName) const;
+            const RHI::Ptr<RHI::ShaderResourceGroupLayout>& GetObjectSrgLayout(const Name& supervariantName) const;
 
             //! Just like the original GetObjectSrgLayout() where it uses the index of the default supervariant.
             //! See the definition of DefaultSupervariantIndex.
@@ -130,13 +135,11 @@ namespace AZ
 
             //! Possibly renames @propertyId based on the material version update steps.
             //! @return true if the property was renamed
-            bool ApplyPropertyRenames(AZ::Name& propertyId) const;
+            bool ApplyPropertyRenames(Name& propertyId) const;
 
         private:
-            bool PostLoadInit() override;
 
-            const RHI::Ptr<RHI::ShaderResourceGroupLayout>& GetSrgLayout(uint32_t shaderIndex, const SupervariantIndex& supervariantIndex, uint32_t srgBindingSlot) const;
-            const RHI::Ptr<RHI::ShaderResourceGroupLayout>& GetSrgLayout(uint32_t shaderIndex, const AZ::Name& supervariantName, uint32_t srgBindingSlot) const;
+            bool PostLoadInit() override;
 
             //! Called by asset creators to assign the asset to a ready state.
             void SetReady();
@@ -158,18 +161,18 @@ namespace AZ
             //! Defines the topology of user-facing inputs to the material
             Ptr<MaterialPropertiesLayout> m_materialPropertiesLayout;
 
-            //! The set of shaders that will be used for this material
-            ShaderCollection m_shaderCollection;
+            MaterialPipelineShaderCollections m_shaderCollections;
 
             //! Material functors provide custom logic and calculations to configure shaders, render states, and more.
             //! See MaterialFunctor.h for details.
             MaterialFunctorList m_materialFunctors;
 
-            //! Index in @m_shaderCollection of the shader asset that contains the MaterialSrg.
-            uint32_t m_materialSrgShaderIndex = InvalidShaderIndex;
-
-            //! Index in @m_shaderCollection of the shader asset that contains the ObjectSrg.
-            uint32_t m_objectSrgShaderIndex = InvalidShaderIndex;
+            //! These are shaders that hold an example of particular ShaderResourceGroups. Every shader in a material type
+            //! must use the same MaterialSrg and ObjectSrg, so we only need to store one example of each. We keep a reference
+            //! to the shader rather than duplicate the SRG layouts to avoid duplication and also because the ShaderAsset
+            //! is needed to create an instance of the SRG so it's convenient to just keep a reference to the ShaderAsset.
+            Data::Asset<ShaderAsset> m_shaderWithMaterialSrg;
+            Data::Asset<ShaderAsset> m_shaderWithObjectSrg;
 
             //! The version of this MaterialTypeAsset. If the version is greater than 1, actions performed
             //! to update this MaterialTypeAsset will be in m_materialVersionUpdateMap

+ 20 - 14
Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialTypeAssetCreator.h

@@ -32,10 +32,14 @@ namespace AZ
             void Begin(const Data::AssetId& assetId);
 
             //! Adds a shader to the built-in shader collection, which will be run for this material.
-            //! shaderTag must be unique within the material type's list of shaders.
-            void AddShader(const AZ::Data::Asset<ShaderAsset>& shaderAsset, const ShaderVariantId& shaderVariantId = ShaderVariantId{});
-            void AddShader(const AZ::Data::Asset<ShaderAsset>& shaderAsset, const ShaderVariantId& shaderVariantId, const AZ::Name& shaderTag);
-            void AddShader(const AZ::Data::Asset<ShaderAsset>& shaderAsset, const AZ::Name& shaderTag);
+            //! @param shaderTag Must be unique within the material type's list of shaders.
+            //! @param materialPipelineName Identifies a specific material pipeline that this shader is used for.
+            //!                             For MaterialPipelineNameCommon, the shader will be used for all pipelines.
+            void AddShader(
+                const AZ::Data::Asset<ShaderAsset>& shaderAsset,
+                const ShaderVariantId& shaderVariantId = {},
+                const AZ::Name& shaderTag = {},
+                const AZ::Name& materialPipelineName = MaterialPipelineNameCommon);
 
             //! Sets the version of the MaterialTypeAsset
             void SetVersion(uint32_t version);
@@ -81,7 +85,9 @@ namespace AZ
 
             //! Adds a MaterialFunctor.
             //! Material functors provide custom logic and calculations to configure shaders, render states, and more.See MaterialFunctor.h for details.
-            void AddMaterialFunctor(const Ptr<MaterialFunctor>& functor);
+            //! @param materialPipelineName Identifies a specific material pipeline that this functor is used for. For MaterialPipelineNameCommon, 
+            //!                             the functor will be used for the main ShaderCollection that applies to all pipelines.
+            void AddMaterialFunctor(const Ptr<MaterialFunctor>& functor, const AZ::Name& materialPipelineName = MaterialPipelineNameCommon);
 
             //! Adds UV name for a shader input.
             void AddUvName(const RHI::ShaderSemantic& shaderInput, const Name& uvName);
@@ -105,18 +111,18 @@ namespace AZ
             void AddMaterialProperty(MaterialPropertyDescriptor&& materialProperty);
             
             bool PropertyCheck(TypeId typeId, const Name& name);
-                
+
             //! The material type holds references to shader assets that contain SRGs that are supposed to be the same across all passes in the material.
             //! This function searches for an SRG given a @bindingSlot. If a valid one is found it makes sure it is the same across all shaders
-            //! and records in srgShaderIndexToUpdate the index of the ShaderAsset in the ShaderCollection where it was found.
-            //! @srgShaderIndexToUpdate Previously found shader index. If Invalid, this will be filled with the index of the shader Asset
-            //!     where the bindingSlot was found. If not Invalid, this will validate that the same SRG is used by
-            //!                    newShaderAsset.
-            //! @newShaderAssetIndex Corresponding index of @newShaderAsset in m_asset->m_shaderCollection.
+            //! and records it in @srgShaderAssetToUpdate.
+            //! @srgShaderAssetToUpdate Previously found shader asset with the desired SRG. If Invalid, this will be filled with the shader asset
+            //!     where the bindingSlot was found. If not Invalid, this will validate that the same SRG is used by newShaderAsset.
             //! @bindingSlot  The binding slot ID of the SRG to fetch from newShaderAsset.
-            bool UpdateShaderIndexForShaderResourceGroup(
-                uint32_t& srgShaderIndexToUpdate, const Data::Asset<ShaderAsset>& newShaderAsset, const uint32_t newShaderAssetIndex,
-                const uint32_t bindingSlot, const char* srgDebugName);
+            bool UpdateShaderAssetForShaderResourceGroup(
+                Data::Asset<ShaderAsset>& srgShaderAssetToUpdate,
+                const Data::Asset<ShaderAsset>& newShaderAsset,
+                const uint32_t bindingSlot,
+                const char* srgDebugName);
 
             //! Saves the per-material SRG layout in m_shaderResourceGroupLayout for easier access
             void CacheMaterialSrgLayout();

+ 8 - 0
Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/ShaderCollection.h

@@ -123,5 +123,13 @@ namespace AZ
             AZStd::vector<Item> m_shaderItems;
             NameReflectionMapForIndex m_shaderTagIndexMap;
         };
+
+        //! An empty Name for material pipeline means apply to any render pipeline
+        static const Name MaterialPipelineNameCommon = Name{};
+
+        //! There can be a ShaderCollection for each material pipeline, to list shaders that only apply to matching render pipelines.
+        //! The shaders for pipeline name MaterialPipelineNameCommon will be sent to every render pipeline.
+        using MaterialPipelineShaderCollections = AZStd::unordered_map<Name/*pipelineName*/, ShaderCollection>;
+
     } // namespace RPI
 } // namespace AZ

+ 3 - 0
Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/System/RenderPipelineDescriptor.h

@@ -38,6 +38,9 @@ namespace AZ
             //! Render pipelines in the same scene won't have same name.
             AZStd::string m_name;
 
+            //! This will be used in the render pipeline's DrawFilterMask for draw item filtering.
+            AZStd::string m_materialPipelineTag = "MainPipeline";
+
             //! Render settings that can be queried by passes to set things like MSAA on attachments
             PipelineRenderSettings m_renderSettings;
 

+ 31 - 15
Gems/Atom/RPI/Code/Source/RPI.Builders/Material/MaterialTypeBuilder.cpp

@@ -43,7 +43,7 @@ namespace AZ
         {
             AssetBuilderSDK::AssetBuilderDesc materialBuilderDescriptor;
             materialBuilderDescriptor.m_name = "Material Type Builder";
-            materialBuilderDescriptor.m_version = 21; // Material functor simplification
+            materialBuilderDescriptor.m_version = 22; // Material pipeline filters
             materialBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.materialtype", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
             materialBuilderDescriptor.m_busId = azrtti_typeid<MaterialTypeBuilder>();
             materialBuilderDescriptor.m_createJobFunction = AZStd::bind(&MaterialTypeBuilder::CreateJobs, this, AZStd::placeholders::_1, AZStd::placeholders::_2);
@@ -350,7 +350,7 @@ namespace AZ
             MaterialTypeSourceData materialType = materialTypeLoadResult.TakeValue();
 
             // Some shader templates may be reused by multiple pipelines, so first collect a full picture of all the dependencies
-            AZStd::unordered_map<MaterialPipelineSourceData::ShaderTemplate, AZStd::vector<AZStd::string/*materialPipielineName*/>> shaderTemplateReferences;
+            AZStd::unordered_map<MaterialPipelineSourceData::ShaderTemplate, AZStd::vector<Name/*materialPipielineName*/>> shaderTemplateReferences;
             {
                 bool foundProblems = false;
 
@@ -395,7 +395,7 @@ namespace AZ
                         resolveTemplateFilePathReference(materialPipelineFilePath, normalizedShaderTemplate.m_shader);
                         resolveTemplateFilePathReference(materialPipelineFilePath, normalizedShaderTemplate.m_azsli);
 
-                        shaderTemplateReferences[normalizedShaderTemplate].push_back(materialPipelineName.c_str());
+                        shaderTemplateReferences[normalizedShaderTemplate].push_back(Name{materialPipelineName});
                     }
                 }
 
@@ -415,23 +415,25 @@ namespace AZ
             }
             materialType.m_materialShaderCode.clear();
             materialType.m_lightingModel.clear();
-            materialType.m_shaderCollection.clear(); // This should already be clear, but just in case
+            // These should already be clear, but just in case
+            materialType.m_shaderCollection.clear(); 
+            materialType.m_pipelineData.clear();
 
             // Generate the required shaders
             for (const auto& [shaderTemplate, materialPipelineList] : shaderTemplateReferences)
             {
                 AZ_TraceContext("Shader Template", shaderTemplate.m_shader.c_str());
 
-                AZStd::string materialPipelineName;
+                AZStd::string materialPipelineIndicator;
 
                 if (materialPipelineList.size() == 1)
                 {
-                    materialPipelineName = *materialPipelineList.begin();
+                    materialPipelineIndicator = materialPipelineList.begin()->GetCStr();
                 }
                 else if(materialPipelineList.size() > 1)
                 {
                     // Multiple material pipelines reference the same shader, so it should have a generic common name.
-                    materialPipelineName = PipelineNameForCommonShaders;
+                    materialPipelineIndicator = PipelineNameForCommonShaders;
                 }
                 else
                 {
@@ -466,7 +468,7 @@ namespace AZ
                 shaderName = shaderName.ReplaceExtension(""); // This will remove the ".shader" extension
 
                 AZ::IO::Path outputAzslFilePath = request.m_tempDirPath;
-                outputAzslFilePath /= AZStd::string::format("%s_%s_%s.azsl", materialTypeName.c_str(), materialPipelineName.c_str(), shaderName.c_str());
+                outputAzslFilePath /= AZStd::string::format("%s_%s_%s.azsl", materialTypeName.c_str(), materialPipelineIndicator.c_str(), shaderName.c_str());
 
                 if (AZ::Utils::WriteFile(generatedAzsl, outputAzslFilePath.c_str()).IsSuccess())
                 {
@@ -490,7 +492,7 @@ namespace AZ
                 AzFramework::StringFunc::Replace(shaderFile.GetValue(), "INSERT_AZSL_PATH_HERE", azslFileReference.c_str());
 
                 AZ::IO::Path outputShaderFilePath = request.m_tempDirPath;
-                outputShaderFilePath /= AZStd::string::format("%s_%s_%s.shader", materialTypeName.c_str(), materialPipelineName.c_str(), shaderName.c_str());
+                outputShaderFilePath /= AZStd::string::format("%s_%s_%s.shader", materialTypeName.c_str(), materialPipelineIndicator.c_str(), shaderName.c_str());
 
                 if (AZ::Utils::WriteFile(shaderFile.GetValue(), outputShaderFilePath.c_str()).IsSuccess())
                 {
@@ -506,18 +508,32 @@ namespace AZ
                     return;
                 }
 
-                // Add shader to intermediate material type
+                // Add shader to intermediate material type, for each pipeline.
 
-                materialType.m_shaderCollection.push_back({});
-                materialType.m_shaderCollection.back().m_shaderFilePath = AZ::IO::Path{outputShaderFilePath.Filename()}.c_str();
+                for (const Name& materialPipelineName : materialPipelineList)
+                {
+                    MaterialTypeSourceData::MaterialPipelineData& pipelineData = materialType.m_pipelineData[materialPipelineName];
+                    pipelineData.m_shaderCollection.push_back({});
+                    pipelineData.m_shaderCollection.back().m_shaderFilePath = AZ::IO::Path{outputShaderFilePath.Filename()}.c_str();
 
-                // Files in the cache, including intermediate files, end up using lower case for all files and folders. We have to match this
-                // in the output .materialtype file, because the asset system's source dependencies are case-sensitive on some platforms.
-                AZStd::to_lower(materialType.m_shaderCollection.back().m_shaderFilePath.begin(), materialType.m_shaderCollection.back().m_shaderFilePath.end());
+                    // Files in the cache, including intermediate files, end up using lower case for all files and folders. We have to match this
+                    // in the output .materialtype file, because the asset system's source dependencies are case-sensitive on some platforms.
+                    AZStd::to_lower(pipelineData.m_shaderCollection.back().m_shaderFilePath.begin(), pipelineData.m_shaderCollection.back().m_shaderFilePath.end());
+                }
 
                 // TODO(MaterialPipeline): We should warn the user if the shader collection has multiple shaders that use the same draw list.
             }
 
+            // Sort the shader file reference just for convenience, for when the user inspects the intermediate .materialtype file
+            for (auto& pipelineDataPair : materialType.m_pipelineData)
+            {
+                AZStd::sort(pipelineDataPair.second.m_shaderCollection.begin(), pipelineDataPair.second.m_shaderCollection.end(),
+                    [](const MaterialTypeSourceData::ShaderVariantReferenceData& a, const MaterialTypeSourceData::ShaderVariantReferenceData& b)
+                    {
+                        return a.m_shaderFilePath < b.m_shaderFilePath;
+                    });
+            }
+
             AZ::IO::Path outputMaterialTypeFilePath = request.m_tempDirPath;
             // The "_generated" postfix is necessary because AP does not allow intermediate file to have the same relative path as a source file.
             outputMaterialTypeFilePath /= AZStd::string::format("%s_generated.materialtype", materialTypeName.c_str());

+ 104 - 50
Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp

@@ -108,6 +108,12 @@ namespace AZ
                     ->Field("propertyGroups", &PropertyLayout::m_propertyGroups)
                     ;
 
+                serializeContext->Class<MaterialPipelineData>()
+                    ->Version(1)
+                    ->Field("shaders", &MaterialPipelineData::m_shaderCollection)
+                    ->Field("functors", &MaterialPipelineData::m_materialFunctorSourceData)
+                    ;
+
                 serializeContext->RegisterGenericType<VersionUpdates>();
                 serializeContext->RegisterGenericType<UvNameMap>();
 
@@ -121,6 +127,7 @@ namespace AZ
                     ->Field("materialShaderCode", &MaterialTypeSourceData::m_materialShaderCode)
                     ->Field("shaders", &MaterialTypeSourceData::m_shaderCollection)
                     ->Field("functors", &MaterialTypeSourceData::m_materialFunctorSourceData)
+                    ->Field("materialPipelines", &MaterialTypeSourceData::m_pipelineData)
                     ->Field("uvNameMap", &MaterialTypeSourceData::m_uvNameMap)
                     ;
             }
@@ -823,7 +830,7 @@ namespace AZ
 
         MaterialTypeSourceData::Format MaterialTypeSourceData::GetFormat() const
         {
-            if (m_shaderCollection.empty())
+            if (m_shaderCollection.empty() && m_pipelineData.empty())
             {
                 // Whenever there is no explicit shader collection, the material type is considered to be in the abstract format.
                 // Even if materialShaderCode and lightingModel are missing, it should still technically work as an abstract format by using
@@ -833,7 +840,7 @@ namespace AZ
             else if(!m_materialShaderCode.empty() || !m_lightingModel.empty())
             {
                 AZ_Error(MaterialTypeSourceDataDebugName, false,
-                    "Invalid material type format, an explicit shader list cannot be combined with materialShaderCode or lightingModel fields.");
+                    "Invalid material type format, an explicit shader list and pipeline data cannot be combined with materialShaderCode or lightingModel fields.");
                 return Format::Invalid;
             }
             else
@@ -842,32 +849,12 @@ namespace AZ
             }
         }
 
-        Outcome<Data::Asset<MaterialTypeAsset>> MaterialTypeSourceData::CreateMaterialTypeAsset(Data::AssetId assetId, AZStd::string_view materialTypeSourceFilePath, bool elevateWarnings) const
+        bool MaterialTypeSourceData::AddShaders(
+            MaterialTypeAssetCreator& materialTypeAssetCreator,
+            const Name& materialPipelineName,
+            const AZStd::vector<ShaderVariantReferenceData>& shaderCollection,
+            AZStd::string_view materialTypeSourceFilePath) const
         {
-            if (Format::Invalid == GetFormat())
-            {
-                // GetFormat() will report an error message in this case
-                return Failure();
-            }
-            else if (Format::Abstract == GetFormat())
-            {
-                AZ_Assert(false, "This material type is not structured for creating a MaterialTypeAsset. It can only be used to generate an intermediate material type for further processing. See MaterialTypeBuilder.");
-                return Failure();
-            }
-
-            MaterialTypeAssetCreator materialTypeAssetCreator;
-            materialTypeAssetCreator.SetElevateWarnings(elevateWarnings);
-            materialTypeAssetCreator.Begin(assetId);
-
-            if (m_propertyLayout.m_versionOld != 0)
-            {
-                materialTypeAssetCreator.ReportError(
-                    "The field '/propertyLayout/version' is deprecated and moved to '/version'. "
-                    "Please edit this material type source file and move the '\"version\": %u' setting up one level.",
-                    m_propertyLayout.m_versionOld);
-                return Failure();
-            }
-
             // Used to gather all the UV streams used in this material type from its shaders in alphabetical order.
             auto semanticComp = [](const RHI::ShaderSemantic& lhs, const RHI::ShaderSemantic& rhs) -> bool
             {
@@ -875,7 +862,7 @@ namespace AZ
             };
             AZStd::set<RHI::ShaderSemantic, decltype(semanticComp)> uvsInThisMaterialType(semanticComp);
 
-            for (const ShaderVariantReferenceData& shaderRef : m_shaderCollection)
+            for (const ShaderVariantReferenceData& shaderRef : shaderCollection)
             {
                 const auto& shaderFile = shaderRef.m_shaderFilePath;
                 auto shaderAssetResult = AssetUtils::LoadAsset<ShaderAsset>(materialTypeSourceFilePath, shaderFile, 0);
@@ -893,9 +880,7 @@ namespace AZ
                         }
                     }
 
-                    materialTypeAssetCreator.AddShader(
-                        shaderAsset, options.GetShaderVariantId(),
-                        shaderRef.m_shaderTag.IsEmpty() ? AZ::Name(Uuid::CreateRandom().ToFixedString()) : shaderRef.m_shaderTag);
+                    materialTypeAssetCreator.AddShader(shaderAsset, options.GetShaderVariantId(), shaderRef.m_shaderTag, materialPipelineName);
 
                     // Gather UV names
                     const ShaderInputContract& shaderInputContract = shaderAsset->GetInputContract();
@@ -912,25 +897,38 @@ namespace AZ
                 else
                 {
                     materialTypeAssetCreator.ReportError("Shader asset not found for source file '%s'. See above for details.", shaderFile.data());
-                    return Failure();
+                    return false;
                 }
             }
 
-            for (const AZStd::unique_ptr<PropertyGroup>& propertyGroup : m_propertyLayout.m_propertyGroups)
+            // Only add the UV mapping related to this material type.
+            for (const auto& uvInput : uvsInThisMaterialType)
             {
-                bool success = BuildPropertyList(materialTypeSourceFilePath, materialTypeAssetCreator, MaterialNameContext{}, propertyGroup.get());
-
-                if (!success)
+                // We may have cases where the uv map is empty or inconsistent (exported from other projects),
+                // So we use semantic if mapping is not found.
+                auto iter = m_uvNameMap.find(uvInput.ToString());
+                if (iter != m_uvNameMap.end())
                 {
-                    return Failure();
+                    materialTypeAssetCreator.AddUvName(uvInput, Name(iter->second));
+                }
+                else
+                {
+                    materialTypeAssetCreator.AddUvName(uvInput, Name(uvInput.ToString()));
                 }
             }
 
+            return true;
+        }
+
+        bool MaterialTypeSourceData::AddFunctors(
+            MaterialTypeAssetCreator& materialTypeAssetCreator,
+            const Name& materialPipelineName,
+            const AZStd::vector<Ptr<MaterialFunctorSourceDataHolder>>& materialFunctorSourceData,
+            AZStd::string_view materialTypeSourceFilePath) const
+        {
             MaterialNameContext nameContext;
 
-            // We cannot create the MaterialFunctor until after all the properties are added because
-            // CreateFunctor() may need to look up properties in the MaterialPropertiesLayout
-            for (auto& functorData : m_materialFunctorSourceData)
+            for (auto& functorData : materialFunctorSourceData)
             {
                 MaterialFunctorSourceData::FunctorResult result = functorData->CreateFunctor(
                     MaterialFunctorSourceData::RuntimeContext(
@@ -946,7 +944,7 @@ namespace AZ
                     Ptr<MaterialFunctor>& functor = result.GetValue();
                     if (functor != nullptr)
                     {
-                        materialTypeAssetCreator.AddMaterialFunctor(functor);
+                        materialTypeAssetCreator.AddMaterialFunctor(functor, materialPipelineName);
 
                         for (const AZ::Name& optionName : functorData->GetActualSourceData()->GetShaderOptionDependencies())
                         {
@@ -957,23 +955,79 @@ namespace AZ
                 else
                 {
                     materialTypeAssetCreator.ReportError("Failed to create MaterialFunctor");
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        Outcome<Data::Asset<MaterialTypeAsset>> MaterialTypeSourceData::CreateMaterialTypeAsset(Data::AssetId assetId, AZStd::string_view materialTypeSourceFilePath, bool elevateWarnings) const
+        {
+            if (Format::Invalid == GetFormat())
+            {
+                // GetFormat() will report an error message in this case
+                return Failure();
+            }
+            else if (Format::Abstract == GetFormat())
+            {
+                AZ_Assert(false, "This material type is not structured for creating a MaterialTypeAsset. It can only be used to generate an intermediate material type for further processing. See MaterialTypeBuilder.");
+                return Failure();
+            }
+
+            MaterialTypeAssetCreator materialTypeAssetCreator;
+            materialTypeAssetCreator.SetElevateWarnings(elevateWarnings);
+            materialTypeAssetCreator.Begin(assetId);
+
+            if (m_propertyLayout.m_versionOld != 0)
+            {
+                materialTypeAssetCreator.ReportError(
+                    "The field '/propertyLayout/version' is deprecated and moved to '/version'. "
+                    "Please edit this material type source file and move the '\"version\": %u' setting up one level.",
+                    m_propertyLayout.m_versionOld);
+                return Failure();
+            }
+
+            // All the shaders must be added before building the property list, because BuildPropertyList will attempt to connect
+            // properties to shader inputs.
+
+            if (!AddShaders(materialTypeAssetCreator, MaterialPipelineNameCommon, m_shaderCollection, materialTypeSourceFilePath))
+            {
+                return Failure();
+            }
+
+            for (auto& [materialPipelineName, materialPipelineData] : m_pipelineData)
+            {
+                if (!AddShaders(materialTypeAssetCreator, materialPipelineName, materialPipelineData.m_shaderCollection, materialTypeSourceFilePath))
+                {
                     return Failure();
                 }
             }
 
-            // Only add the UV mapping related to this material type.
-            for (const auto& uvInput : uvsInThisMaterialType)
+            // Now that all the shaders are in place, we can add the properties which may reference the shaders
+
+            for (const AZStd::unique_ptr<PropertyGroup>& propertyGroup : m_propertyLayout.m_propertyGroups)
             {
-                // We may have cases where the uv map is empty or inconsistent (exported from other projects),
-                // So we use semantic if mapping is not found.
-                auto iter = m_uvNameMap.find(uvInput.ToString());
-                if (iter != m_uvNameMap.end())
+                bool success = BuildPropertyList(materialTypeSourceFilePath, materialTypeAssetCreator, MaterialNameContext{}, propertyGroup.get());
+
+                if (!success)
                 {
-                    materialTypeAssetCreator.AddUvName(uvInput, Name(iter->second));
+                    return Failure();
                 }
-                else
+            }
+
+            // We cannot create the MaterialFunctor until after all the properties are added because
+            // CreateFunctor() may need to look up properties in the MaterialPropertiesLayout
+            if(!AddFunctors(materialTypeAssetCreator, MaterialPipelineNameCommon, m_materialFunctorSourceData, materialTypeSourceFilePath))
+            {
+                return Failure();
+            }
+
+            for (auto& [materialPipelineName, materialPipelineData] : m_pipelineData)
+            {
+                if (!AddFunctors(materialTypeAssetCreator, materialPipelineName, materialPipelineData.m_materialFunctorSourceData, materialTypeSourceFilePath))
                 {
-                    materialTypeAssetCreator.AddUvName(uvInput, Name(uvInput.ToString()));
+                    return Failure();
                 }
             }
 

+ 43 - 23
Gems/Atom/RPI/Code/Source/RPI.Public/Material/Material.cpp

@@ -86,16 +86,19 @@ namespace AZ
                 return RHI::ResultCode::Fail;
             }
 
-            // Copy the shader collection because the material will make changes, like updating the ShaderVariantId.
-            m_shaderCollection = materialAsset.GetShaderCollection();
+            // Copy the shader collections because the material will make changes, like updating the ShaderVariantId.
+            m_shaderCollections = materialAsset.GetShaderCollections();
 
             // Register for update events related to Shader instances that own the ShaderAssets inside
             // the shader collection.
             ShaderReloadNotificationBus::MultiHandler::BusDisconnect();
-            for (auto& shaderItem : m_shaderCollection)
+            for (const auto& shaderCollectionPair : m_shaderCollections)
             {
-                ShaderReloadDebugTracker::Printf("(Material has ShaderAsset %p)", shaderItem.GetShaderAsset().Get());
-                ShaderReloadNotificationBus::MultiHandler::BusConnect(shaderItem.GetShaderAsset().GetId());
+                for (const auto& shaderItem : shaderCollectionPair.second)
+                {
+                    ShaderReloadDebugTracker::Printf("(Material has ShaderAsset %p)", shaderItem.GetShaderAsset().Get());
+                    ShaderReloadNotificationBus::MultiHandler::BusConnect(shaderItem.GetShaderAsset().GetId());
+                }
             }
 
             // If this Init() is actually a re-initialize, we need to re-apply any overridden property values
@@ -160,9 +163,21 @@ namespace AZ
             ShaderReloadNotificationBus::MultiHandler::BusDisconnect();
         }
 
-        const ShaderCollection& Material::GetShaderCollection() const
+        const MaterialPipelineShaderCollections& Material::GetShaderCollections() const
         {
-            return m_shaderCollection;
+            return m_shaderCollections;
+        }
+
+        const ShaderCollection& Material::GetShaderCollection(const Name& forPipeline) const
+        {
+            auto iter = m_shaderCollections.find(forPipeline);
+            if (iter == m_shaderCollections.end())
+            {
+                static ShaderCollection EmptyShaderCollection;
+                return EmptyShaderCollection;
+            }
+
+            return iter->second;
         }
 
         AZ::Outcome<uint32_t> Material::SetSystemShaderOption(const Name& shaderOptionName, RPI::ShaderOptionValue value)
@@ -171,27 +186,33 @@ namespace AZ
 
             // We won't set any shader options if the shader option is owned by any of the other shaders in this material.
             // If the material uses an option in any shader, then it owns that option for all its shaders.
-            for (auto& shaderItem : m_shaderCollection)
+            for (const auto& shaderCollectionPair : m_shaderCollections)
             {
-                const ShaderOptionGroupLayout* layout = shaderItem.GetShaderOptions()->GetShaderOptionLayout();
-                ShaderOptionIndex index = layout->FindShaderOptionIndex(shaderOptionName);
-                if (index.IsValid())
+                for (const auto& shaderItem : shaderCollectionPair.second)
                 {
-                    if (shaderItem.MaterialOwnsShaderOption(index))
+                    const ShaderOptionGroupLayout* layout = shaderItem.GetShaderOptions()->GetShaderOptionLayout();
+                    ShaderOptionIndex index = layout->FindShaderOptionIndex(shaderOptionName);
+                    if (index.IsValid())
                     {
-                        return AZ::Failure();
+                        if (shaderItem.MaterialOwnsShaderOption(index))
+                        {
+                            return AZ::Failure();
+                        }
                     }
                 }
             }
 
-            for (auto& shaderItem : m_shaderCollection)
+            for (auto& shaderCollectionPair : m_shaderCollections)
             {
-                const ShaderOptionGroupLayout* layout = shaderItem.GetShaderOptions()->GetShaderOptionLayout();
-                ShaderOptionIndex index = layout->FindShaderOptionIndex(shaderOptionName);
-                if (index.IsValid())
+                for (auto& shaderItem : shaderCollectionPair.second)
                 {
-                    shaderItem.GetShaderOptions()->SetValue(index, value);
-                    appliedCount++;
+                    const ShaderOptionGroupLayout* layout = shaderItem.GetShaderOptions()->GetShaderOptionLayout();
+                    ShaderOptionIndex index = layout->FindShaderOptionIndex(shaderOptionName);
+                    if (index.IsValid())
+                    {
+                        shaderItem.GetShaderOptions()->SetValue(index, value);
+                        appliedCount++;
+                    }
                 }
             }
 
@@ -326,13 +347,12 @@ namespace AZ
                             MaterialFunctor::RuntimeContext processContext = MaterialFunctor::RuntimeContext(
                                 m_propertyValues,
                                 m_layout,
-                                &m_shaderCollection,
+                                &m_shaderCollections,
                                 m_shaderResourceGroup.get(),
                                 &materialPropertyDependencies,
                                 psoHandling
                             );
 
-
                             functor->Process(processContext);
                         }
                     }
@@ -548,7 +568,7 @@ namespace AZ
                 }
                 else if (outputId.m_type == MaterialPropertyOutputType::ShaderOption)
                 {
-                    ShaderCollection::Item& shaderReference = m_shaderCollection[outputId.m_containerIndex.GetIndex()];
+                    ShaderCollection::Item& shaderReference = m_shaderCollections[outputId.m_materialPipelineName][outputId.m_containerIndex.GetIndex()];
                     if (!SetShaderOption(*shaderReference.GetShaderOptions(), ShaderOptionIndex{outputId.m_itemIndex.GetIndex()}, value))
                     {
                         return false;
@@ -556,7 +576,7 @@ namespace AZ
                 }
                 else if (outputId.m_type == MaterialPropertyOutputType::ShaderEnabled)
                 {
-                    ShaderCollection::Item& shaderReference = m_shaderCollection[outputId.m_containerIndex.GetIndex()];
+                    ShaderCollection::Item& shaderReference = m_shaderCollections[outputId.m_materialPipelineName][outputId.m_containerIndex.GetIndex()];
                     if (savedPropertyValue.Is<bool>())
                     {
                         shaderReference.SetEnabled(savedPropertyValue.GetValue<bool>());

+ 31 - 15
Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp

@@ -61,16 +61,19 @@ namespace AZ
 
         void MeshDrawPacket::ForValidShaderOptionName(const Name& shaderOptionName, const AZStd::function<bool(const ShaderCollection::Item&, ShaderOptionIndex)>& callback)
         {
-            for (auto& shaderItem : m_material->GetShaderCollection())
+            for (auto shaderCollectionIter : m_material->GetShaderCollections())
             {
-                const ShaderOptionGroupLayout* layout = shaderItem.GetShaderOptions()->GetShaderOptionLayout();
-                ShaderOptionIndex index = layout->FindShaderOptionIndex(shaderOptionName);
-                if (index.IsValid())
+                for (auto& shaderItem : shaderCollectionIter.second)
                 {
-                    bool shouldContinue = callback(shaderItem, index);
-                    if (!shouldContinue)
+                    const ShaderOptionGroupLayout* layout = shaderItem.GetShaderOptions()->GetShaderOptionLayout();
+                    ShaderOptionIndex index = layout->FindShaderOptionIndex(shaderOptionName);
+                    if (index.IsValid())
                     {
-                        return;
+                        bool shouldContinue = callback(shaderItem, index);
+                        if (!shouldContinue)
+                        {
+                            return;
+                        }
                     }
                 }
             }
@@ -199,7 +202,7 @@ namespace AZ
 
             m_perDrawSrgs.clear();
 
-            auto appendShader = [&](const ShaderCollection::Item& shaderItem)
+            auto appendShader = [&](const ShaderCollection::Item& shaderItem, const Name& materialPipelineName)
             {
                 // Skip the shader item without creating the shader instance
                 // if the mesh is not going to be rendered based on the draw tag
@@ -333,10 +336,19 @@ namespace AZ
                     drawRequest.m_uniqueShaderResourceGroup = drawSrg->GetRHIShaderResourceGroup();
                     m_perDrawSrgs.push_back(drawSrg);
                 }
+
+                if (materialPipelineName != MaterialPipelineNameCommon)
+                {
+                    RHI::DrawFilterTag pipelineTag = parentScene.GetDrawFilterTagRegistry()->AcquireTag(materialPipelineName);
+                    AZ_Assert(pipelineTag.IsValid(), "Could not acquire pipeline filter tag '%s'.", materialPipelineName.GetCStr());
+                    drawRequest.m_drawFilterMask = 1 << pipelineTag.GetIndex();
+                }
+
                 drawPacketBuilder.AddDrawItem(drawRequest);
                 
                 ShaderData shaderData;
                 shaderData.m_shader = AZStd::move(shader);
+                shaderData.m_materialPipelineName = materialPipelineName;
                 shaderData.m_shaderTag = shaderItem.GetShaderTag();
                 shaderData.m_requestedShaderVariantId = requestedVariantId;
                 shaderData.m_activeShaderVariantId = variant.GetShaderVariantId();
@@ -348,17 +360,21 @@ namespace AZ
 
             m_material->ApplyGlobalShaderOptions();
 
-            for (auto& shaderItem : m_material->GetShaderCollection())
+            // TODO(MaterialPipeline): We might want to detect duplicate ShaderItem objects here, and merge them to avoid redundant RHI DrawItems.
+            for (auto& [materialPipelineName, shaderCollection] : m_material->GetShaderCollections())
             {
-                if (shaderItem.IsEnabled())
+                for (auto& shaderItem : shaderCollection)
                 {
-                    if (shaderList.size() == RHI::DrawPacketBuilder::DrawItemCountMax)
+                    if (shaderItem.IsEnabled())
                     {
-                        AZ_Error("MeshDrawPacket", false, "Material has more than the limit of %d active shader items.", RHI::DrawPacketBuilder::DrawItemCountMax);
-                        return false;
-                    }
+                        if (shaderList.size() == RHI::DrawPacketBuilder::DrawItemCountMax)
+                        {
+                            AZ_Error("MeshDrawPacket", false, "Material has more than the limit of %d active shader items.", RHI::DrawPacketBuilder::DrawItemCountMax);
+                            return false;
+                        }
 
-                    appendShader(shaderItem);
+                        appendShader(shaderItem, materialPipelineName);
+                    }
                 }
             }
 

+ 22 - 13
Gems/Atom/RPI/Code/Source/RPI.Public/RenderPipeline.cpp

@@ -112,6 +112,7 @@ namespace AZ
             pipeline->m_descriptor = desc;
             pipeline->m_mainViewTag = Name(desc.m_mainViewTagName);
             pipeline->m_nameId = desc.m_name.data();
+            pipeline->m_materialPipelineTagName = Name{desc.m_materialPipelineTag};
             pipeline->m_activeRenderSettings = desc.m_renderSettings;
             pipeline->m_passTree.m_rootPass->SetRenderPipeline(pipeline);
             pipeline->m_passTree.m_rootPass->m_flags.m_isPipelineRoot = true;
@@ -362,7 +363,8 @@ namespace AZ
             m_scene = nullptr;
             PassSystemInterface::Get()->RemoveRenderPipeline(this);
 
-            m_drawFilterTag.Reset();
+            m_drawFilterTagForPipelineInstanceName.Reset();
+            m_drawFilterTagForMaterialPipeline.Reset();
             m_drawFilterMask = 0;
         }
 
@@ -635,29 +637,36 @@ namespace AZ
             return m_renderMode != RenderMode::NoRender;
         }
 
-        RHI::DrawFilterTag RenderPipeline::GetDrawFilterTag() const
-        {
-            return m_drawFilterTag;
-        }
-
         RHI::DrawFilterMask RenderPipeline::GetDrawFilterMask() const
         {
             return m_drawFilterMask;
         }
 
-        void RenderPipeline::SetDrawFilterTag(RHI::DrawFilterTag tag)
-        {
-            m_drawFilterTag = tag;
-            if (m_drawFilterTag.IsValid())
+        void RenderPipeline::SetDrawFilterTags(RHI::DrawFilterTagRegistry* tagRegistry)
+        {   
+            m_drawFilterTagForPipelineInstanceName = tagRegistry->AcquireTag(m_nameId);
+            m_drawFilterTagForMaterialPipeline = tagRegistry->AcquireTag(m_materialPipelineTagName);
+                        
+            m_drawFilterMask = 0;
+            
+            if (m_drawFilterTagForPipelineInstanceName.IsValid())
             {
-                m_drawFilterMask = 1 << tag.GetIndex();
+                m_drawFilterMask |= 1 << m_drawFilterTagForPipelineInstanceName.GetIndex();
             }
-            else
+            if (m_drawFilterTagForMaterialPipeline.IsValid())
             {
-                m_drawFilterMask = 0;
+                m_drawFilterMask |= 1 << m_drawFilterTagForMaterialPipeline.GetIndex();
             }
         }
 
+        void RenderPipeline::ReleaseDrawFilterTags(RHI::DrawFilterTagRegistry* tagRegistry)
+        {
+            tagRegistry->ReleaseTag(m_drawFilterTagForPipelineInstanceName);
+            tagRegistry->ReleaseTag(m_drawFilterTagForMaterialPipeline);
+            m_drawFilterTagForPipelineInstanceName.Reset();
+            m_drawFilterTagForMaterialPipeline.Reset();
+        }
+
         const RenderPipelineDescriptor& RenderPipeline::GetDescriptor() const
         {
             return m_descriptor;

+ 2 - 2
Gems/Atom/RPI/Code/Source/RPI.Public/Scene.cpp

@@ -358,7 +358,7 @@ namespace AZ
                 return;
             }
 
-            pipeline->SetDrawFilterTag(m_drawFilterTagRegistry->AcquireTag(pipelineId));
+            pipeline->SetDrawFilterTags(m_drawFilterTagRegistry.get());
 
             m_pipelines.push_back(pipeline);
 
@@ -397,7 +397,7 @@ namespace AZ
                         m_defaultPipeline = nullptr;
                     }
 
-                    m_drawFilterTagRegistry->ReleaseTag(pipelineToRemove->GetDrawFilterTag());
+                    pipelineToRemove->ReleaseDrawFilterTags(m_drawFilterTagRegistry.get());
 
                     pipelineToRemove->OnRemovedFromScene(this);
                     m_pipelines.erase(it);

+ 4 - 4
Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp

@@ -428,7 +428,7 @@ namespace AZ
         {
             if (index < GetShaderCount())
             {
-                return LuaMaterialFunctorShaderItem{this, &(*m_runtimeContextImpl->m_shaderCollection)[index]};
+                return LuaMaterialFunctorShaderItem{this, &(*m_runtimeContextImpl->m_commonShaderCollection)[index]};
             }
             else
             {
@@ -440,9 +440,9 @@ namespace AZ
         LuaMaterialFunctorShaderItem LuaMaterialFunctorRuntimeContext::GetShaderByTag(const char* shaderTag)
         {
             const AZ::Name tag{shaderTag};
-            if (m_runtimeContextImpl->m_shaderCollection->HasShaderTag(tag))
+            if (m_runtimeContextImpl->m_commonShaderCollection->HasShaderTag(tag))
             {
-                return LuaMaterialFunctorShaderItem{this, &(*m_runtimeContextImpl->m_shaderCollection)[tag]};
+                return LuaMaterialFunctorShaderItem{this, &(*m_runtimeContextImpl->m_commonShaderCollection)[tag]};
             }
             else
             {
@@ -454,7 +454,7 @@ namespace AZ
         
         bool LuaMaterialFunctorRuntimeContext::HasShaderWithTag(const char* shaderTag)
         {
-            return m_runtimeContextImpl->m_shaderCollection->HasShaderTag(AZ::Name{shaderTag});
+            return m_runtimeContextImpl->m_commonShaderCollection->HasShaderTag(AZ::Name{shaderTag});
         }
 
         void LuaMaterialFunctorEditorContext::LuaMaterialFunctorEditorContext::Reflect(BehaviorContext* behaviorContext)

+ 7 - 2
Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialAsset.cpp

@@ -57,9 +57,14 @@ namespace AZ
             return m_materialTypeAsset;
         }
 
-        const ShaderCollection& MaterialAsset::GetShaderCollection() const
+        const MaterialPipelineShaderCollections& MaterialAsset::GetShaderCollections() const
         {
-            return m_materialTypeAsset->GetShaderCollection();
+            return m_materialTypeAsset->GetShaderCollections();
+        }
+
+        const ShaderCollection& MaterialAsset::GetShaderCollection(const Name& forPipeline) const
+        {
+            return m_materialTypeAsset->GetShaderCollection(forPipeline);
         }
 
         const MaterialFunctorList& MaterialAsset::GetMaterialFunctors() const

+ 43 - 29
Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp

@@ -33,46 +33,60 @@ namespace AZ
         MaterialFunctor::RuntimeContext::RuntimeContext(
             const AZStd::vector<MaterialPropertyValue>& propertyValues,
             RHI::ConstPtr<MaterialPropertiesLayout> materialPropertiesLayout,
-            ShaderCollection* shaderCollection,
+            MaterialPipelineShaderCollections* shaderCollections,
             ShaderResourceGroup* shaderResourceGroup,
             const MaterialPropertyFlags* materialPropertyDependencies,
             MaterialPropertyPsoHandling psoHandling
         )
             : m_materialPropertyValues(propertyValues)
             , m_materialPropertiesLayout(materialPropertiesLayout)
-            , m_shaderCollection(shaderCollection)
+            , m_allShaderCollections(shaderCollections)
             , m_shaderResourceGroup(shaderResourceGroup)
             , m_materialPropertyDependencies(materialPropertyDependencies)
             , m_psoHandling(psoHandling)
-        {}
+        {
+            static ShaderCollection EmptyShaderCollection;
 
+            auto iter = m_allShaderCollections->find(MaterialPipelineNameCommon);
+            if (iter != m_allShaderCollections->end())
+            {
+                m_commonShaderCollection = &iter->second;
+            }
+            else
+            {
+                m_commonShaderCollection = &EmptyShaderCollection;
+            }
+        }
 
         template<typename ValueType>
         bool MaterialFunctor::RuntimeContext::SetShaderOptionValueHelper(const Name& name, const ValueType& value)
         {
             bool didSetOne = false;
 
-            for (ShaderCollection::Item& shaderItem : *m_shaderCollection)
+            for (auto& shaderCollectionPair : *m_allShaderCollections)
             {
-                ShaderOptionGroup* shaderOptionGroup = shaderItem.GetShaderOptions();
-                const ShaderOptionGroupLayout* layout = shaderOptionGroup->GetShaderOptionLayout();
-                ShaderOptionIndex optionIndex = layout->FindShaderOptionIndex(name);
-
-                if (!optionIndex.IsValid())
-                {
-                    continue;
-                }
-
-                if (!shaderItem.MaterialOwnsShaderOption(optionIndex))
-                {
-                    AZ_Error("MaterialFunctor", false, "Shader option '%s' is not owned by this material.", name.GetCStr());
-                    AZ_Assert(!didSetOne, "The material build pipeline should have ensured that MaterialOwnsShaderOption is consistent across all shaders.");
-                    return false;
-                }
-
-                if (shaderOptionGroup->SetValue(optionIndex, value))
+                for (ShaderCollection::Item& shaderItem : shaderCollectionPair.second)
                 {
-                    didSetOne = true;
+                    ShaderOptionGroup* shaderOptionGroup = shaderItem.GetShaderOptions();
+                    const ShaderOptionGroupLayout* layout = shaderOptionGroup->GetShaderOptionLayout();
+                    ShaderOptionIndex optionIndex = layout->FindShaderOptionIndex(name);
+
+                    if (!optionIndex.IsValid())
+                    {
+                        continue;
+                    }
+
+                    if (!shaderItem.MaterialOwnsShaderOption(optionIndex))
+                    {
+                        AZ_Error("MaterialFunctor", false, "Shader option '%s' is not owned by this material.", name.GetCStr());
+                        AZ_Assert(!didSetOne, "The material build pipeline should have ensured that MaterialOwnsShaderOption is consistent across all shaders.");
+                        return false;
+                    }
+
+                    if (shaderOptionGroup->SetValue(optionIndex, value))
+                    {
+                        didSetOne = true;
+                    }
                 }
             }
 
@@ -101,37 +115,37 @@ namespace AZ
 
         AZStd::size_t MaterialFunctor::RuntimeContext::GetShaderCount() const
         {
-            return m_shaderCollection->size();
+            return m_commonShaderCollection->size();
         }
 
         void MaterialFunctor::RuntimeContext::SetShaderEnabled(AZStd::size_t shaderIndex, bool enabled)
         {
-            (*m_shaderCollection)[shaderIndex].SetEnabled(enabled);
+            (*m_commonShaderCollection)[shaderIndex].SetEnabled(enabled);
         }
 
         void MaterialFunctor::RuntimeContext::SetShaderEnabled(const AZ::Name& shaderTag, bool enabled)
         {
-            (*m_shaderCollection)[shaderTag].SetEnabled(enabled);
+            (*m_commonShaderCollection)[shaderTag].SetEnabled(enabled);
         }
 
         void MaterialFunctor::RuntimeContext::SetShaderDrawListTagOverride(AZStd::size_t shaderIndex, const Name& drawListTagName)
         {
-            (*m_shaderCollection)[shaderIndex].SetDrawListTagOverride(drawListTagName);
+            (*m_commonShaderCollection)[shaderIndex].SetDrawListTagOverride(drawListTagName);
         }
 
         void MaterialFunctor::RuntimeContext::SetShaderDrawListTagOverride(const AZ::Name& shaderTag, const Name& drawListTagName)
         {
-            (*m_shaderCollection)[shaderTag].SetDrawListTagOverride(drawListTagName);
+            (*m_commonShaderCollection)[shaderTag].SetDrawListTagOverride(drawListTagName);
         }
 
         void MaterialFunctor::RuntimeContext::ApplyShaderRenderStateOverlay(AZStd::size_t shaderIndex, const RHI::RenderStates& renderStatesOverlay)
         {
-            RHI::MergeStateInto(renderStatesOverlay, *((*m_shaderCollection)[shaderIndex].GetRenderStatesOverlay()));
+            RHI::MergeStateInto(renderStatesOverlay, *((*m_commonShaderCollection)[shaderIndex].GetRenderStatesOverlay()));
         }
 
         void MaterialFunctor::RuntimeContext::ApplyShaderRenderStateOverlay(const AZ::Name& shaderTag, const RHI::RenderStates& renderStatesOverlay)
         {
-            RHI::MergeStateInto(renderStatesOverlay, *((*m_shaderCollection)[shaderTag].GetRenderStatesOverlay()));
+            RHI::MergeStateInto(renderStatesOverlay, *((*m_commonShaderCollection)[shaderTag].GetRenderStatesOverlay()));
         }
 
         MaterialFunctor::EditorContext::EditorContext(

+ 2 - 1
Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyDescriptor.cpp

@@ -155,8 +155,9 @@ namespace AZ
             if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
             {
                 serializeContext->Class<MaterialPropertyOutputId>()
-                    ->Version(1)
+                    ->Version(2)
                     ->Field("m_type", &MaterialPropertyOutputId::m_type)
+                    ->Field("m_materialPipelineName", &MaterialPropertyOutputId::m_materialPipelineName)
                     ->Field("m_containerIndex", &MaterialPropertyOutputId::m_containerIndex)
                     ->Field("m_itemIndex", &MaterialPropertyOutputId::m_itemIndex)
                     ;

+ 54 - 42
Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAsset.cpp

@@ -13,6 +13,7 @@
 #include <Atom/RPI.Public/Shader/ShaderReloadDebugTracker.h>
 
 #include <AzCore/Serialization/SerializeContext.h>
+#include <AzCore/Asset/AssetSerializer.h>
 
 namespace AZ
 {
@@ -44,13 +45,13 @@ namespace AZ
                 serializeContext->RegisterGenericType<MaterialUvNameMap>();
 
                 serializeContext->Class<MaterialTypeAsset, AZ::Data::AssetData>()
-                    ->Version(5) // Material version update
+                    ->Version(7) // Material pipeline support
                     ->Field("Version", &MaterialTypeAsset::m_version)
                     ->Field("VersionUpdates", &MaterialTypeAsset::m_materialVersionUpdates)
-                    ->Field("ShaderCollection", &MaterialTypeAsset::m_shaderCollection)
+                    ->Field("ShaderCollections", &MaterialTypeAsset::m_shaderCollections)
                     ->Field("MaterialFunctors", &MaterialTypeAsset::m_materialFunctors)
-                    ->Field("MaterialSrgShaderIndex", &MaterialTypeAsset::m_materialSrgShaderIndex)
-                    ->Field("ObjectSrgShaderIndex", &MaterialTypeAsset::m_objectSrgShaderIndex)
+                    ->Field("ShaderWithMaterialSrg", &MaterialTypeAsset::m_shaderWithMaterialSrg)
+                    ->Field("ShaderWithObjectSrg", &MaterialTypeAsset::m_shaderWithObjectSrg)
                     ->Field("MaterialPropertiesLayout", &MaterialTypeAsset::m_materialPropertiesLayout)
                     ->Field("DefaultPropertyValues", &MaterialTypeAsset::m_propertyValues)
                     ->Field("UvNameMap", &MaterialTypeAsset::m_uvNameMap)
@@ -66,50 +67,49 @@ namespace AZ
             AssetInitBus::Handler::BusDisconnect();
         }
 
-        const ShaderCollection& MaterialTypeAsset::GetShaderCollection() const
+        const MaterialPipelineShaderCollections& MaterialTypeAsset::GetShaderCollections() const
         {
-            return m_shaderCollection;
+            return m_shaderCollections;
         }
 
-        const MaterialFunctorList& MaterialTypeAsset::GetMaterialFunctors() const
+        const ShaderCollection& MaterialTypeAsset::GetShaderCollection(const Name& forPipeline) const
         {
-            return m_materialFunctors;
-        }
+            static const ShaderCollection EmptyCollection;
 
-        const RHI::Ptr<RHI::ShaderResourceGroupLayout>& MaterialTypeAsset::GetSrgLayout(
-            uint32_t shaderIndex, const SupervariantIndex& supervariantIndex, uint32_t srgBindingSlot) const
-        {
-            const bool validShaderIndex = (m_shaderCollection.size() > shaderIndex);
-            if (!validShaderIndex)
+            auto pipelineDataIter = m_shaderCollections.find(forPipeline);
+            if (pipelineDataIter == m_shaderCollections.end())
             {
-                return RHI::NullSrgLayout;
+                return EmptyCollection;
             }
-            const auto& shaderAsset = m_shaderCollection[shaderIndex].GetShaderAsset();
-            return shaderAsset->FindShaderResourceGroupLayout(srgBindingSlot, supervariantIndex);
+
+            return pipelineDataIter->second;
         }
 
-        const RHI::Ptr<RHI::ShaderResourceGroupLayout>& MaterialTypeAsset::GetSrgLayout(
-            uint32_t shaderIndex, const AZ::Name& supervariantName, uint32_t srgBindingSlot) const
+        const MaterialFunctorList& MaterialTypeAsset::GetMaterialFunctors() const
         {
-            const bool validShaderIndex = (m_shaderCollection.size() > shaderIndex);
-            if (!validShaderIndex)
-            {
-                return RHI::NullSrgLayout;
-            }
-            const auto& shaderAsset = m_shaderCollection[shaderIndex].GetShaderAsset();
-            auto supervariantIndex = shaderAsset->GetSupervariantIndex(supervariantName);
-            return shaderAsset->FindShaderResourceGroupLayout(srgBindingSlot, supervariantIndex);
+            return m_materialFunctors;
         }
 
         const RHI::Ptr<RHI::ShaderResourceGroupLayout>& MaterialTypeAsset::GetMaterialSrgLayout(
             const SupervariantIndex& supervariantIndex) const
         {
-            return GetSrgLayout(m_materialSrgShaderIndex, supervariantIndex, RPI::SrgBindingSlot::Material);
+            if (!m_shaderWithMaterialSrg)
+            {
+                return RHI::NullSrgLayout;
+            }
+
+            return m_shaderWithMaterialSrg->FindShaderResourceGroupLayout(RPI::SrgBindingSlot::Material, supervariantIndex);
         }
 
         const RHI::Ptr<RHI::ShaderResourceGroupLayout>& MaterialTypeAsset::GetMaterialSrgLayout(const AZ::Name& supervariantName) const
         {
-            return GetSrgLayout(m_materialSrgShaderIndex, supervariantName, RPI::SrgBindingSlot::Material);
+            if (!m_shaderWithMaterialSrg)
+            {
+                return RHI::NullSrgLayout;
+            }
+
+            auto supervariantIndex = m_shaderWithMaterialSrg->GetSupervariantIndex(supervariantName);
+            return m_shaderWithMaterialSrg->FindShaderResourceGroupLayout(RPI::SrgBindingSlot::Material, supervariantIndex);
         }
 
         const RHI::Ptr<RHI::ShaderResourceGroupLayout>& MaterialTypeAsset::GetMaterialSrgLayout() const
@@ -119,20 +119,29 @@ namespace AZ
 
         const Data::Asset<ShaderAsset>& MaterialTypeAsset::GetShaderAssetForMaterialSrg() const
         {
-            AZ_Assert(m_shaderCollection.size() > m_materialSrgShaderIndex, "shaderIndex %u is invalid because there are only %zu shaders in the collection", m_materialSrgShaderIndex,
-                m_shaderCollection.size());
-            return m_shaderCollection[m_materialSrgShaderIndex].GetShaderAsset();
+            return m_shaderWithMaterialSrg;
         }
 
         const RHI::Ptr<RHI::ShaderResourceGroupLayout>& MaterialTypeAsset::GetObjectSrgLayout(
             const SupervariantIndex& supervariantIndex) const
         {
-            return GetSrgLayout(m_objectSrgShaderIndex, supervariantIndex, RPI::SrgBindingSlot::Object);
+            if (!m_shaderWithObjectSrg)
+            {
+                return RHI::NullSrgLayout;
+            }
+
+            return m_shaderWithObjectSrg->FindShaderResourceGroupLayout(RPI::SrgBindingSlot::Object, supervariantIndex);
         }
 
         const RHI::Ptr<RHI::ShaderResourceGroupLayout>& MaterialTypeAsset::GetObjectSrgLayout(const AZ::Name& supervariantName) const
         {
-            return GetSrgLayout(m_objectSrgShaderIndex, supervariantName, RPI::SrgBindingSlot::Object);
+            if (!m_shaderWithObjectSrg)
+            {
+                return RHI::NullSrgLayout;
+            }
+
+            auto supervariantIndex = m_shaderWithObjectSrg->GetSupervariantIndex(supervariantName);
+            return m_shaderWithObjectSrg->FindShaderResourceGroupLayout(RPI::SrgBindingSlot::Object, supervariantIndex);
         }
 
         const RHI::Ptr<RHI::ShaderResourceGroupLayout>& MaterialTypeAsset::GetObjectSrgLayout() const
@@ -142,10 +151,7 @@ namespace AZ
 
         const Data::Asset<ShaderAsset>& MaterialTypeAsset::GetShaderAssetForObjectSrg() const
         {
-            AZ_Assert(m_shaderCollection.size() > m_objectSrgShaderIndex,
-                "shaderIndex %u is invalid because there are only %zu shaders in the collection", m_objectSrgShaderIndex,
-                m_shaderCollection.size());
-            return m_shaderCollection[m_objectSrgShaderIndex].GetShaderAsset();
+            return m_shaderWithObjectSrg;
         }
 
         const MaterialPropertiesLayout* MaterialTypeAsset::GetMaterialPropertiesLayout() const
@@ -185,9 +191,12 @@ namespace AZ
 
         bool MaterialTypeAsset::PostLoadInit()
         {
-            for (const auto& shaderItem : m_shaderCollection)
+            for (const auto& shaderCollectionPair : m_shaderCollections)
             {
-                Data::AssetBus::MultiHandler::BusConnect(shaderItem.GetShaderAsset().GetId());
+                for (const auto& shaderItem : shaderCollectionPair.second)
+                {
+                    Data::AssetBus::MultiHandler::BusConnect(shaderItem.GetShaderAsset().GetId());
+                }
             }
             AssetInitBus::Handler::BusDisconnect();
 
@@ -208,9 +217,12 @@ namespace AZ
             // The order of asset reloads is non-deterministic. If the MaterialTypeAsset reloads before these
             // dependency assets, this will make sure the MaterialTypeAsset gets the latest ones when they reload.
             // Or in some cases a these assets could get updated and reloaded without reloading the MaterialTypeAsset at all.
-            for (auto& shaderItem : m_shaderCollection)
+            for (auto& shaderCollectionPair : m_shaderCollections)
             {
-                TryReplaceAsset(shaderItem.m_shaderAsset, asset);
+                for (auto& shaderItem : shaderCollectionPair.second)
+                {
+                    TryReplaceAsset(shaderItem.m_shaderAsset, asset);
+                }
             }
         }
 

+ 67 - 53
Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialTypeAssetCreator.cpp

@@ -40,8 +40,11 @@ namespace AZ
             return EndCommon(result);
         }
 
-        bool MaterialTypeAssetCreator::UpdateShaderIndexForShaderResourceGroup(
-            uint32_t& srgShaderIndexToUpdate, const Data::Asset<ShaderAsset>& newShaderAsset, const uint32_t newShaderAssetIndex, const uint32_t bindingSlot, const char* srgDebugName)
+        bool MaterialTypeAssetCreator::UpdateShaderAssetForShaderResourceGroup(
+            Data::Asset<ShaderAsset>& srgShaderAssetToUpdate,
+            const Data::Asset<ShaderAsset>& newShaderAsset,
+            const uint32_t bindingSlot,
+            const char* srgDebugName)
         {
             const auto& newSrgLayout = newShaderAsset->FindShaderResourceGroupLayout(bindingSlot);
 
@@ -51,10 +54,11 @@ namespace AZ
                 return true;
             }
 
-            if (srgShaderIndexToUpdate != MaterialTypeAsset::InvalidShaderIndex)
+            if (srgShaderAssetToUpdate.GetId().IsValid())
             {
-                const auto& currentShaderAsset = m_asset->m_shaderCollection[srgShaderIndexToUpdate].GetShaderAsset();
-                const auto& currentSrgLayout = currentShaderAsset->FindShaderResourceGroupLayout(bindingSlot);
+                AZ_Assert(srgShaderAssetToUpdate.Get(), "srgShaderAssetToUpdate has an AssetId but is not loaded");
+
+                const auto& currentSrgLayout = srgShaderAssetToUpdate->FindShaderResourceGroupLayout(bindingSlot);
                 if (currentSrgLayout->GetHash() != newSrgLayout->GetHash())
                 {
                     ReportError("All shaders in a material must use the same %s ShaderResourceGroup.", srgDebugName);
@@ -63,7 +67,7 @@ namespace AZ
             }
             else
             {
-                srgShaderIndexToUpdate = newShaderAssetIndex;
+                srgShaderAssetToUpdate = newShaderAsset;
             }
 
             return true;
@@ -73,7 +77,7 @@ namespace AZ
         {
             if (!m_materialShaderResourceGroupLayout)
             {
-                if (m_asset->m_materialSrgShaderIndex != MaterialTypeAsset::InvalidShaderIndex)
+                if (m_asset->m_shaderWithMaterialSrg)
                 {
                     // [GFX TODO] At the moment we are using the default supervariant.
                     //            In the future it may be necessary to get the layout
@@ -94,34 +98,31 @@ namespace AZ
                 [this](const char* message){ ReportError("%s", message); });
         }
 
-        void MaterialTypeAssetCreator::AddShader(const AZ::Data::Asset<ShaderAsset>& shaderAsset, const ShaderVariantId& shaderVariantId, const AZ::Name& shaderTag)
+        void MaterialTypeAssetCreator::AddShader(
+            const AZ::Data::Asset<ShaderAsset>& shaderAsset,
+            const ShaderVariantId& shaderVariantId,
+            const AZ::Name& shaderTag,
+            const AZ::Name& materialPipelineName)
         {
             if (ValidateIsReady() && ValidateNotNull(shaderAsset, "ShaderAsset"))
             {
-                m_asset->m_shaderCollection.m_shaderItems.push_back(ShaderCollection::Item{shaderAsset, shaderTag, shaderVariantId});
-                if (!m_asset->m_shaderCollection.m_shaderTagIndexMap.Insert(shaderTag, RHI::Handle<uint32_t>(m_asset->m_shaderCollection.m_shaderItems.size() - 1)))
+                ShaderCollection& shaderCollection = m_asset->m_shaderCollections[materialPipelineName];
+
+                AZ::Name finalShaderTag = !shaderTag.IsEmpty() ? shaderTag : AZ::Name{AZ::Uuid::CreateRandom().ToFixedString()};
+
+                shaderCollection.m_shaderItems.push_back(ShaderCollection::Item{shaderAsset, finalShaderTag, shaderVariantId});
+                if (!shaderCollection.m_shaderTagIndexMap.Insert(finalShaderTag, RHI::Handle<uint32_t>(shaderCollection.m_shaderItems.size() - 1)))
                 {
-                    ReportError(AZStd::string::format("Failed to insert shader tag '%s'. Shader tag must be unique.", shaderTag.GetCStr()).c_str());
+                    ReportError("Failed to insert shader tag '%s' for pipeline '%s'. Shader tag must be unique.", finalShaderTag.GetCStr(), materialPipelineName.GetCStr());
                 }
 
-                uint32_t newShaderIndex = aznumeric_caster(m_asset->m_shaderCollection.m_shaderItems.size() - 1);
-                UpdateShaderIndexForShaderResourceGroup(m_asset->m_materialSrgShaderIndex, shaderAsset, newShaderIndex, SrgBindingSlot::Material, "material");
-                UpdateShaderIndexForShaderResourceGroup(m_asset->m_objectSrgShaderIndex, shaderAsset, newShaderIndex, SrgBindingSlot::Object, "object");
+                UpdateShaderAssetForShaderResourceGroup(m_asset->m_shaderWithMaterialSrg, shaderAsset, SrgBindingSlot::Material, "material");
+                UpdateShaderAssetForShaderResourceGroup(m_asset->m_shaderWithObjectSrg, shaderAsset, SrgBindingSlot::Object, "object");
 
                 CacheMaterialSrgLayout();
             }
         }
 
-        void MaterialTypeAssetCreator::AddShader(const AZ::Data::Asset<ShaderAsset>& shaderAsset, const ShaderVariantId& shaderVariantId)
-        {
-            AddShader(shaderAsset, shaderVariantId, AZ::Name(AZ::Uuid::CreateRandom().ToFixedString()));
-        }
-
-        void MaterialTypeAssetCreator::AddShader(const AZ::Data::Asset<ShaderAsset>& shaderAsset, const AZ::Name& shaderTag)
-        {
-            AddShader(shaderAsset, ShaderVariantId{}, shaderTag);
-        }
-
         void MaterialTypeAssetCreator::SetVersion(uint32_t version)
         {
             m_asset->m_version = version;
@@ -136,13 +137,16 @@ namespace AZ
         {
             bool optionFound = false;
 
-            for (ShaderCollection::Item& shaderItem : m_asset->m_shaderCollection)
+            for (auto& shaderCollectionPair : m_asset->m_shaderCollections)
             {
-                ShaderOptionIndex index = shaderItem.GetShaderOptions()->FindShaderOptionIndex(shaderOptionName);
-                if (index.IsValid())
+                for (ShaderCollection::Item& shaderItem : shaderCollectionPair.second)
                 {
-                    shaderItem.m_ownedShaderOptionIndices.insert(index);
-                    optionFound = true;
+                    ShaderOptionIndex index = shaderItem.GetShaderOptions()->FindShaderOptionIndex(shaderOptionName);
+                    if (index.IsValid())
+                    {
+                        shaderItem.m_ownedShaderOptionIndices.insert(index);
+                        optionFound = true;
+                    }
                 }
             }
 
@@ -351,23 +355,27 @@ namespace AZ
             }
 
             bool foundShaderOptions = false;
-            for (int shaderIndex = 0; shaderIndex < m_asset->m_shaderCollection.size(); ++shaderIndex)
+            for (auto& [materialPipelineName, shaderCollection] : m_asset->m_shaderCollections)
             {
-                ShaderCollection::Item& shaderItem = m_asset->m_shaderCollection[shaderIndex];
-                auto optionsLayout = shaderItem.GetShaderAsset()->GetShaderOptionGroupLayout();
-                ShaderOptionIndex optionIndex = optionsLayout->FindShaderOptionIndex(shaderOptionName);
-                if (optionIndex.IsValid())
+                for (int shaderIndex = 0; shaderIndex < shaderCollection.size(); ++shaderIndex)
                 {
-                    foundShaderOptions = true;
+                    ShaderCollection::Item& shaderItem = shaderCollection[shaderIndex];
+                    auto optionsLayout = shaderItem.GetShaderAsset()->GetShaderOptionGroupLayout();
+                    ShaderOptionIndex optionIndex = optionsLayout->FindShaderOptionIndex(shaderOptionName);
+                    if (optionIndex.IsValid())
+                    {
+                        foundShaderOptions = true;
 
-                    MaterialPropertyOutputId outputId;
-                    outputId.m_type = MaterialPropertyOutputType::ShaderOption;
-                    outputId.m_containerIndex = RHI::Handle<uint32_t>{shaderIndex};
-                    outputId.m_itemIndex = RHI::Handle<uint32_t>{optionIndex.GetIndex()};
+                        MaterialPropertyOutputId outputId;
+                        outputId.m_type = MaterialPropertyOutputType::ShaderOption;
+                        outputId.m_materialPipelineName = materialPipelineName;
+                        outputId.m_containerIndex = RHI::Handle<uint32_t>{shaderIndex};
+                        outputId.m_itemIndex = RHI::Handle<uint32_t>{optionIndex.GetIndex()};
 
-                    m_wipMaterialProperty.m_outputConnections.push_back(outputId);
+                        m_wipMaterialProperty.m_outputConnections.push_back(outputId);
 
-                    shaderItem.m_ownedShaderOptionIndices.insert(optionIndex);
+                        shaderItem.m_ownedShaderOptionIndices.insert(optionIndex);
+                    }
                 }
             }
 
@@ -392,21 +400,25 @@ namespace AZ
             }
 
             bool foundShader = false;
-            for (int i = 0; i < m_asset->m_shaderCollection.size() && GetErrorCount() == 0; ++i)
+            for (auto& [materialPipelineName, shaderCollection] : m_asset->m_shaderCollections)
             {
-                ShaderCollection::Item& shaderItem = m_asset->m_shaderCollection[i];
-
-                if (shaderItem.GetShaderTag() == shaderTag)
+                for (int shaderIndex = 0; shaderIndex < shaderCollection.size(); ++shaderIndex)
                 {
-                    foundShader = true;
+                    ShaderCollection::Item& shaderItem = shaderCollection[shaderIndex];
 
-                    MaterialPropertyOutputId outputId;
-                    outputId.m_type = MaterialPropertyOutputType::ShaderEnabled;
-                    outputId.m_containerIndex = RHI::Handle<uint32_t>{i};
+                    if (shaderItem.GetShaderTag() == shaderTag)
+                    {
+                        foundShader = true;
 
-                    m_wipMaterialProperty.m_outputConnections.push_back(outputId);
+                        MaterialPropertyOutputId outputId;
+                        outputId.m_materialPipelineName = materialPipelineName;
+                        outputId.m_type = MaterialPropertyOutputType::ShaderEnabled;
+                        outputId.m_containerIndex = RHI::Handle<uint32_t>{shaderIndex};
 
-                    break;
+                        m_wipMaterialProperty.m_outputConnections.push_back(outputId);
+
+                        break;
+                    }
                 }
             }
 
@@ -498,11 +510,13 @@ namespace AZ
             SetPropertyValue(name, Data::Asset<ImageAsset>(imageAsset));
         }
 
-        void MaterialTypeAssetCreator::AddMaterialFunctor(const Ptr<MaterialFunctor>& functor)
+        void MaterialTypeAssetCreator::AddMaterialFunctor(const Ptr<MaterialFunctor>& functor, const AZ::Name& /*materialPipelineName*/)
         {
             if (ValidateIsReady() && ValidateNotNull(functor, "MaterialFunctor"))
             {
                 m_asset->m_materialFunctors.emplace_back(functor);
+                //TODO(MaterialPipeline): Add support for per-pipeline material functors
+                //m_asset->m_materialFunctors[materialPipelineName].emplace_back(functor);
             }
         }
 
@@ -520,7 +534,7 @@ namespace AZ
                 {
                     m_asset->m_uvNameMap.push_back(UvNamePair(shaderInput, uvName));
                 }
-                else
+                else if (iter->m_uvName != uvName)
                 {
                     ReportError("Multiple UV names are defined for shader input %s.", shaderInput.ToString().c_str());
                 }

+ 1 - 0
Gems/Atom/RPI/Code/Source/RPI.Reflect/System/RenderPipelineDescriptor.cpp

@@ -23,6 +23,7 @@ namespace AZ
                     ->Field("Name", &RenderPipelineDescriptor::m_name)
                     ->Field("MainViewTag", &RenderPipelineDescriptor::m_mainViewTagName)
                     ->Field("RootPassTemplate", &RenderPipelineDescriptor::m_rootPassTemplate)
+                    ->Field("MaterialPipelineTag", &RenderPipelineDescriptor::m_materialPipelineTag)
                     ->Field("ExecuteOnce", &RenderPipelineDescriptor::m_executeOnce)
                     ->Field("RenderSettings", &RenderPipelineDescriptor::m_renderSettings)
                     ->Field("AllowModification", &RenderPipelineDescriptor::m_allowModification)

+ 32 - 30
Gems/Atom/RPI/Code/Tests/Material/LuaMaterialFunctorTests.cpp

@@ -97,7 +97,7 @@ namespace UnitTest
         {
             MaterialTypeAssetCreator materialTypeCreator;
             materialTypeCreator.Begin(Uuid::CreateRandom());
-            materialTypeCreator.AddShader(CreateTestShaderAsset(Uuid::CreateRandom()), Name{"TestShader"});
+            materialTypeCreator.AddShader(CreateTestShaderAsset(Uuid::CreateRandom()), AZ::RPI::ShaderVariantId{}, Name{"TestShader"});
             materialTypeCreator.BeginMaterialProperty(Name{materialPropertyName}, dataType);
             materialTypeCreator.EndMaterialProperty();
             LuaMaterialFunctorTests::AddLuaFunctor(materialTypeCreator, luaFunctorScript);
@@ -150,7 +150,7 @@ namespace UnitTest
         {
             MaterialTypeAssetCreator materialTypeCreator;
             materialTypeCreator.Begin(Uuid::CreateRandom());
-            materialTypeCreator.AddShader(CreateTestShaderAsset(Uuid::CreateRandom(), {}, shaderOptionsLayout), Name{"TestShader"});
+            materialTypeCreator.AddShader(CreateTestShaderAsset(Uuid::CreateRandom(), {}, shaderOptionsLayout), AZ::RPI::ShaderVariantId{}, Name{"TestShader"});
             materialTypeCreator.BeginMaterialProperty(Name{materialPropertyName}, dataType);
             materialTypeCreator.EndMaterialProperty();
             LuaMaterialFunctorTests::AddLuaFunctor(materialTypeCreator, luaFunctorScript);
@@ -534,11 +534,11 @@ namespace UnitTest
 
         testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true});
         EXPECT_TRUE(testData.GetMaterial()->Compile());
-        EXPECT_EQ(1, testData.GetMaterial()->GetShaderCollection()[0].GetShaderOptions()->GetValue(Name{"o_bool"}).GetIndex());
+        EXPECT_EQ(1, testData.GetMaterial()->GetShaderCollection(MaterialPipelineNameCommon)[0].GetShaderOptions()->GetValue(Name{"o_bool"}).GetIndex());
 
         testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{false});
         EXPECT_TRUE(testData.GetMaterial()->Compile());
-        EXPECT_EQ(0, testData.GetMaterial()->GetShaderCollection()[0].GetShaderOptions()->GetValue(Name{"o_bool"}).GetIndex());
+        EXPECT_EQ(0, testData.GetMaterial()->GetShaderCollection(MaterialPipelineNameCommon)[0].GetShaderOptions()->GetValue(Name{"o_bool"}).GetIndex());
     }
 
     TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_SetShaderOption_UInt)
@@ -568,7 +568,7 @@ namespace UnitTest
 
         testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{6});
         EXPECT_TRUE(testData.GetMaterial()->Compile());
-        EXPECT_EQ(12, testData.GetMaterial()->GetShaderCollection()[0].GetShaderOptions()->GetValue(Name{"o_uint"}).GetIndex());
+        EXPECT_EQ(12, testData.GetMaterial()->GetShaderCollection(MaterialPipelineNameCommon)[0].GetShaderOptions()->GetValue(Name{"o_uint"}).GetIndex());
     }
 
     TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_SetShaderOption_Enum)
@@ -602,11 +602,11 @@ namespace UnitTest
 
         testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true});
         EXPECT_TRUE(testData.GetMaterial()->Compile());
-        EXPECT_EQ(2, testData.GetMaterial()->GetShaderCollection()[0].GetShaderOptions()->GetValue(Name{"o_enum"}).GetIndex());
+        EXPECT_EQ(2, testData.GetMaterial()->GetShaderCollection(MaterialPipelineNameCommon)[0].GetShaderOptions()->GetValue(Name{"o_enum"}).GetIndex());
 
         testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{false});
         EXPECT_TRUE(testData.GetMaterial()->Compile());
-        EXPECT_EQ(1, testData.GetMaterial()->GetShaderCollection()[0].GetShaderOptions()->GetValue(Name{"o_enum"}).GetIndex());
+        EXPECT_EQ(1, testData.GetMaterial()->GetShaderCollection(MaterialPipelineNameCommon)[0].GetShaderOptions()->GetValue(Name{"o_enum"}).GetIndex());
     }
 
     TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_ShaderItem_SetShaderOption_Bool)
@@ -637,12 +637,12 @@ namespace UnitTest
         testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true});
         EXPECT_TRUE(testData.GetMaterial()->Compile());
         EXPECT_EQ(
-            1, testData.GetMaterial()->GetShaderCollection()[Name{"TestShader"}].GetShaderOptions()->GetValue(Name{"o_bool"}).GetIndex());
+            1, testData.GetMaterial()->GetShaderCollection(MaterialPipelineNameCommon)[Name{"TestShader"}].GetShaderOptions()->GetValue(Name{"o_bool"}).GetIndex());
 
         testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{false});
         EXPECT_TRUE(testData.GetMaterial()->Compile());
         EXPECT_EQ(
-            0, testData.GetMaterial()->GetShaderCollection()[Name{"TestShader"}].GetShaderOptions()->GetValue(Name{"o_bool"}).GetIndex());
+            0, testData.GetMaterial()->GetShaderCollection(MaterialPipelineNameCommon)[Name{"TestShader"}].GetShaderOptions()->GetValue(Name{"o_bool"}).GetIndex());
     }
 
     TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_ShaderItem_SetShaderOption_UInt)
@@ -673,7 +673,7 @@ namespace UnitTest
         testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{6});
         EXPECT_TRUE(testData.GetMaterial()->Compile());
         EXPECT_EQ(
-            12, testData.GetMaterial()->GetShaderCollection()[Name{"TestShader"}].GetShaderOptions()->GetValue(Name{"o_uint"}).GetIndex());
+            12, testData.GetMaterial()->GetShaderCollection(MaterialPipelineNameCommon)[Name{"TestShader"}].GetShaderOptions()->GetValue(Name{"o_uint"}).GetIndex());
     }
 
     TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_ShaderItem_SetShaderOption_Enum)
@@ -708,12 +708,12 @@ namespace UnitTest
         testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true});
         EXPECT_TRUE(testData.GetMaterial()->Compile());
         EXPECT_EQ(
-            2, testData.GetMaterial()->GetShaderCollection()[Name{"TestShader"}].GetShaderOptions()->GetValue(Name{"o_enum"}).GetIndex());
+            2, testData.GetMaterial()->GetShaderCollection(MaterialPipelineNameCommon)[Name{"TestShader"}].GetShaderOptions()->GetValue(Name{"o_enum"}).GetIndex());
 
         testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{false});
         EXPECT_TRUE(testData.GetMaterial()->Compile());
         EXPECT_EQ(
-            1, testData.GetMaterial()->GetShaderCollection()[Name{"TestShader"}].GetShaderOptions()->GetValue(Name{"o_enum"}).GetIndex());
+            1, testData.GetMaterial()->GetShaderCollection(MaterialPipelineNameCommon)[Name{"TestShader"}].GetShaderOptions()->GetValue(Name{"o_enum"}).GetIndex());
     }
 
     TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_EditorContext_SetMaterialPropertyVisibility)
@@ -952,26 +952,28 @@ namespace UnitTest
         testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true});
         EXPECT_TRUE(testData.GetMaterial()->Compile());
 
-        EXPECT_EQ(1, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositionsCount);
-        EXPECT_EQ(2, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositions[0].m_x);
-        EXPECT_EQ(4, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositions[0].m_y);
-        EXPECT_EQ(RHI::CullMode::None, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_rasterState.m_cullMode);
-        EXPECT_EQ(1, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_blendState.m_targets[1].m_enable);
-        EXPECT_EQ(-1, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_rasterState.m_depthBias);
-        EXPECT_FLOAT_EQ(0.2, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_rasterState.m_depthBiasClamp);
-        EXPECT_EQ(0xF0, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_depthStencilState.m_stencil.m_writeMask);
+        const ShaderCollection& shaderCollection = testData.GetMaterial()->GetShaderCollection(MaterialPipelineNameCommon);
+
+        EXPECT_EQ(1, shaderCollection[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositionsCount);
+        EXPECT_EQ(2, shaderCollection[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositions[0].m_x);
+        EXPECT_EQ(4, shaderCollection[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositions[0].m_y);
+        EXPECT_EQ(RHI::CullMode::None, shaderCollection[0].GetRenderStatesOverlay()->m_rasterState.m_cullMode);
+        EXPECT_EQ(1, shaderCollection[0].GetRenderStatesOverlay()->m_blendState.m_targets[1].m_enable);
+        EXPECT_EQ(-1, shaderCollection[0].GetRenderStatesOverlay()->m_rasterState.m_depthBias);
+        EXPECT_FLOAT_EQ(0.2, shaderCollection[0].GetRenderStatesOverlay()->m_rasterState.m_depthBiasClamp);
+        EXPECT_EQ(0xF0, shaderCollection[0].GetRenderStatesOverlay()->m_depthStencilState.m_stencil.m_writeMask);
 
         testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{false});
         EXPECT_TRUE(testData.GetMaterial()->Compile());
 
-        EXPECT_EQ(RHI::RenderStates_InvalidUInt, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositionsCount);
-        EXPECT_EQ(RHI::Limits::Pipeline::MultiSampleCustomLocationGridSize, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositions[0].m_x);
-        EXPECT_EQ(RHI::Limits::Pipeline::MultiSampleCustomLocationGridSize, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositions[0].m_y);
-        EXPECT_EQ(RHI::CullMode::Invalid, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_rasterState.m_cullMode);
-        EXPECT_EQ(RHI::RenderStates_InvalidBool, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_blendState.m_targets[1].m_enable);
-        EXPECT_EQ(RHI::RenderStates_InvalidInt, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_rasterState.m_depthBias);
-        EXPECT_FLOAT_EQ(RHI::RenderStates_InvalidFloat, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_rasterState.m_depthBiasClamp);
-        EXPECT_EQ(RHI::RenderStates_InvalidUInt, testData.GetMaterial()->GetShaderCollection()[0].GetRenderStatesOverlay()->m_depthStencilState.m_stencil.m_writeMask);
+        EXPECT_EQ(RHI::RenderStates_InvalidUInt, shaderCollection[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositionsCount);
+        EXPECT_EQ(RHI::Limits::Pipeline::MultiSampleCustomLocationGridSize, shaderCollection[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositions[0].m_x);
+        EXPECT_EQ(RHI::Limits::Pipeline::MultiSampleCustomLocationGridSize, shaderCollection[0].GetRenderStatesOverlay()->m_multisampleState.m_customPositions[0].m_y);
+        EXPECT_EQ(RHI::CullMode::Invalid, shaderCollection[0].GetRenderStatesOverlay()->m_rasterState.m_cullMode);
+        EXPECT_EQ(RHI::RenderStates_InvalidBool, shaderCollection[0].GetRenderStatesOverlay()->m_blendState.m_targets[1].m_enable);
+        EXPECT_EQ(RHI::RenderStates_InvalidInt, shaderCollection[0].GetRenderStatesOverlay()->m_rasterState.m_depthBias);
+        EXPECT_FLOAT_EQ(RHI::RenderStates_InvalidFloat, shaderCollection[0].GetRenderStatesOverlay()->m_rasterState.m_depthBiasClamp);
+        EXPECT_EQ(RHI::RenderStates_InvalidUInt, shaderCollection[0].GetRenderStatesOverlay()->m_depthStencilState.m_stencil.m_writeMask);
     }
 
     TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_SetShaderEnabledByTag)
@@ -996,7 +998,7 @@ namespace UnitTest
         testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{true});
         EXPECT_TRUE(testData.GetMaterial()->Compile());
 
-        EXPECT_EQ(true, testData.GetMaterial()->GetShaderCollection()[Name{"TestShader"}].IsEnabled());
+        EXPECT_EQ(true, testData.GetMaterial()->GetShaderCollection(MaterialPipelineNameCommon)[Name{"TestShader"}].IsEnabled());
     }
 
     TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_SetShaderDrawListTagOverride)
@@ -1024,7 +1026,7 @@ namespace UnitTest
         EXPECT_TRUE(testData.GetMaterial()->Compile());
 
         RHI::DrawListTag tag = drawListTagRegistry->FindTag(Name{"TestDrawListTag"});
-        EXPECT_EQ(tag, testData.GetMaterial()->GetShaderCollection()[Name{"TestShader"}].GetDrawListTagOverride());
+        EXPECT_EQ(tag, testData.GetMaterial()->GetShaderCollection(MaterialPipelineNameCommon)[Name{"TestShader"}].GetDrawListTagOverride());
 
         drawListTagRegistry->ReleaseTag(tag);
     }

+ 12 - 10
Gems/Atom/RPI/Code/Tests/Material/MaterialFunctorTests.cpp

@@ -156,7 +156,9 @@ namespace UnitTest
         // Most of this data can be empty since this particular functor doesn't access it.
         AZStd::vector<MaterialPropertyValue> unusedPropertyValues;
         ShaderResourceGroup* unusedSrg = nullptr;
-        ShaderCollection shaderCollectionCopy = materialTypeAsset->GetShaderCollection();
+
+
+        MaterialPipelineShaderCollections shaderCollectionCopy = materialTypeAsset->GetShaderCollections();
 
         {
             // Successfully set o_optionA
@@ -170,9 +172,9 @@ namespace UnitTest
             };
             testFunctorSetOptionA.Process(runtimeContext);
             EXPECT_TRUE(testFunctorSetOptionA.GetProcessResult());
-            EXPECT_EQ(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 0 }).GetIndex());
-            EXPECT_NE(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 1 }).GetIndex());
-            EXPECT_NE(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 2 }).GetIndex());
+            EXPECT_EQ(1, shaderCollectionCopy[MaterialPipelineNameCommon][0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 0 }).GetIndex());
+            EXPECT_NE(1, shaderCollectionCopy[MaterialPipelineNameCommon][0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 1 }).GetIndex());
+            EXPECT_NE(1, shaderCollectionCopy[MaterialPipelineNameCommon][0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 2 }).GetIndex());
         }
 
         {
@@ -187,9 +189,9 @@ namespace UnitTest
             };
             testFunctorSetOptionB.Process(runtimeContext);
             EXPECT_TRUE(testFunctorSetOptionB.GetProcessResult());
-            EXPECT_EQ(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 0 }).GetIndex());
-            EXPECT_EQ(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 1 }).GetIndex());
-            EXPECT_NE(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 2 }).GetIndex());
+            EXPECT_EQ(1, shaderCollectionCopy[MaterialPipelineNameCommon][0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 0 }).GetIndex());
+            EXPECT_EQ(1, shaderCollectionCopy[MaterialPipelineNameCommon][0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 1 }).GetIndex());
+            EXPECT_NE(1, shaderCollectionCopy[MaterialPipelineNameCommon][0].GetShaderOptions()->GetValue(ShaderOptionIndex{ 2 }).GetIndex());
         }
 
         {
@@ -224,9 +226,9 @@ namespace UnitTest
             AZ_TEST_STOP_TRACE_SUPPRESSION(1);
         }
 
-        EXPECT_EQ(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{0}).GetIndex());
-        EXPECT_EQ(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{1}).GetIndex());
-        EXPECT_NE(1, shaderCollectionCopy[0].GetShaderOptions()->GetValue(ShaderOptionIndex{2}).GetIndex());
+        EXPECT_EQ(1, shaderCollectionCopy[MaterialPipelineNameCommon][0].GetShaderOptions()->GetValue(ShaderOptionIndex{0}).GetIndex());
+        EXPECT_EQ(1, shaderCollectionCopy[MaterialPipelineNameCommon][0].GetShaderOptions()->GetValue(ShaderOptionIndex{1}).GetIndex());
+        EXPECT_NE(1, shaderCollectionCopy[MaterialPipelineNameCommon][0].GetShaderOptions()->GetValue(ShaderOptionIndex{2}).GetIndex());
     }
 
     TEST_F(MaterialFunctorTests, ReprocessTest)

+ 26 - 22
Gems/Atom/RPI/Code/Tests/Material/MaterialTests.cpp

@@ -484,7 +484,7 @@ namespace UnitTest
         EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{"RangeB"})), 10u);
 
         // Check the values on the underlying ShaderCollection::Item
-        ShaderOptionGroup options{optionsLayout, material->GetShaderCollection()[0].GetShaderVariantId()};
+        ShaderOptionGroup options{optionsLayout, material->GetShaderCollection(MaterialPipelineNameCommon)[0].GetShaderVariantId()};
         EXPECT_EQ(optionEnumA.Get(options).GetIndex(), optionEnumA.FindValue(Name{"High"}).GetIndex());
         EXPECT_EQ(optionEnumB.Get(options).GetIndex(), optionEnumB.FindValue(Name{"Med"}).GetIndex());
         EXPECT_EQ(optionBoolA.Get(options).GetIndex(), optionBoolA.FindValue(Name{"True"}).GetIndex());
@@ -506,7 +506,7 @@ namespace UnitTest
         EXPECT_EQ(material->GetPropertyValue<uint32_t>(material->FindPropertyIndex(Name{"RangeB"})), 7u);
 
         // Check the values on the underlying ShaderCollection::Item
-        ShaderOptionGroup options2{optionsLayout, material->GetShaderCollection()[0].GetShaderVariantId()};
+        ShaderOptionGroup options2{optionsLayout, material->GetShaderCollection(MaterialPipelineNameCommon)[0].GetShaderVariantId()};
         EXPECT_EQ(optionEnumA.Get(options2).GetIndex(), optionEnumA.FindValue(Name{"Med"}).GetIndex());
         EXPECT_EQ(optionEnumB.Get(options2).GetIndex(), optionEnumB.FindValue(Name{"Low"}).GetIndex());
         EXPECT_EQ(optionBoolA.Get(options2).GetIndex(), optionBoolA.FindValue(Name{"False"}).GetIndex());
@@ -541,11 +541,13 @@ namespace UnitTest
 
         auto& optionRangeB = optionsLayout->GetShaderOption(optionsLayout->FindShaderOptionIndex(Name{"o_rangeB"}));
 
+        const ShaderCollection& shaderCollection = material->GetShaderCollection(MaterialPipelineNameCommon);
+
         // Check the values on the underlying ShaderVariantReferences
         {
-            ShaderOptionGroup options0{optionsLayout, material->GetShaderCollection()[0].GetShaderVariantId()};
-            ShaderOptionGroup options1{optionsLayout, material->GetShaderCollection()[1].GetShaderVariantId()};
-            ShaderOptionGroup options2{optionsLayout, material->GetShaderCollection()[2].GetShaderVariantId()};
+            ShaderOptionGroup options0{optionsLayout, shaderCollection[0].GetShaderVariantId()};
+            ShaderOptionGroup options1{optionsLayout, shaderCollection[1].GetShaderVariantId()};
+            ShaderOptionGroup options2{optionsLayout, shaderCollection[2].GetShaderVariantId()};
             EXPECT_EQ(optionRangeB.Get(options0).GetIndex(),  2);
             EXPECT_EQ(optionRangeB.Get(options1).GetIndex(),  2);
             EXPECT_EQ(optionRangeB.Get(options2).GetIndex(),  2);
@@ -556,9 +558,9 @@ namespace UnitTest
 
         // Check the values on the underlying ShaderVariantReferences
         {
-            ShaderOptionGroup options0{optionsLayout, material->GetShaderCollection()[0].GetShaderVariantId()};
-            ShaderOptionGroup options1{optionsLayout, material->GetShaderCollection()[1].GetShaderVariantId()};
-            ShaderOptionGroup options2{optionsLayout, material->GetShaderCollection()[2].GetShaderVariantId()};
+            ShaderOptionGroup options0{optionsLayout, shaderCollection[0].GetShaderVariantId()};
+            ShaderOptionGroup options1{optionsLayout, shaderCollection[1].GetShaderVariantId()};
+            ShaderOptionGroup options2{optionsLayout, shaderCollection[2].GetShaderVariantId()};
             EXPECT_EQ(optionRangeB.Get(options0).GetIndex(),  5);
             EXPECT_EQ(optionRangeB.Get(options1).GetIndex(),  5);
             EXPECT_EQ(optionRangeB.Get(options2).GetIndex(),  5);
@@ -569,9 +571,9 @@ namespace UnitTest
     {
         MaterialTypeAssetCreator materialTypeCreator;
         materialTypeCreator.Begin(Uuid::CreateRandom());
-        materialTypeCreator.AddShader(m_testMaterialShaderAsset, Name{"one"});
-        materialTypeCreator.AddShader(m_testMaterialShaderAsset, Name{"two"});
-        materialTypeCreator.AddShader(m_testMaterialShaderAsset, Name{"three"});
+        materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"one"});
+        materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"two"});
+        materialTypeCreator.AddShader(m_testMaterialShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"three"});
         materialTypeCreator.BeginMaterialProperty(Name{"EnableSecondShader"}, MaterialPropertyDataType::Bool);
         materialTypeCreator.ConnectMaterialPropertyToShaderEnabled(Name{"two"});
         materialTypeCreator.EndMaterialProperty();
@@ -585,21 +587,23 @@ namespace UnitTest
 
         MaterialPropertyIndex enableShader = material->FindPropertyIndex(Name{"EnableSecondShader"});
 
-        EXPECT_TRUE(material->GetShaderCollection()[0].IsEnabled());
-        EXPECT_FALSE(material->GetShaderCollection()[1].IsEnabled());
-        EXPECT_TRUE(material->GetShaderCollection()[2].IsEnabled());
+        const ShaderCollection& shaderCollection = material->GetShaderCollection(MaterialPipelineNameCommon);
+
+        EXPECT_TRUE(shaderCollection[0].IsEnabled());
+        EXPECT_FALSE(shaderCollection[1].IsEnabled());
+        EXPECT_TRUE(shaderCollection[2].IsEnabled());
 
         material->SetPropertyValue(enableShader, true);
 
-        EXPECT_TRUE(material->GetShaderCollection()[0].IsEnabled());
-        EXPECT_TRUE(material->GetShaderCollection()[1].IsEnabled());
-        EXPECT_TRUE(material->GetShaderCollection()[2].IsEnabled());
+        EXPECT_TRUE(shaderCollection[0].IsEnabled());
+        EXPECT_TRUE(shaderCollection[1].IsEnabled());
+        EXPECT_TRUE(shaderCollection[2].IsEnabled());
 
         material->SetPropertyValue(enableShader, false);
 
-        EXPECT_TRUE(material->GetShaderCollection()[0].IsEnabled());
-        EXPECT_FALSE(material->GetShaderCollection()[1].IsEnabled());
-        EXPECT_TRUE(material->GetShaderCollection()[2].IsEnabled());
+        EXPECT_TRUE(shaderCollection[0].IsEnabled());
+        EXPECT_FALSE(shaderCollection[1].IsEnabled());
+        EXPECT_TRUE(shaderCollection[2].IsEnabled());
     }
 
 
@@ -642,9 +646,9 @@ namespace UnitTest
         EXPECT_TRUE(result.IsSuccess());
         EXPECT_EQ(0, result.GetValue());
 
-        for (size_t i = 0; i < material->GetShaderCollection().size(); ++i)
+        for (size_t i = 0; i < material->GetShaderCollection(MaterialPipelineNameCommon).size(); ++i)
         {
-            auto& shaderItem = material->GetShaderCollection()[i];
+            auto& shaderItem = material->GetShaderCollection(MaterialPipelineNameCommon)[i];
 
             EXPECT_EQ(0, shaderItem.GetShaderOptions()->GetValue(Name{"o_enumA"}).GetIndex());
             EXPECT_EQ(1, shaderItem.GetShaderOptions()->GetValue(Name{"o_enumB"}).GetIndex());

+ 30 - 26
Gems/Atom/RPI/Code/Tests/Material/MaterialTypeAssetTests.cpp

@@ -414,9 +414,9 @@ namespace UnitTest
         MaterialTypeAssetCreator materialTypeCreator;
         materialTypeCreator.Begin(Uuid::CreateRandom());
 
-        materialTypeCreator.AddShader(m_testShaderAsset, Name{"first"});
-        materialTypeCreator.AddShader(m_testShaderAsset, Name{"second"});
-        materialTypeCreator.AddShader(m_testShaderAsset, Name{"third"});
+        materialTypeCreator.AddShader(m_testShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"first"});
+        materialTypeCreator.AddShader(m_testShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"second"});
+        materialTypeCreator.AddShader(m_testShaderAsset, AZ::RPI::ShaderVariantId{}, Name{"third"});
 
         materialTypeCreator.BeginMaterialProperty(Name{"SecondShaderEnabled"}, MaterialPropertyDataType::Bool);
         materialTypeCreator.ConnectMaterialPropertyToShaderEnabled(Name{"second"});
@@ -1322,19 +1322,21 @@ namespace UnitTest
         tester.SerializeOut(materialTypeAsset.Get());
         materialTypeAsset = tester.SerializeIn(Data::AssetId(Uuid::CreateRandom()));
 
-        EXPECT_EQ(3, materialTypeAsset->GetShaderCollection().size());
-        EXPECT_EQ(shaderA, materialTypeAsset->GetShaderCollection()[0].GetShaderAsset());
-        EXPECT_EQ(shaderB, materialTypeAsset->GetShaderCollection()[1].GetShaderAsset());
-        EXPECT_EQ(shaderC, materialTypeAsset->GetShaderCollection()[2].GetShaderAsset());
+        const ShaderCollection& shaderCollection = materialTypeAsset->GetShaderCollection(MaterialPipelineNameCommon);
 
-        EXPECT_EQ(materialTypeAsset->GetShaderCollection()[0].GetShaderOptions()->GetValue(Name{"o_quality"}).GetIndex(),
+        EXPECT_EQ(3, shaderCollection.size());
+        EXPECT_EQ(shaderA, shaderCollection[0].GetShaderAsset());
+        EXPECT_EQ(shaderB, shaderCollection[1].GetShaderAsset());
+        EXPECT_EQ(shaderC, shaderCollection[2].GetShaderAsset());
+
+        EXPECT_EQ(shaderCollection[0].GetShaderOptions()->GetValue(Name{"o_quality"}).GetIndex(),
             m_testShaderOptionsLayout->FindValue(Name{"o_quality"}, Name{"Med"}).GetIndex());
-        EXPECT_EQ(materialTypeAsset->GetShaderCollection()[0].GetShaderOptions()->GetValue(Name{"o_lightCount"}).GetIndex(), 5);
-        EXPECT_EQ(materialTypeAsset->GetShaderCollection()[1].GetShaderOptions()->GetValue(Name{"o_quality"}).GetIndex(),
+        EXPECT_EQ(shaderCollection[0].GetShaderOptions()->GetValue(Name{"o_lightCount"}).GetIndex(), 5);
+        EXPECT_EQ(shaderCollection[1].GetShaderOptions()->GetValue(Name{"o_quality"}).GetIndex(),
             m_testShaderOptionsLayout->FindValue(Name{"o_quality"}, Name{"High"}).GetIndex());
-        EXPECT_EQ(materialTypeAsset->GetShaderCollection()[1].GetShaderOptions()->GetValue(Name{"o_lightCount"}).GetIndex(), 3);
-        EXPECT_FALSE(materialTypeAsset->GetShaderCollection()[2].GetShaderOptions()->GetValue(Name{"o_quality"}).IsValid());
-        EXPECT_FALSE(materialTypeAsset->GetShaderCollection()[2].GetShaderOptions()->GetValue(Name{"o_lightCount"}).IsValid());
+        EXPECT_EQ(shaderCollection[1].GetShaderOptions()->GetValue(Name{"o_lightCount"}).GetIndex(), 3);
+        EXPECT_FALSE(shaderCollection[2].GetShaderOptions()->GetValue(Name{"o_quality"}).IsValid());
+        EXPECT_FALSE(shaderCollection[2].GetShaderOptions()->GetValue(Name{"o_lightCount"}).IsValid());
 
         EXPECT_EQ(m_testMaterialSrgLayout, materialTypeAsset->GetMaterialSrgLayout());
 
@@ -1665,19 +1667,21 @@ namespace UnitTest
 
         // Check ownership results...
 
-        EXPECT_TRUE(materialTypeAsset->GetShaderCollection()[0].MaterialOwnsShaderOption(Name{"o_materialOption_inBothShaders"}));
-        EXPECT_TRUE(materialTypeAsset->GetShaderCollection()[0].MaterialOwnsShaderOption(Name{"o_materialOption_inShaderA"}));
-        EXPECT_FALSE(materialTypeAsset->GetShaderCollection()[0].MaterialOwnsShaderOption(Name{"o_materialOption_inShaderB"}));
-        EXPECT_FALSE(materialTypeAsset->GetShaderCollection()[0].MaterialOwnsShaderOption(Name{"o_globalOption_inBothShaders"}));
-        EXPECT_FALSE(materialTypeAsset->GetShaderCollection()[0].MaterialOwnsShaderOption(Name{"o_globalOption_inShaderA"}));
-        EXPECT_FALSE(materialTypeAsset->GetShaderCollection()[0].MaterialOwnsShaderOption(Name{"o_globalOption_inShaderB"}));
-
-        EXPECT_TRUE(materialTypeAsset->GetShaderCollection()[1].MaterialOwnsShaderOption(Name{"o_materialOption_inBothShaders"}));
-        EXPECT_FALSE(materialTypeAsset->GetShaderCollection()[1].MaterialOwnsShaderOption(Name{"o_materialOption_inShaderA"}));
-        EXPECT_TRUE(materialTypeAsset->GetShaderCollection()[1].MaterialOwnsShaderOption(Name{"o_materialOption_inShaderB"}));
-        EXPECT_FALSE(materialTypeAsset->GetShaderCollection()[1].MaterialOwnsShaderOption(Name{"o_globalOption_inBothShaders"}));
-        EXPECT_FALSE(materialTypeAsset->GetShaderCollection()[1].MaterialOwnsShaderOption(Name{"o_globalOption_inShaderA"}));
-        EXPECT_FALSE(materialTypeAsset->GetShaderCollection()[1].MaterialOwnsShaderOption(Name{"o_globalOption_inShaderB"}));
+        const ShaderCollection& shaderCollection = materialTypeAsset->GetShaderCollection(MaterialPipelineNameCommon);
+
+        EXPECT_TRUE(shaderCollection[0].MaterialOwnsShaderOption(Name{"o_materialOption_inBothShaders"}));
+        EXPECT_TRUE(shaderCollection[0].MaterialOwnsShaderOption(Name{"o_materialOption_inShaderA"}));
+        EXPECT_FALSE(shaderCollection[0].MaterialOwnsShaderOption(Name{"o_materialOption_inShaderB"}));
+        EXPECT_FALSE(shaderCollection[0].MaterialOwnsShaderOption(Name{"o_globalOption_inBothShaders"}));
+        EXPECT_FALSE(shaderCollection[0].MaterialOwnsShaderOption(Name{"o_globalOption_inShaderA"}));
+        EXPECT_FALSE(shaderCollection[0].MaterialOwnsShaderOption(Name{"o_globalOption_inShaderB"}));
+
+        EXPECT_TRUE(shaderCollection[1].MaterialOwnsShaderOption(Name{"o_materialOption_inBothShaders"}));
+        EXPECT_FALSE(shaderCollection[1].MaterialOwnsShaderOption(Name{"o_materialOption_inShaderA"}));
+        EXPECT_TRUE(shaderCollection[1].MaterialOwnsShaderOption(Name{"o_materialOption_inShaderB"}));
+        EXPECT_FALSE(shaderCollection[1].MaterialOwnsShaderOption(Name{"o_globalOption_inBothShaders"}));
+        EXPECT_FALSE(shaderCollection[1].MaterialOwnsShaderOption(Name{"o_globalOption_inShaderA"}));
+        EXPECT_FALSE(shaderCollection[1].MaterialOwnsShaderOption(Name{"o_globalOption_inShaderB"}));
     }
 
 }

+ 15 - 11
Gems/Atom/RPI/Code/Tests/Material/MaterialTypeSourceDataTests.cpp

@@ -827,15 +827,17 @@ namespace UnitTest
 
         // Check the results...
 
+        const ShaderCollection& shaderCollection = materialTypeAsset->GetShaderCollection(MaterialPipelineNameCommon);
+
         EXPECT_EQ(m_testMaterialSrgLayout, materialTypeAsset->GetMaterialSrgLayout());
-        EXPECT_EQ(3, materialTypeAsset->GetShaderCollection().size());
-        EXPECT_EQ(shaderAssetA, materialTypeAsset->GetShaderCollection()[0].GetShaderAsset());
-        EXPECT_EQ(shaderAssetB, materialTypeAsset->GetShaderCollection()[1].GetShaderAsset());
-        EXPECT_EQ(shaderAssetC, materialTypeAsset->GetShaderCollection()[2].GetShaderAsset());
-
-        ShaderOptionGroup shaderAOptions{shaderOptions, materialTypeAsset->GetShaderCollection()[0].GetShaderVariantId()};
-        ShaderOptionGroup shaderBOptions{shaderOptions, materialTypeAsset->GetShaderCollection()[1].GetShaderVariantId()};
-        ShaderOptionGroup shaderCOptions{shaderOptions, materialTypeAsset->GetShaderCollection()[2].GetShaderVariantId()};
+        EXPECT_EQ(3, shaderCollection.size());
+        EXPECT_EQ(shaderAssetA, shaderCollection[0].GetShaderAsset());
+        EXPECT_EQ(shaderAssetB, shaderCollection[1].GetShaderAsset());
+        EXPECT_EQ(shaderAssetC, shaderCollection[2].GetShaderAsset());
+
+        ShaderOptionGroup shaderAOptions{shaderOptions, shaderCollection[0].GetShaderVariantId()};
+        ShaderOptionGroup shaderBOptions{shaderOptions, shaderCollection[1].GetShaderVariantId()};
+        ShaderOptionGroup shaderCOptions{shaderOptions, shaderCollection[2].GetShaderVariantId()};
         ShaderOptionIndex fooOption = shaderOptions->FindShaderOptionIndex(Name{"o_foo"});
         ShaderOptionIndex barOption = shaderOptions->FindShaderOptionIndex(Name{"o_bar"});
         EXPECT_EQ(shaderAOptions.GetValue(fooOption).GetIndex(), 1);
@@ -1436,12 +1438,14 @@ namespace UnitTest
         EXPECT_TRUE(materialTypeOutcome.IsSuccess());
         Data::Asset<MaterialTypeAsset> materialTypeAsset = materialTypeOutcome.GetValue();
 
+        const ShaderCollection& shaderCollection = materialTypeAsset->GetShaderCollection(MaterialPipelineNameCommon);
+
         // This option is not a dependency of the functor and therefore is not owned by the material
-        EXPECT_FALSE(materialTypeAsset->GetShaderCollection()[0].MaterialOwnsShaderOption(Name{"o_quality"}));
+        EXPECT_FALSE(shaderCollection[0].MaterialOwnsShaderOption(Name{"o_quality"}));
 
         // These options are listed as dependencies of the functor, so the material owns them
-        EXPECT_TRUE(materialTypeAsset->GetShaderCollection()[0].MaterialOwnsShaderOption(Name{"o_foo"}));
-        EXPECT_TRUE(materialTypeAsset->GetShaderCollection()[0].MaterialOwnsShaderOption(Name{"o_bar"}));
+        EXPECT_TRUE(shaderCollection[0].MaterialOwnsShaderOption(Name{"o_foo"}));
+        EXPECT_TRUE(shaderCollection[0].MaterialOwnsShaderOption(Name{"o_bar"}));
     }
     
     TEST_F(MaterialTypeSourceDataTests, CreateMaterialTypeAsset_FunctorIsInsidePropertyGroup)

+ 8 - 2
Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp

@@ -239,7 +239,13 @@ namespace ShaderManagementConsole
             return AZStd::vector<AZ::RPI::ShaderCollection::Item>();
         }
 
-        return AZStd::vector<AZ::RPI::ShaderCollection::Item>(
-            materialInstance->GetShaderCollection().begin(), materialInstance->GetShaderCollection().end());
+        AZStd::vector<AZ::RPI::ShaderCollection::Item> shaderItems;
+
+        for (const auto& shaderCollectionPair : materialInstance->GetShaderCollections())
+        {
+            shaderItems.insert(shaderItems.end(), shaderCollectionPair.second.begin(), shaderCollectionPair.second.end());
+        }
+
+        return shaderItems;
     }
 } // namespace ShaderManagementConsole

+ 1 - 1
Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiMaterialDetails.inl

@@ -139,7 +139,7 @@ namespace AZ::Render
 
                                 const ImGuiTreeNodeFlags shaderNodeFlags = ImGuiTreeNodeFlags_DefaultOpen;
 
-                                if (ImGui::TreeNodeEx(AZStd::string::format("Shader: %s - %s", shaderData.m_shaderTag.GetCStr(), shaderData.m_shader->GetAsset().GetHint().c_str()).c_str(), shaderNodeFlags))
+                                if (ImGui::TreeNodeEx(AZStd::string::format("Shader: %s - %s - %s", shaderData.m_materialPipelineName.GetCStr(), shaderData.m_shaderTag.GetCStr(), shaderData.m_shader->GetAsset().GetHint().c_str()).c_str(), shaderNodeFlags))
                                 {
                                     ImGui::Indent();
 

+ 49 - 31
Gems/AtomLyIntegration/EditorModeFeedback/Code/Source/Draw/EditorStateMeshDrawPacket.cpp

@@ -52,39 +52,45 @@ namespace AZ::Render
     bool EditorStateMeshDrawPacket::SetShaderOption(const Name& shaderOptionName, RPI::ShaderOptionValue value)
     {
         // check if the material owns this option in any of its shaders, if so it can't be set externally
-        for (auto& shaderItem : m_material->GetShaderCollection())
+        for (auto shaderCollectionIter : m_material->GetShaderCollections())
         {
-            const RPI::ShaderOptionGroupLayout* layout = shaderItem.GetShaderOptions()->GetShaderOptionLayout();
-            RPI::ShaderOptionIndex index = layout->FindShaderOptionIndex(shaderOptionName);
-            if (index.IsValid())
+            for (auto& shaderItem : shaderCollectionIter.second)
             {
-                if (shaderItem.MaterialOwnsShaderOption(index))
+                const RPI::ShaderOptionGroupLayout* layout = shaderItem.GetShaderOptions()->GetShaderOptionLayout();
+                RPI::ShaderOptionIndex index = layout->FindShaderOptionIndex(shaderOptionName);
+                if (index.IsValid())
                 {
-                    return false;
+                    if (shaderItem.MaterialOwnsShaderOption(index))
+                    {
+                        return false;
+                    }
                 }
             }
         }
 
-        for (auto& shaderItem : m_material->GetShaderCollection())
+        for (auto shaderCollectionIter : m_material->GetShaderCollections())
         {
-            const RPI::ShaderOptionGroupLayout* layout = shaderItem.GetShaderOptions()->GetShaderOptionLayout();
-            RPI::ShaderOptionIndex index = layout->FindShaderOptionIndex(shaderOptionName);
-            if (index.IsValid())
+            for (auto& shaderItem : shaderCollectionIter.second)
             {
-                // try to find an existing option entry in the list
-                auto itEntry = AZStd::find_if(m_shaderOptions.begin(), m_shaderOptions.end(), [&shaderOptionName](const ShaderOptionPair& entry)
-                {
-                    return entry.first == shaderOptionName;
-                });
-
-                // store the option name and value, they will be used in DoUpdate() to select the appropriate shader variant
-                if (itEntry == m_shaderOptions.end())
-                {
-                    m_shaderOptions.push_back({ shaderOptionName, value });
-                }
-                else
+                const RPI::ShaderOptionGroupLayout* layout = shaderItem.GetShaderOptions()->GetShaderOptionLayout();
+                RPI::ShaderOptionIndex index = layout->FindShaderOptionIndex(shaderOptionName);
+                if (index.IsValid())
                 {
-                    itEntry->second = value;
+                    // try to find an existing option entry in the list
+                    auto itEntry = AZStd::find_if(m_shaderOptions.begin(), m_shaderOptions.end(), [&shaderOptionName](const ShaderOptionPair& entry)
+                        {
+                            return entry.first == shaderOptionName;
+                        });
+
+                    // store the option name and value, they will be used in DoUpdate() to select the appropriate shader variant
+                    if (itEntry == m_shaderOptions.end())
+                    {
+                        m_shaderOptions.push_back({shaderOptionName, value});
+                    }
+                    else
+                    {
+                        itEntry->second = value;
+                    }
                 }
             }
         }
@@ -148,7 +154,7 @@ namespace AZ::Render
 
         m_perDrawSrgs.clear();
 
-        auto appendShader = [&](const RPI::ShaderCollection::Item& shaderItem)
+        auto appendShader = [&](const RPI::ShaderCollection::Item& shaderItem, const Name& materialPipelineName)
         {
             if (!parentScene.HasOutputForPipelineState(m_drawListTag))
             {
@@ -264,6 +270,14 @@ namespace AZ::Render
                 drawRequest.m_uniqueShaderResourceGroup = drawSrg->GetRHIShaderResourceGroup();
                 m_perDrawSrgs.push_back(drawSrg);
             }
+
+            if (materialPipelineName != RPI::MaterialPipelineNameCommon)
+            {
+                RHI::DrawFilterTag pipelineTag = parentScene.GetDrawFilterTagRegistry()->AcquireTag(materialPipelineName);
+                AZ_Assert(pipelineTag.IsValid(), "Could not acquire pipeline filter tag '%s'.", materialPipelineName.GetCStr());
+                drawRequest.m_drawFilterMask = 1 << pipelineTag.GetIndex();
+            }
+
             drawPacketBuilder.AddDrawItem(drawRequest);
 
             shaderList.emplace_back(AZStd::move(shader));
@@ -284,17 +298,21 @@ namespace AZ::Render
             }
         }
 
-        for (auto& shaderItem : m_material->GetShaderCollection())
+        // TODO(MaterialPipeline): We might want to detect duplicate ShaderItem objects here, and merge them to avoid redundant RHI DrawItems.
+        for (auto& [materialPipelineName, shaderCollection] : m_material->GetShaderCollections())
         {
-            if (shaderItem.IsEnabled())
+            for (auto& shaderItem : shaderCollection)
             {
-                if (shaderList.size() == RHI::DrawPacketBuilder::DrawItemCountMax)
+                if (shaderItem.IsEnabled())
                 {
-                    AZ_Error("EditorStateMeshDrawPacket", false, "Material has more than the limit of %d active shader items.", RHI::DrawPacketBuilder::DrawItemCountMax);
-                    return false;
-                }
+                    if (shaderList.size() == RHI::DrawPacketBuilder::DrawItemCountMax)
+                    {
+                        AZ_Error("EditorStateMeshDrawPacket", false, "Material has more than the limit of %d active shader items.", RHI::DrawPacketBuilder::DrawItemCountMax);
+                        return false;
+                    }
 
-                appendShader(shaderItem);
+                    appendShader(shaderItem, materialPipelineName);
+                }
             }
         }
 

+ 1 - 1
Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp

@@ -215,7 +215,7 @@ namespace Terrain
     {
         m_terrainSrg = {};
 
-        for (auto& shaderItem : m_materialInstance->GetShaderCollection())
+        for (auto& shaderItem : m_materialInstance->GetShaderCollection(AZ::RPI::MaterialPipelineNameCommon))
         {
             if (shaderItem.GetShaderAsset()->GetDrawListName() == AZ::Name("forward"))
             {

+ 2 - 2
Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.cpp

@@ -125,7 +125,7 @@ namespace Terrain
             m_materialInstance = materialInstance;
 
             // Queue the load of the material's shaders now since they'll be needed later.
-            for (auto& shaderItem : m_materialInstance->GetShaderCollection())
+            for (auto& shaderItem : m_materialInstance->GetShaderCollection(AZ::RPI::MaterialPipelineNameCommon))
             {
                 AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = shaderItem.GetShaderAsset();
                 if (!shaderAsset.IsReady())
@@ -455,7 +455,7 @@ namespace Terrain
         m_candidateSectors.clear();
 
         // Rebuild common draw packet data
-        for (auto& shaderItem : m_materialInstance->GetShaderCollection())
+        for (auto& shaderItem : m_materialInstance->GetShaderCollection(AZ::RPI::MaterialPipelineNameCommon))
         {
             if (!shaderItem.IsEnabled())
             {