Преглед на файлове

Merge pull request #709 from nick-l-o3de/update_main_to_25051

Update `main` branch to the last known good build for 25.05.1
Nicholas Lawson преди 1 месец
родител
ревизия
42acad1c95
променени са 100 файла, в които са добавени 3305 реда и са изтрити 1612 реда
  1. 2 1
      Gem/Code/CMakeLists.txt
  2. 2 2
      Gem/Code/Source/AreaLightExampleComponent.cpp
  3. 1 1
      Gem/Code/Source/AreaLightExampleComponent.h
  4. 9 9
      Gem/Code/Source/AssetLoadTestComponent.cpp
  5. 129 136
      Gem/Code/Source/Automation/ScriptManager.cpp
  6. 2 2
      Gem/Code/Source/Automation/ScriptManager.h
  7. 2 2
      Gem/Code/Source/Automation/ScriptReporter.cpp
  8. 22 30
      Gem/Code/Source/Automation/ScriptableImGui.cpp
  9. 2 5
      Gem/Code/Source/Automation/ScriptableImGui.h
  10. 0 2
      Gem/Code/Source/AuxGeomSharedDrawFunctions.cpp
  11. 3 2
      Gem/Code/Source/BakedShaderVariantExampleComponent.cpp
  12. 4 5
      Gem/Code/Source/BloomExampleComponent.cpp
  13. 1 0
      Gem/Code/Source/BloomExampleComponent.h
  14. 4 2
      Gem/Code/Source/CheckerboardExampleComponent.cpp
  15. 5 4
      Gem/Code/Source/CullingAndLodExampleComponent.cpp
  16. 3 2
      Gem/Code/Source/CullingAndLodExampleComponent.h
  17. 3 3
      Gem/Code/Source/DecalExampleComponent.cpp
  18. 2 2
      Gem/Code/Source/DepthOfFieldExampleComponent.cpp
  19. 23 21
      Gem/Code/Source/DiffuseGIExampleComponent.cpp
  20. 9 19
      Gem/Code/Source/DynamicMaterialTestComponent.cpp
  21. 0 2
      Gem/Code/Source/DynamicMaterialTestComponent.h
  22. 16 16
      Gem/Code/Source/ExposureExampleComponent.cpp
  23. 1 4
      Gem/Code/Source/ExposureExampleComponent.h
  24. 6 4
      Gem/Code/Source/EyeMaterialExampleComponent.cpp
  25. 15 18
      Gem/Code/Source/LightCullingExampleComponent.cpp
  26. 1 4
      Gem/Code/Source/LightCullingExampleComponent.h
  27. 15 18
      Gem/Code/Source/MSAA_RPI_ExampleComponent.cpp
  28. 0 7
      Gem/Code/Source/MSAA_RPI_ExampleComponent.h
  29. 29 20
      Gem/Code/Source/MeshExampleComponent.cpp
  30. 0 2
      Gem/Code/Source/MeshExampleComponent.h
  31. 271 0
      Gem/Code/Source/MultiGPURPIExampleComponent.cpp
  32. 83 0
      Gem/Code/Source/MultiGPURPIExampleComponent.h
  33. 9 3
      Gem/Code/Source/MultiRenderPipelineExampleComponent.cpp
  34. 29 30
      Gem/Code/Source/MultiSceneExampleComponent.cpp
  35. 1 4
      Gem/Code/Source/MultiSceneExampleComponent.h
  36. 4 1
      Gem/Code/Source/MultiViewSingleSceneAuxGeomExampleComponent.cpp
  37. 5 3
      Gem/Code/Source/ParallaxMappingExampleComponent.cpp
  38. 23 27
      Gem/Code/Source/Passes/RayTracingAmbientOcclusionPass.cpp
  39. 2 3
      Gem/Code/Source/Passes/RayTracingAmbientOcclusionPass.h
  40. 13 12
      Gem/Code/Source/Performance/HighInstanceExampleComponent.cpp
  41. 1 1
      Gem/Code/Source/Platform/Android/SSRExampleComponent_Traits_Platform.h
  42. 0 5
      Gem/Code/Source/Platform/Android/SampleComponentManager_Android.cpp
  43. 6 1
      Gem/Code/Source/Platform/Android/Utils_Android.cpp
  44. 1 1
      Gem/Code/Source/Platform/Linux/SSRExampleComponent_Traits_Platform.h
  45. 0 5
      Gem/Code/Source/Platform/Linux/SampleComponentManager_Linux.cpp
  46. 10 7
      Gem/Code/Source/Platform/Linux/Utils_Linux.cpp
  47. 1 1
      Gem/Code/Source/Platform/Mac/SSRExampleComponent_Traits_Platform.h
  48. 0 5
      Gem/Code/Source/Platform/Mac/SampleComponentManager_Mac.cpp
  49. 12 8
      Gem/Code/Source/Platform/Mac/Utils_Mac.cpp
  50. 1 1
      Gem/Code/Source/Platform/Windows/SSRExampleComponent_Traits_Platform.h
  51. 0 11
      Gem/Code/Source/Platform/Windows/SampleComponentManager_Windows.cpp
  52. 8 2
      Gem/Code/Source/Platform/Windows/Utils_Windows.cpp
  53. 1 1
      Gem/Code/Source/Platform/iOS/SSRExampleComponent_Traits_Platform.h
  54. 0 5
      Gem/Code/Source/Platform/iOS/SampleComponentManager_iOS.cpp
  55. 6 1
      Gem/Code/Source/Platform/iOS/Utils_iOS.cpp
  56. 52 20
      Gem/Code/Source/ProceduralSkinnedMesh.cpp
  57. 9 4
      Gem/Code/Source/ProceduralSkinnedMesh.h
  58. 50 38
      Gem/Code/Source/ProceduralSkinnedMeshUtils.cpp
  59. 31 32
      Gem/Code/Source/RHI/AlphaToCoverageExampleComponent.cpp
  60. 1 1
      Gem/Code/Source/RHI/AlphaToCoverageExampleComponent.h
  61. 129 124
      Gem/Code/Source/RHI/AsyncComputeExampleComponent.cpp
  62. 2 3
      Gem/Code/Source/RHI/AsyncComputeExampleComponent.h
  63. 8 4
      Gem/Code/Source/RHI/BasicRHIComponent.cpp
  64. 5 1
      Gem/Code/Source/RHI/BasicRHIComponent.h
  65. 669 205
      Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.cpp
  66. 86 11
      Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.h
  67. 34 40
      Gem/Code/Source/RHI/ComputeExampleComponent.cpp
  68. 1 2
      Gem/Code/Source/RHI/ComputeExampleComponent.h
  69. 25 29
      Gem/Code/Source/RHI/CopyQueueComponent.cpp
  70. 1 2
      Gem/Code/Source/RHI/CopyQueueComponent.h
  71. 19 24
      Gem/Code/Source/RHI/DualSourceBlendingComponent.cpp
  72. 1 2
      Gem/Code/Source/RHI/DualSourceBlendingComponent.h
  73. 74 63
      Gem/Code/Source/RHI/IndirectRenderingExampleComponent.cpp
  74. 1 0
      Gem/Code/Source/RHI/IndirectRenderingExampleComponent.h
  75. 29 24
      Gem/Code/Source/RHI/InputAssemblyExampleComponent.cpp
  76. 1 1
      Gem/Code/Source/RHI/InputAssemblyExampleComponent.h
  77. 38 45
      Gem/Code/Source/RHI/MRTExampleComponent.cpp
  78. 1 4
      Gem/Code/Source/RHI/MRTExampleComponent.h
  79. 50 59
      Gem/Code/Source/RHI/MSAAExampleComponent.cpp
  80. 2 2
      Gem/Code/Source/RHI/MSAAExampleComponent.h
  81. 25 29
      Gem/Code/Source/RHI/MatrixAlignmentTestExampleComponent.cpp
  82. 1 3
      Gem/Code/Source/RHI/MatrixAlignmentTestExampleComponent.h
  83. 710 0
      Gem/Code/Source/RHI/MultiGPUExampleComponent.cpp
  84. 126 0
      Gem/Code/Source/RHI/MultiGPUExampleComponent.h
  85. 23 27
      Gem/Code/Source/RHI/MultiThreadComponent.cpp
  86. 1 2
      Gem/Code/Source/RHI/MultiThreadComponent.h
  87. 38 44
      Gem/Code/Source/RHI/MultipleViewsComponent.cpp
  88. 1 4
      Gem/Code/Source/RHI/MultipleViewsComponent.h
  89. 51 50
      Gem/Code/Source/RHI/QueryExampleComponent.cpp
  90. 1 1
      Gem/Code/Source/RHI/QueryExampleComponent.h
  91. 57 70
      Gem/Code/Source/RHI/RayTracingExampleComponent.cpp
  92. 11 13
      Gem/Code/Source/RHI/RayTracingExampleComponent.h
  93. 33 35
      Gem/Code/Source/RHI/SphericalHarmonicsExampleComponent.cpp
  94. 1 3
      Gem/Code/Source/RHI/SphericalHarmonicsExampleComponent.h
  95. 30 37
      Gem/Code/Source/RHI/StencilExampleComponent.cpp
  96. 2 4
      Gem/Code/Source/RHI/StencilExampleComponent.h
  97. 54 42
      Gem/Code/Source/RHI/SubpassExampleComponent.cpp
  98. 3 1
      Gem/Code/Source/RHI/SubpassExampleComponent.h
  99. 19 24
      Gem/Code/Source/RHI/Texture3dExampleComponent.cpp
  100. 1 0
      Gem/Code/Source/RHI/Texture3dExampleComponent.h

+ 2 - 1
Gem/Code/CMakeLists.txt

@@ -25,10 +25,11 @@ ly_add_target(
         PUBLIC
             AZ::AzGameFramework
             Gem::Atom_AtomBridge.Static
-            Gem::Atom_Feature_Common.Static
+            Gem::Atom_Feature_Common.Public
             Gem::Atom_Component_DebugCamera.Static
             Gem::Profiler.Static
             Gem::DiffuseProbeGrid.Static
+            Gem::DebugDraw.Static
 )
 
 ly_add_target(

+ 2 - 2
Gem/Code/Source/AreaLightExampleComponent.cpp

@@ -209,13 +209,13 @@ namespace AtomSampleViewer
             {
                 if (!meshHandle.IsValid())
                 {
-                    meshHandle = m_meshFeatureProcessor->AcquireMesh(MeshHandleDescriptor{ modelAsset }, m_materialInstances.at(i));
+                    meshHandle = m_meshFeatureProcessor->AcquireMesh(AZ::Render::MeshHandleDescriptor(modelAsset, m_materialInstances.at(i)));
                 }
                 else if (m_modelAsset.GetId() != modelAsset.GetId())
                 {
                     // Valid mesh handle, but wrong asset. Release and reacquire.
                     m_meshFeatureProcessor->ReleaseMesh(meshHandle);
-                    meshHandle = m_meshFeatureProcessor->AcquireMesh(MeshHandleDescriptor{ modelAsset }, m_materialInstances.at(i));
+                    meshHandle = m_meshFeatureProcessor->AcquireMesh(AZ::Render::MeshHandleDescriptor(modelAsset, m_materialInstances.at(i)));
                 }
 
                 AZ::Transform transform = AZ::Transform::CreateIdentity();

+ 1 - 1
Gem/Code/Source/AreaLightExampleComponent.h

@@ -80,7 +80,7 @@ namespace AtomSampleViewer
         struct Configuration
         {
             LightType m_lightType = Point;
-            AZStd::string m_modelAssetPath = "objects/test/area_light_test_sphere.azmodel";
+            AZStd::string m_modelAssetPath = "objects/test/area_light_test_sphere.fbx.azmodel";
             uint32_t m_count = 1;
 
             float m_intensity = 30.0f;

+ 9 - 9
Gem/Code/Source/AssetLoadTestComponent.cpp

@@ -66,9 +66,9 @@ namespace AtomSampleViewer
 
         const AZStd::vector<AZStd::string> defaultModelAllowist =
         {
-            "Objects/bunny.azmodel",
-            "Objects/Shaderball_simple.azmodel",
-            "Objects/suzanne.azmodel",
+            "Objects/bunny.fbx.azmodel",
+            "Objects/Shaderball_simple.fbx.azmodel",
+            "Objects/suzanne.fbx.azmodel",
         };
         m_modelBrowser.SetDefaultPinnedAssets(defaultModelAllowist);
     }
@@ -179,7 +179,7 @@ namespace AtomSampleViewer
             if (instanceData.m_materialAssetId.IsValid())
             {
                 AZ::Data::Asset<RPI::MaterialAsset> materialAsset;
-                materialAsset.Create(instanceData.m_materialAssetId);
+                materialAsset.Create(instanceData.m_materialAssetId, true);
                 materialInstance = AZ::RPI::Material::FindOrCreate(materialAsset);
 
                 // cache the material when its loaded
@@ -188,10 +188,10 @@ namespace AtomSampleViewer
 
             if (instanceData.m_modelAssetId.IsValid())
             {
-                AZ::Data::Asset<AZ::RPI::ModelAsset> modelAsset;
-                modelAsset.Create(instanceData.m_modelAssetId);
-
-                instanceData.m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor{ modelAsset }, materialInstance);
+                AZ::Render::MeshHandleDescriptor descriptor;
+                descriptor.m_modelAsset.Create(instanceData.m_modelAssetId, true);
+                descriptor.m_customMaterials[AZ::Render::DefaultCustomMaterialId].m_material = materialInstance;
+                instanceData.m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(descriptor);
                 GetMeshFeatureProcessor()->SetTransform(instanceData.m_meshHandle, instanceData.m_transform);
             }
         }
@@ -226,7 +226,7 @@ namespace AtomSampleViewer
         }
         else
         {
-            return AZ::RPI::AssetUtils::GetAssetIdForProductPath("testdata/objects/cube/cube.azmodel", AZ::RPI::AssetUtils::TraceLevel::Error);
+            return AZ::RPI::AssetUtils::GetAssetIdForProductPath("testdata/objects/cube/cube.fbx.azmodel", AZ::RPI::AssetUtils::TraceLevel::Error);
         }
     }
 

+ 129 - 136
Gem/Code/Source/Automation/ScriptManager.cpp

@@ -27,6 +27,7 @@
 #include <AzCore/Script/ScriptAsset.h>
 #include <AzCore/Math/MathReflection.h>
 #include <AzCore/Console/IConsole.h>
+#include <AzCore/Time/ITime.h>
 
 #include <AzFramework/API/ApplicationAPI.h>
 #include <AzFramework/Components/ConsoleBus.h>
@@ -38,20 +39,23 @@
 
 namespace AtomSampleViewer
 {
-    ScriptManager* ScriptManager::s_instance = nullptr;
-
     ScriptManager::ScriptManager()
         : m_scriptBrowser("@user@/lua_script_browser.xml")
     {
     }
 
-    void ScriptManager::Activate()
+    ScriptManager* ScriptManager::GetInstance()
     {
-        AZ_Assert(s_instance == nullptr, "ScriptManager is already activated");
-        s_instance = this;
-
-        ScriptableImGui::Create();
+        static ScriptManager* s_instance = nullptr;
+        if (!s_instance)
+        {
+            AtomSampleViewer::SampleComponentSingletonRequestBus::BroadcastResult(s_instance, &AtomSampleViewer::SampleComponentSingletonRequestBus::Events::GetScriptManagerInstance);
+        }
+        return s_instance;
+    }
 
+    void ScriptManager::Activate()
+    {
         m_scriptContext = AZStd::make_unique<AZ::ScriptContext>();
         m_sriptBehaviorContext = AZStd::make_unique<AZ::BehaviorContext>();
         ReflectScriptContext(m_sriptBehaviorContext.get());
@@ -72,11 +76,9 @@ namespace AtomSampleViewer
 
     void ScriptManager::Deactivate()
     {
-        s_instance = nullptr;
         m_scriptContext = nullptr;
         m_sriptBehaviorContext = nullptr;
         m_scriptBrowser.Deactivate();
-        ScriptableImGui::Destory();
         m_imageComparisonOptions.Deactivate();
         ScriptRunnerRequestBus::Handler::BusDisconnect();
         ScriptRepeaterRequestBus::Handler::BusDisconnect();
@@ -932,6 +934,8 @@ namespace AtomSampleViewer
 
     void ScriptManager::ExecuteScript(const AZStd::string& scriptFilePath)
     {
+        ScriptManager* s_instance = GetInstance();
+
         AZ::Data::Asset<AZ::ScriptAsset> scriptAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::ScriptAsset>(scriptFilePath.c_str());
         if (!scriptAsset)
         {
@@ -955,7 +959,7 @@ namespace AtomSampleViewer
         // Execute(script) will add commands to the m_scriptOperations. These should be considered part of their own test script, for reporting purposes.
         s_instance->m_scriptOperations.push([scriptFilePath]()
             {
-                s_instance->m_scriptReporter.PushScript(scriptFilePath);
+                GetInstance()->m_scriptReporter.PushScript(scriptFilePath);
             }
         );
 
@@ -981,8 +985,8 @@ namespace AtomSampleViewer
         s_instance->m_scriptOperations.push([]()
             {
                 // We don't call m_scriptReporter.PopScript() yet because some cleanup needs to happen in TickScript() on the next frame.
-                AZ_Assert(!s_instance->m_shouldPopScript, "m_shouldPopScript is already true");
-                s_instance->m_shouldPopScript = true;
+                AZ_Assert(!GetInstance()->m_shouldPopScript, "m_shouldPopScript is already true");
+                GetInstance()->m_shouldPopScript = true;
             }
         );
     }
@@ -1133,8 +1137,7 @@ namespace AtomSampleViewer
         {
             ReportScriptError(message.c_str());
         };
-
-        s_instance->m_scriptOperations.push(AZStd::move(func));
+        GetInstance()->m_scriptOperations.push(AZStd::move(func));
     }
 
     void ScriptManager::Script_Warning(const AZStd::string& message)
@@ -1143,8 +1146,7 @@ namespace AtomSampleViewer
         {
             ReportScriptWarning(message.c_str());
         };
-
-        s_instance->m_scriptOperations.push(AZStd::move(func));
+        GetInstance()->m_scriptOperations.push(AZStd::move(func));
     }
 
     void ScriptManager::Script_Print(const AZStd::string& message [[maybe_unused]])
@@ -1155,7 +1157,7 @@ namespace AtomSampleViewer
             AZ_TracePrintf("Automation", "Script: %s\n", message.c_str());
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(func));
+        GetInstance()->m_scriptOperations.push(AZStd::move(func));
 #endif
     }
 
@@ -1175,8 +1177,6 @@ namespace AtomSampleViewer
     {
         auto operation = [sampleName]()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
-
             if (sampleName.empty())
             {
                 SampleComponentManagerRequestBus::Broadcast(&SampleComponentManagerRequests::Reset);
@@ -1193,13 +1193,13 @@ namespace AtomSampleViewer
                     // They need 1 frame to activate, 1 frame to start ticking, and 1 frame to guarantee
                     // that a sample OnTick occurs before a ScriptManager::OnTick. We schedule
                     // a few extra just in case.
-                    AZ_Assert(s_instance->m_scriptIdleFrames == 0, "m_scriptIdleFrames is being stomped");
-                    s_instance->m_scriptIdleFrames = 6;
+                    AZ_Assert(GetInstance()->m_scriptIdleFrames == 0, "m_scriptIdleFrames is being stomped");
+                    GetInstance()->m_scriptIdleFrames = 6;
                 }
             }
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_ShowTool(const AZStd::string& toolName, bool enable)
@@ -1212,67 +1212,60 @@ namespace AtomSampleViewer
             AZ_Warning("ScriptManager", foundTool, "Can't find [%s] tool", toolName.c_str());
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_RunScript(const AZStd::string& scriptFilePath)
     {
         // Unlike other Script_ callback functions, we process immediately instead of pushing onto the m_scriptOperations queue.
         // This function is special because running the script is what adds more commands onto the m_scriptOperations queue.
-        s_instance->ExecuteScript(scriptFilePath);
+        GetInstance()->ExecuteScript(scriptFilePath);
     }
 
     void ScriptManager::Script_IdleFrames(int numFrames)
     {
         auto operation = [numFrames]()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
-
-            AZ_Assert(s_instance->m_scriptIdleFrames == 0, "m_scriptIdleFrames is being stomped");
-            s_instance->m_scriptIdleFrames = numFrames;
+            AZ_Assert(GetInstance()->m_scriptIdleFrames == 0, "m_scriptIdleFrames is being stomped");
+            GetInstance()->m_scriptIdleFrames = numFrames;
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_IdleSeconds(float numSeconds)
     {
         auto operation = [numSeconds]()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
-
-            s_instance->m_scriptIdleSeconds = numSeconds;
+            GetInstance()->m_scriptIdleSeconds = numSeconds;
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_LockFrameTime(float seconds)
     {
         auto operation = [seconds]()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
-
-            int milliseconds = static_cast<int>(seconds * 1000);
-
-            AZ::Interface<AZ::IConsole>::Get()->PerformCommand(AZStd::string::format("t_simulationTickDeltaOverride %d", milliseconds).c_str());
-            s_instance->m_frameTimeIsLocked = true;
+            AZ::TimeUs us = AZ::SecondsToTimeUs(seconds);
+            
+            AZ::CVarFixedString commandString = AZ::CVarFixedString::format("t_simulationTickDeltaOverride %" PRId64, static_cast<int64_t>(us));
+            AZ::Interface<AZ::IConsole>::Get()->PerformCommand(commandString.c_str());
+            GetInstance()->m_frameTimeIsLocked = true;
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_UnlockFrameTime()
     {
         auto operation = []()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
-
             AZ::Interface<AZ::IConsole>::Get()->PerformCommand("t_simulationTickDeltaOverride 0");
-            s_instance->m_frameTimeIsLocked = false;
+            GetInstance()->m_frameTimeIsLocked = false;
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_SetImguiValue(AZ::ScriptDataContext& dc)
@@ -1304,7 +1297,7 @@ namespace AtomSampleViewer
                 ScriptableImGui::SetBool(fieldNameString, value);
             };
 
-            s_instance->m_scriptOperations.push(AZStd::move(func));
+            GetInstance()->m_scriptOperations.push(AZStd::move(func));
         }
         else if (dc.IsNumber(1))
         {
@@ -1316,7 +1309,7 @@ namespace AtomSampleViewer
                 ScriptableImGui::SetNumber(fieldNameString, value);
             };
 
-            s_instance->m_scriptOperations.push(AZStd::move(func));
+            GetInstance()->m_scriptOperations.push(AZStd::move(func));
         }
         else if (dc.IsString(1))
         {
@@ -1330,7 +1323,7 @@ namespace AtomSampleViewer
                 ScriptableImGui::SetString(fieldNameString, valueString);
             };
 
-            s_instance->m_scriptOperations.push(AZStd::move(func));
+            GetInstance()->m_scriptOperations.push(AZStd::move(func));
         }
         else if (dc.IsClass<AZ::Vector3>(1))
         {
@@ -1342,7 +1335,7 @@ namespace AtomSampleViewer
                 ScriptableImGui::SetVector(fieldNameString, value);
             };
 
-            s_instance->m_scriptOperations.push(AZStd::move(func));
+            GetInstance()->m_scriptOperations.push(AZStd::move(func));
         }
         else if (dc.IsClass<AZ::Vector2>(1))
         {
@@ -1354,7 +1347,7 @@ namespace AtomSampleViewer
                 ScriptableImGui::SetVector(fieldNameString, value);
             };
 
-            s_instance->m_scriptOperations.push(AZStd::move(func));
+            GetInstance()->m_scriptOperations.push(AZStd::move(func));
         }
     }
 
@@ -1370,11 +1363,11 @@ namespace AtomSampleViewer
             }
             else
             {
-                s_instance->ReportScriptError("ResizeViewport() is not supported on this platform");
+                GetInstance()->ReportScriptError("ResizeViewport() is not supported on this platform");
             }
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_ExecuteConsoleCommand(const AZStd::string& command)
@@ -1384,7 +1377,7 @@ namespace AtomSampleViewer
             AZ::Interface<AZ::IConsole>::Get()->PerformCommand(command.c_str());
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::SetShowImGui(bool show)
@@ -1405,10 +1398,10 @@ namespace AtomSampleViewer
     {
         auto operation = [show]()
         {
-            s_instance->SetShowImGui(show);
+            GetInstance()->SetShowImGui(show);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     bool ScriptManager::PrepareForScreenCapture(const AZStd::string& imageName)
@@ -1435,6 +1428,7 @@ namespace AtomSampleViewer
             return false;
         }
 
+        ScriptManager* s_instance = GetInstance();
         s_instance->m_scriptReporter.AddScreenshotTest(imageName);
 
         s_instance->m_isCapturePending = true;
@@ -1451,7 +1445,7 @@ namespace AtomSampleViewer
                 &AZ::Render::FrameCaptureTestRequestBus::Events::SetScreenshotFolder, screenshotFolder);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_SetTestEnvPath(const AZStd::string& envPath)
@@ -1462,7 +1456,7 @@ namespace AtomSampleViewer
                 &AZ::Render::FrameCaptureTestRequestBus::Events::SetTestEnvPath, envPath);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_SetOfficialBaselineImageFolder(const AZStd::string& baselineFolder)
@@ -1473,7 +1467,7 @@ namespace AtomSampleViewer
                 &AZ::Render::FrameCaptureTestRequestBus::Events::SetOfficialBaselineImageFolder, baselineFolder);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_SetLocalBaselineImageFolder(const AZStd::string& baselineFolder)
@@ -1484,17 +1478,17 @@ namespace AtomSampleViewer
                 &AZ::Render::FrameCaptureTestRequestBus::Events::SetLocalBaselineImageFolder, baselineFolder);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_SelectImageComparisonToleranceLevel(const AZStd::string& presetName)
     {
         auto operation = [presetName]()
         {
-            s_instance->m_imageComparisonOptions.SelectToleranceLevel(presetName);
+            GetInstance()->m_imageComparisonOptions.SelectToleranceLevel(presetName);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_CaptureScreenshot(const AZStd::string& imageName)
@@ -1503,6 +1497,8 @@ namespace AtomSampleViewer
 
         auto operation = [imageName]()
         {
+            ScriptManager* s_instance = GetInstance();
+
             // Note this will pause the script until the capture is complete
             if (PrepareForScreenCapture(imageName))
             {
@@ -1530,7 +1526,7 @@ namespace AtomSampleViewer
                 AZ::Render::FrameCaptureRequestBus::BroadcastResult(capOutcome, &AZ::Render::FrameCaptureRequestBus::Events::CaptureScreenshot, screenshotFilePath);
                 if (!capOutcome.IsSuccess())
                 {
-                    ReportScriptError(AZStd::string::format("Failed to initiate frame capture for '%s'", screenshotFilePath.c_str()));
+                    ReportScriptError(AZStd::string::format("Failed to initiate screenshot capture for '%s: %s'", screenshotFilePath.c_str(), capOutcome.GetError().m_errorMessage.c_str()));
                     s_instance->m_isCapturePending = false;
                     s_instance->m_frameCaptureId = AZ::Render::InvalidFrameCaptureId;
                     s_instance->ResumeScript();
@@ -1542,16 +1538,17 @@ namespace AtomSampleViewer
             }
         };
 
+        ScriptManager* s_instance = GetInstance();
         s_instance->m_scriptOperations.push(AZStd::move(operation));
         s_instance->m_scriptOperations.push([]()
             {
-                s_instance->m_scriptReporter.CheckLatestScreenshot(s_instance->m_imageComparisonOptions.GetCurrentToleranceLevel());
+                GetInstance()->m_scriptReporter.CheckLatestScreenshot(GetInstance()->m_imageComparisonOptions.GetCurrentToleranceLevel());
             });
 
         // restore imgui show/hide
         s_instance->m_scriptOperations.push([]()
             {
-                s_instance->SetShowImGui(s_instance->m_prevShowImGui);
+                GetInstance()->SetShowImGui(GetInstance()->m_prevShowImGui);
             });
 
     }
@@ -1562,6 +1559,8 @@ namespace AtomSampleViewer
 
         auto operation = [imageName]()
         {
+            ScriptManager* s_instance = GetInstance();
+
             // Note this will pause the script until the capture is complete
             if (PrepareForScreenCapture(imageName))
             {
@@ -1589,7 +1588,7 @@ namespace AtomSampleViewer
                 AZ::Render::FrameCaptureRequestBus::BroadcastResult(capOutcome, &AZ::Render::FrameCaptureRequestBus::Events::CaptureScreenshot, screenshotFilePath);
                 if (!capOutcome.IsSuccess())
                 {
-                    ReportScriptError(AZStd::string::format("Failed to initiate frame capture for '%s'", screenshotFilePath.c_str()));
+                    ReportScriptError(AZStd::string::format("Failed to initiate screenshot capture for '%s: %s'", screenshotFilePath.c_str(), capOutcome.GetError().m_errorMessage.c_str()));
                     s_instance->m_isCapturePending = false;
                     s_instance->m_frameCaptureId = AZ::Render::InvalidFrameCaptureId;
                     s_instance->ResumeScript();
@@ -1601,16 +1600,18 @@ namespace AtomSampleViewer
             }
         };
 
+        ScriptManager* s_instance = GetInstance();
+
         s_instance->m_scriptOperations.push(AZStd::move(operation));
         s_instance->m_scriptOperations.push([]()
             {
-                s_instance->m_scriptReporter.CheckLatestScreenshot(s_instance->m_imageComparisonOptions.GetCurrentToleranceLevel());
+                GetInstance()->m_scriptReporter.CheckLatestScreenshot(GetInstance()->m_imageComparisonOptions.GetCurrentToleranceLevel());
             });
 
         // restore imgui show/hide
         s_instance->m_scriptOperations.push([]()
             {
-                s_instance->SetShowImGui(s_instance->m_prevShowImGui);
+                GetInstance()->SetShowImGui(GetInstance()->m_prevShowImGui);
             });
     }
 
@@ -1618,6 +1619,8 @@ namespace AtomSampleViewer
     {
         auto operation = [imageName]()
         {
+            ScriptManager* s_instance = GetInstance();
+
             // Note this will pause the script until the capture is complete
             if (PrepareForScreenCapture(imageName))
             {
@@ -1645,7 +1648,7 @@ namespace AtomSampleViewer
                 AZ::Render::FrameCaptureRequestBus::BroadcastResult(capOutcome, &AZ::Render::FrameCaptureRequestBus::Events::CaptureScreenshotWithPreview, screenshotFilePath);
                 if (!capOutcome.IsSuccess())
                 {
-                    ReportScriptError(AZStd::string::format("Failed to initiate frame capture for '%s'", screenshotFilePath.c_str()));
+                    ReportScriptError(AZStd::string::format("Failed to initiate screenshot capture for '%s: %s'", screenshotFilePath.c_str(), capOutcome.GetError().m_errorMessage.c_str()));
                     s_instance->m_isCapturePending = false;
                     s_instance->m_frameCaptureId = AZ::Render::InvalidFrameCaptureId;
                     s_instance->ResumeScript();
@@ -1657,10 +1660,11 @@ namespace AtomSampleViewer
             }
         };
 
+        ScriptManager* s_instance = GetInstance();
         s_instance->m_scriptOperations.push(AZStd::move(operation));
         s_instance->m_scriptOperations.push([]()
             {
-                s_instance->m_scriptReporter.CheckLatestScreenshot(s_instance->m_imageComparisonOptions.GetCurrentToleranceLevel());
+                GetInstance()->m_scriptReporter.CheckLatestScreenshot(GetInstance()->m_imageComparisonOptions.GetCurrentToleranceLevel());
             });
     }
 
@@ -1741,6 +1745,8 @@ namespace AtomSampleViewer
 
         auto operation = [passHierarchy, slot, imageName, readbackOption]()
         {
+            ScriptManager* s_instance = GetInstance();
+
             // Note this will pause the script until the capture is complete
             if (PrepareForScreenCapture(imageName))
             {
@@ -1768,7 +1774,7 @@ namespace AtomSampleViewer
                 AZ::Render::FrameCaptureRequestBus::BroadcastResult(capOutcome, &AZ::Render::FrameCaptureRequestBus::Events::CapturePassAttachment, screenshotFilePath, passHierarchy, slot, readbackOption);
                 if (!capOutcome.IsSuccess())
                 {
-                    ReportScriptError(AZStd::string::format("Failed to initiate frame capture for '%s'", screenshotFilePath.c_str()));
+                    ReportScriptError(AZStd::string::format("Failed to initiate screenshot capture for '%s: %s'", screenshotFilePath.c_str(), capOutcome.GetError().m_errorMessage.c_str()));
                     s_instance->m_isCapturePending = false;
                     s_instance->m_frameCaptureId = AZ::Render::InvalidFrameCaptureId;
                     s_instance->ResumeScript();
@@ -1781,10 +1787,10 @@ namespace AtomSampleViewer
             }
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
-        s_instance->m_scriptOperations.push([]()
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push([]()
             {
-                s_instance->m_scriptReporter.CheckLatestScreenshot(s_instance->m_imageComparisonOptions.GetCurrentToleranceLevel());
+                GetInstance()->m_scriptReporter.CheckLatestScreenshot(GetInstance()->m_imageComparisonOptions.GetCurrentToleranceLevel());
             });
     }
 
@@ -1851,14 +1857,14 @@ namespace AtomSampleViewer
 
         auto operation = [outputFilePath]()
         {
-            s_instance->m_isCapturePending = true;
-            s_instance->AZ::Render::ProfilingCaptureNotificationBus::Handler::BusConnect();
-            s_instance->PauseScript();
+            GetInstance()->m_isCapturePending = true;
+            GetInstance()->AZ::Render::ProfilingCaptureNotificationBus::Handler::BusConnect();
+            GetInstance()->PauseScript();
 
             AZ::Render::ProfilingCaptureRequestBus::Broadcast(&AZ::Render::ProfilingCaptureRequestBus::Events::CapturePassTimestamp, outputFilePath);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_CaptureCpuFrameTime(AZ::ScriptDataContext& dc)
@@ -1872,14 +1878,14 @@ namespace AtomSampleViewer
 
         auto operation = [outputFilePath]()
         {
-            s_instance->m_isCapturePending = true;
-            s_instance->AZ::Render::ProfilingCaptureNotificationBus::Handler::BusConnect();
-            s_instance->PauseScript();
+            GetInstance()->m_isCapturePending = true;
+            GetInstance()->AZ::Render::ProfilingCaptureNotificationBus::Handler::BusConnect();
+            GetInstance()->PauseScript();
 
             AZ::Render::ProfilingCaptureRequestBus::Broadcast(&AZ::Render::ProfilingCaptureRequestBus::Events::CaptureCpuFrameTime, outputFilePath);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_CapturePassPipelineStatistics(AZ::ScriptDataContext& dc)
@@ -1893,14 +1899,14 @@ namespace AtomSampleViewer
 
         auto operation = [outputFilePath]()
         {
-            s_instance->m_isCapturePending = true;
-            s_instance->AZ::Render::ProfilingCaptureNotificationBus::Handler::BusConnect();
-            s_instance->PauseScript();
+            GetInstance()->m_isCapturePending = true;
+            GetInstance()->AZ::Render::ProfilingCaptureNotificationBus::Handler::BusConnect();
+            GetInstance()->PauseScript();
 
             AZ::Render::ProfilingCaptureRequestBus::Broadcast(&AZ::Render::ProfilingCaptureRequestBus::Events::CapturePassPipelineStatistics, outputFilePath);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_CaptureCpuProfilingStatistics(AZ::ScriptDataContext& dc)
@@ -1914,9 +1920,9 @@ namespace AtomSampleViewer
 
         auto operation = [outputFilePath]()
         {
-            s_instance->m_isCapturePending = true;
-            s_instance->AZ::Debug::ProfilerNotificationBus::Handler::BusConnect();
-            s_instance->PauseScript();
+            GetInstance()->m_isCapturePending = true;
+            GetInstance()->AZ::Debug::ProfilerNotificationBus::Handler::BusConnect();
+            GetInstance()->PauseScript();
 
             if (auto profilerSystem = AZ::Debug::ProfilerSystemInterface::Get(); profilerSystem)
             {
@@ -1924,7 +1930,7 @@ namespace AtomSampleViewer
             }
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_CaptureBenchmarkMetadata(AZ::ScriptDataContext& dc)
@@ -1952,14 +1958,14 @@ namespace AtomSampleViewer
 
         auto operation = [benchmarkName, outputFilePath]()
         {
-            s_instance->m_isCapturePending = true;
-            s_instance->AZ::Render::ProfilingCaptureNotificationBus::Handler::BusConnect();
-            s_instance->PauseScript();
+            GetInstance()->m_isCapturePending = true;
+            GetInstance()->AZ::Render::ProfilingCaptureNotificationBus::Handler::BusConnect();
+            GetInstance()->PauseScript();
 
             AZ::Render::ProfilingCaptureRequestBus::Broadcast(&AZ::Render::ProfilingCaptureRequestBus::Events::CaptureBenchmarkMetadata, benchmarkName, outputFilePath);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     bool ScriptManager::ValidateProfilingCaptureScripContexts(AZ::ScriptDataContext& dc, AZStd::string& outputFilePath)
@@ -2003,12 +2009,12 @@ namespace AtomSampleViewer
 
     int ScriptManager::Script_GetRandomTestSeed()
     {
-        return s_instance->m_testSuiteRunConfig.m_randomSeed;
+        return GetInstance()->m_testSuiteRunConfig.m_randomSeed;
     }
 
     void ScriptManager::CheckArcBallControllerHandler()
     {
-        if (0 == AZ::Debug::ArcBallControllerRequestBus::GetNumOfEventHandlers(s_instance->m_cameraEntity->GetId()))
+        if (0 == AZ::Debug::ArcBallControllerRequestBus::GetNumOfEventHandlers(GetInstance()->m_cameraEntity->GetId()))
         {
             ReportScriptError("There is no handler for ArcBallControllerRequestBus for the camera entity.");
         }
@@ -2016,7 +2022,7 @@ namespace AtomSampleViewer
 
     void ScriptManager::CheckNoClipControllerHandler()
     {
-        if (0 == AZ::Debug::NoClipControllerRequestBus::GetNumOfEventHandlers(s_instance->m_cameraEntity->GetId()))
+        if (0 == AZ::Debug::NoClipControllerRequestBus::GetNumOfEventHandlers(GetInstance()->m_cameraEntity->GetId()))
         {
             ReportScriptError("There is no handler for NoClipControllerRequestBus for the camera entity.");
         }
@@ -2026,119 +2032,109 @@ namespace AtomSampleViewer
     {
         auto operation = [center]()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
             CheckArcBallControllerHandler();
-            AZ::Debug::ArcBallControllerRequestBus::Event(s_instance->m_cameraEntity->GetId(), &AZ::Debug::ArcBallControllerRequestBus::Events::SetCenter, center);
+            AZ::Debug::ArcBallControllerRequestBus::Event(GetInstance()->m_cameraEntity->GetId(), &AZ::Debug::ArcBallControllerRequestBus::Events::SetCenter, center);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_ArcBallCameraController_SetPan(AZ::Vector3 pan)
     {
         auto operation = [pan]()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
             CheckArcBallControllerHandler();
-            AZ::Debug::ArcBallControllerRequestBus::Event(s_instance->m_cameraEntity->GetId(), &AZ::Debug::ArcBallControllerRequestBus::Events::SetPan, pan);
+            AZ::Debug::ArcBallControllerRequestBus::Event(GetInstance()->m_cameraEntity->GetId(), &AZ::Debug::ArcBallControllerRequestBus::Events::SetPan, pan);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_ArcBallCameraController_SetDistance(float distance)
     {
         auto operation = [distance]()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
             CheckArcBallControllerHandler();
-            AZ::Debug::ArcBallControllerRequestBus::Event(s_instance->m_cameraEntity->GetId(), &AZ::Debug::ArcBallControllerRequestBus::Events::SetDistance, distance);
+            AZ::Debug::ArcBallControllerRequestBus::Event(GetInstance()->m_cameraEntity->GetId(), &AZ::Debug::ArcBallControllerRequestBus::Events::SetDistance, distance);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_ArcBallCameraController_SetHeading(float heading)
     {
         auto operation = [heading]()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
             CheckArcBallControllerHandler();
-            AZ::Debug::ArcBallControllerRequestBus::Event(s_instance->m_cameraEntity->GetId(), &AZ::Debug::ArcBallControllerRequestBus::Events::SetHeading, heading);
+            AZ::Debug::ArcBallControllerRequestBus::Event(GetInstance()->m_cameraEntity->GetId(), &AZ::Debug::ArcBallControllerRequestBus::Events::SetHeading, heading);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_ArcBallCameraController_SetPitch(float pitch)
     {
         auto operation = [pitch]()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
             CheckArcBallControllerHandler();
-            AZ::Debug::ArcBallControllerRequestBus::Event(s_instance->m_cameraEntity->GetId(), &AZ::Debug::ArcBallControllerRequestBus::Events::SetPitch, pitch);
+            AZ::Debug::ArcBallControllerRequestBus::Event(GetInstance()->m_cameraEntity->GetId(), &AZ::Debug::ArcBallControllerRequestBus::Events::SetPitch, pitch);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_NoClipCameraController_SetPosition(AZ::Vector3 position)
     {
         auto operation = [position]()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
             CheckNoClipControllerHandler();
-            AZ::Debug::NoClipControllerRequestBus::Event(s_instance->m_cameraEntity->GetId(), &AZ::Debug::NoClipControllerRequestBus::Events::SetPosition, position);
+            AZ::Debug::NoClipControllerRequestBus::Event(GetInstance()->m_cameraEntity->GetId(), &AZ::Debug::NoClipControllerRequestBus::Events::SetPosition, position);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_NoClipCameraController_SetHeading(float heading)
     {
         auto operation = [heading]()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
             CheckNoClipControllerHandler();
-            AZ::Debug::NoClipControllerRequestBus::Event(s_instance->m_cameraEntity->GetId(), &AZ::Debug::NoClipControllerRequestBus::Events::SetHeading, heading);
+            AZ::Debug::NoClipControllerRequestBus::Event(GetInstance()->m_cameraEntity->GetId(), &AZ::Debug::NoClipControllerRequestBus::Events::SetHeading, heading);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_NoClipCameraController_SetPitch(float pitch)
     {
         auto operation = [pitch]()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
             CheckNoClipControllerHandler();
-            AZ::Debug::NoClipControllerRequestBus::Event(s_instance->m_cameraEntity->GetId(), &AZ::Debug::NoClipControllerRequestBus::Events::SetPitch, pitch);
+            AZ::Debug::NoClipControllerRequestBus::Event(GetInstance()->m_cameraEntity->GetId(), &AZ::Debug::NoClipControllerRequestBus::Events::SetPitch, pitch);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_NoClipCameraController_SetFov(float fov)
     {
         auto operation = [fov]()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
             CheckNoClipControllerHandler();
-            AZ::Debug::NoClipControllerRequestBus::Event(s_instance->m_cameraEntity->GetId(), &AZ::Debug::NoClipControllerRequestBus::Events::SetFov, fov);
+            AZ::Debug::NoClipControllerRequestBus::Event(GetInstance()->m_cameraEntity->GetId(), &AZ::Debug::NoClipControllerRequestBus::Events::SetFov, fov);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_AssetTracking_Start()
     {
         auto operation = []()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
-            s_instance->m_assetStatusTracker.StartTracking();
+            GetInstance()->m_assetStatusTracker.StartTracking();
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
 
@@ -2146,36 +2142,33 @@ namespace AtomSampleViewer
     {
         auto operation = [sourceAssetPath, expectedCount]()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
-            s_instance->m_assetStatusTracker.ExpectAsset(sourceAssetPath, expectedCount);
+            GetInstance()->m_assetStatusTracker.ExpectAsset(sourceAssetPath, expectedCount);
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_AssetTracking_IdleUntilExpectedAssetsFinish(float timeout)
     {
         auto operation = [timeout]()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
 
-            AZ_Assert(!s_instance->m_waitForAssetTracker, "It shouldn't be possible to run the next command until m_waitForAssetTracker is false");
+            AZ_Assert(!GetInstance()->m_waitForAssetTracker, "It shouldn't be possible to run the next command until m_waitForAssetTracker is false");
 
-            s_instance->m_waitForAssetTracker = true;
-            s_instance->m_assetTrackingTimeout = timeout;
+            GetInstance()->m_waitForAssetTracker = true;
+            GetInstance()->m_assetTrackingTimeout = timeout;
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 
     void ScriptManager::Script_AssetTracking_Stop()
     {
         auto operation = []()
         {
-            AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
-            s_instance->m_assetStatusTracker.StopTracking();
+            GetInstance()->m_assetStatusTracker.StopTracking();
         };
 
-        s_instance->m_scriptOperations.push(AZStd::move(operation));
+        GetInstance()->m_scriptOperations.push(AZStd::move(operation));
     }
 } // namespace AtomSampleViewer

+ 2 - 2
Gem/Code/Source/Automation/ScriptManager.h

@@ -69,6 +69,8 @@ namespace AtomSampleViewer
 
         void RunMainTestSuite(const AZStd::string& suiteFilePath, bool exitOnTestEnd, int randomSeed);
 
+        static ScriptManager* GetInstance();
+
     private:
         static constexpr const char* FullSuiteScriptFilepath = "scripts/_fulltestsuite_.bv.luac";
 
@@ -352,7 +354,5 @@ namespace AtomSampleViewer
 
         bool m_prevShowImGui = true;
         bool m_showImGui = true;
-
-        static ScriptManager* s_instance;
     };
 } // namespace AtomSampleViewer

+ 2 - 2
Gem/Code/Source/Automation/ScriptReporter.cpp

@@ -323,8 +323,8 @@ namespace AtomSampleViewer
 
     const ImageComparisonToleranceLevel* ScriptReporter::FindBestToleranceLevel(float diffScore, bool filterImperceptibleDiffs) const
     {
-        float thresholdChecked = 0.0f;
-        bool ignoringMinorDiffs = false;
+        [[maybe_unused]] float thresholdChecked = 0.0f;
+        [[maybe_unused]] bool ignoringMinorDiffs = false;
         for (const ImageComparisonToleranceLevel& level : m_availableToleranceLevels)
         {
             AZ_Assert(level.m_threshold > thresholdChecked || thresholdChecked == 0.0f, "Threshold values are not sequential");

+ 22 - 30
Gem/Code/Source/Automation/ScriptableImGui.cpp

@@ -11,27 +11,23 @@
 #include <AzFramework/StringFunc/StringFunc.h>
 #include <Utils/Utils.h>
 
+#include <SampleComponentManagerBus.h>
+
 namespace AtomSampleViewer
 {
-    ScriptableImGui* ScriptableImGui::s_instance = nullptr;
-
-    void ScriptableImGui::Create()
+    ScriptableImGui* ScriptableImGui::GetInstance()
     {
-        AZ_Assert(s_instance == nullptr, "instance already called");
-        s_instance = aznew ScriptableImGui();
-    }
-
-    void ScriptableImGui::Destory()
-    {
-        AZ_Assert(s_instance != nullptr, "instance is null");
-        delete s_instance;
-        s_instance = nullptr;
+        static ScriptableImGui* s_instance = nullptr;
+        if (!s_instance)
+        {
+            AtomSampleViewer::SampleComponentSingletonRequestBus::BroadcastResult(s_instance, &AtomSampleViewer::SampleComponentSingletonRequestBus::Events::GetScriptableImGuiInstance);
+        }
+        return s_instance;
     }
 
     void ScriptableImGui::CheckAllActionsConsumed()
     {
-        AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
-
+        ScriptableImGui* s_instance = GetInstance();
         AZ_Error("Automation", s_instance->m_scriptedActions.empty(), "Not all scripted ImGui actions were consumed");
         for (auto iter : s_instance->m_scriptedActions)
         {
@@ -43,31 +39,27 @@ namespace AtomSampleViewer
 
     void ScriptableImGui::ClearActions()
     {
-        AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
-
+        ScriptableImGui* s_instance = GetInstance();
         s_instance->m_scriptedActions.clear();
         s_instance->m_nameContextStack.clear();
     }
 
     void ScriptableImGui::PushNameContext(const AZStd::string& nameContext)
     {
-        AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
-
+        ScriptableImGui* s_instance = GetInstance();
         s_instance->m_nameContextStack.push_back(nameContext);
     }
 
     void ScriptableImGui::PopNameContext()
     {
-        AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
-
+        ScriptableImGui* s_instance = GetInstance();
         AZ_Assert(!s_instance->m_nameContextStack.empty(), "Called PopNameContext too many times");
         s_instance->m_nameContextStack.pop_back();
     }
 
     ScriptableImGui::ActionItem ScriptableImGui::FindAndRemoveAction(const AZStd::string& pathToImGuiItem)
     {
-        AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
-
+        ScriptableImGui* s_instance = GetInstance();
         auto iter = s_instance->m_scriptedActions.find(pathToImGuiItem);
         if (iter != s_instance->m_scriptedActions.end())
         {
@@ -81,6 +73,7 @@ namespace AtomSampleViewer
 
     AZStd::string ScriptableImGui::MakeFullPath(const AZStd::string& forLabel)
     {
+        ScriptableImGui* s_instance = GetInstance();
         static constexpr char Delimiter[] = "/";
 
         AZStd::string fullPath;
@@ -101,31 +94,31 @@ namespace AtomSampleViewer
 
     void ScriptableImGui::SetBool(const AZStd::string& pathToImGuiItem, bool value)
     {
-        AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
+        ScriptableImGui* s_instance = GetInstance();
         s_instance->m_scriptedActions[pathToImGuiItem] = value;
     }
 
     void ScriptableImGui::SetNumber(const AZStd::string& pathToImGuiItem, float value)
     {
-        AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
+        ScriptableImGui* s_instance = GetInstance();
         s_instance->m_scriptedActions[pathToImGuiItem] = value;
     }
 
     void ScriptableImGui::SetVector(const AZStd::string& pathToImGuiItem, const AZ::Vector2& value)
     {
-        AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
+        ScriptableImGui* s_instance = GetInstance();
         s_instance->m_scriptedActions[pathToImGuiItem] = value;
     }
 
     void ScriptableImGui::SetVector(const AZStd::string& pathToImGuiItem, const AZ::Vector3& value)
     {
-        AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
+        ScriptableImGui* s_instance = GetInstance();
         s_instance->m_scriptedActions[pathToImGuiItem] = value;
     }
 
     void ScriptableImGui::SetString(const AZStd::string& pathToImGuiItem, const AZStd::string& value)
     {
-        AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
+        ScriptableImGui* s_instance = GetInstance();
         s_instance->m_scriptedActions[pathToImGuiItem] = value;
     }
 
@@ -498,8 +491,6 @@ namespace AtomSampleViewer
 
     bool ScriptableImGui::TreeNodeEx(const char* label, ImGuiSelectableFlags flags)
     {
-        AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
-
         if (ImGui::TreeNodeEx(label, flags))
         {
             PushNameContext(label);
@@ -517,7 +508,7 @@ namespace AtomSampleViewer
 
     bool ScriptableImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags)
     {
-        AZ_DEBUG_STATIC_MEMEBER(instance, s_instance);
+        ScriptableImGui* s_instance = GetInstance();
 
         const AZStd::string pathToImGuiItem = MakeFullPath(label);
 
@@ -541,6 +532,7 @@ namespace AtomSampleViewer
 
     void ScriptableImGui::EndCombo()
     {
+        ScriptableImGui* s_instance = GetInstance();
         if (s_instance->m_isInScriptedComboPopup)
         {
             s_instance->m_isInScriptedComboPopup = false;

+ 2 - 5
Gem/Code/Source/Automation/ScriptableImGui.h

@@ -42,6 +42,8 @@ namespace AtomSampleViewer
             ~ScopedNameContext() { ScriptableImGui::PopNameContext(); }
         };
 
+        static ScriptableImGui* GetInstance();
+
         //! This can be used to add some context around the ImGui labels that are exposed to the script system.
         //! Each call to PushNameContext() will add a prefix the ImGui labels to form the script field IDs.
         //! For example, the following will result in a script field ID of "A/B/MyButton" instead of just "MyButton".
@@ -97,9 +99,6 @@ namespace AtomSampleViewer
         /////////////////////////////////////////////////////////////////////////////////////////////////
         // Private API for ScriptManager to call...
 
-        static void Create();
-        static void Destory();
-
         //! Call this every frame to report errors when scripted actions aren't consumed through ImGui API function calls.
         //! This usually indicates that a script is trying to manipulate ImGui elements that don't exist.
         static void CheckAllActionsConsumed();
@@ -152,8 +151,6 @@ namespace AtomSampleViewer
         ActionMap m_scriptedActions;
 
         bool m_isInScriptedComboPopup = false;
-
-        static ScriptableImGui* s_instance;
     };
 
 } // namespace AtomSampleViewer

+ 0 - 2
Gem/Code/Source/AuxGeomSharedDrawFunctions.cpp

@@ -802,8 +802,6 @@ namespace AtomSampleViewer
             // Spread the draw style out along the X axis
             for (int style = 0; style < numStyles; ++style)
             {
-                Vector3 boxPosition = basePosition;
-
                 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                 // Layer 0: AABBs with no transform
                 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

+ 3 - 2
Gem/Code/Source/BakedShaderVariantExampleComponent.cpp

@@ -38,7 +38,7 @@ namespace AtomSampleViewer
     {
         namespace Products
         {
-            static constexpr const char ModelFilePath[] = "objects/plane.azmodel";
+            static constexpr const char ModelFilePath[] = "objects/plane.fbx.azmodel";
         } // namespace Products
     } // namespace
 
@@ -88,7 +88,8 @@ namespace AtomSampleViewer
 
         Transform meshTransform =
             Transform::CreateFromQuaternion(Quaternion::CreateFromAxisAngle(Vector3::CreateAxisX(), -AZ::Constants::HalfPi));
-        m_meshHandle = m_meshFeatureProcessor->AcquireMesh(Render::MeshHandleDescriptor{ m_modelAsset }, m_material);
+
+        m_meshHandle = m_meshFeatureProcessor->AcquireMesh(AZ::Render::MeshHandleDescriptor(m_modelAsset, m_material));
         m_meshFeatureProcessor->SetTransform(m_meshHandle, meshTransform);
 
         AZ::RPI::PassFilter passFilter = AZ::RPI::PassFilter::CreateWithPassName(AZ::Name(ATOMSAMPLEVIEWER_TRAIT_BAKED_SHADERVARIANT_SAMPLE_PASS_NAME), 

+ 4 - 5
Gem/Code/Source/BloomExampleComponent.cpp

@@ -372,11 +372,10 @@ namespace AtomSampleViewer
     void BloomExampleComponent::DrawImage(const ImageToDraw* imageInfo)
     {
         // Build draw packet
-        RHI::DrawPacketBuilder drawPacketBuilder;
+        RHI::DrawPacketBuilder drawPacketBuilder{RHI::MultiDevice::DefaultDevice};
         drawPacketBuilder.Begin(nullptr);
-        RHI::DrawLinear drawLinear;
-        drawLinear.m_vertexCount = 4;
-        drawPacketBuilder.SetDrawArguments(drawLinear);
+        m_geometryView.SetDrawArguments(RHI::DrawLinear(4, 0));
+        drawPacketBuilder.SetGeometryView(&m_geometryView);
 
         RHI::DrawPacketBuilder::DrawRequest drawRequest;
         drawRequest.m_listTag = m_drawListTag;
@@ -386,7 +385,7 @@ namespace AtomSampleViewer
         drawPacketBuilder.AddDrawItem(drawRequest);
 
         // Submit draw packet
-        AZStd::unique_ptr<const RHI::DrawPacket> drawPacket(drawPacketBuilder.End());
+        auto drawPacket{drawPacketBuilder.End()};
         m_dynamicDraw->AddDrawPacket(m_scene, AZStd::move(drawPacket));
     }
 

+ 1 - 0
Gem/Code/Source/BloomExampleComponent.h

@@ -98,6 +98,7 @@ namespace AtomSampleViewer
         AZ::RHI::DrawListTag m_drawListTag;
         AZ::Data::Asset<AZ::RPI::ShaderAsset> m_shaderAsset;
         AZ::RHI::Ptr<AZ::RHI::ShaderResourceGroupLayout> m_srgLayout;
+        AZ::RHI::GeometryView m_geometryView;
 
         // shader input indices
         AZ::RHI::ShaderInputNameIndex m_imageInputIndex = "m_texture";

+ 4 - 2
Gem/Code/Source/CheckerboardExampleComponent.cpp

@@ -44,12 +44,14 @@ namespace AtomSampleViewer
     void CheckerboardExampleComponent::Activate()
     {
         AZ::RPI::AssetUtils::TraceLevel traceLevel = AZ::RPI::AssetUtils::TraceLevel::Assert;
-        auto meshAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/shaderball_simple.azmodel", traceLevel);
+        auto modelAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/shaderball_simple.fbx.azmodel", traceLevel);
         auto materialAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::MaterialAsset>(DefaultPbrMaterialPath, traceLevel);
         auto materialInstance = AZ::RPI::Material::FindOrCreate(materialAsset);
 
         m_meshFeatureProcessor = m_scene->GetFeatureProcessor<AZ::Render::MeshFeatureProcessorInterface>();
-        m_meshHandle = m_meshFeatureProcessor->AcquireMesh(AZ::Render::MeshHandleDescriptor{ meshAsset }, materialInstance);
+
+        m_meshHandle = m_meshFeatureProcessor->AcquireMesh(AZ::Render::MeshHandleDescriptor(modelAsset, materialInstance));
+
         m_meshFeatureProcessor->SetTransform(m_meshHandle, AZ::Transform::CreateIdentity());
 
         AZ::Debug::CameraControllerRequestBus::Event(

+ 5 - 4
Gem/Code/Source/CullingAndLodExampleComponent.cpp

@@ -169,8 +169,8 @@ namespace AtomSampleViewer
 
         ClearMeshes();
 
-        const char objectModelFilename[] = "Objects/sphere_5lods.azmodel";
-        const char planeModelFilename[] = "Objects/plane.azmodel";
+        const char objectModelFilename[] = "Objects/sphere_5lods.fbx.azmodel";
+        const char planeModelFilename[] = "Objects/plane.fbx.azmodel";
         Data::Asset<RPI::ModelAsset> objectModelAsset = RPI::AssetUtils::LoadAssetByProductPath<RPI::ModelAsset>(
             objectModelFilename, RPI::AssetUtils::TraceLevel::Assert);
         Data::Asset<RPI::ModelAsset> planeModelAsset = RPI::AssetUtils::LoadAssetByProductPath<RPI::ModelAsset>(
@@ -185,14 +185,14 @@ namespace AtomSampleViewer
         {
             for (uint32_t y = 0; y < numAlongYAxis; ++y)
             {
-                auto meshHandle = meshFP->AcquireMesh(Render::MeshHandleDescriptor{ objectModelAsset }, material);
+                auto meshHandle = meshFP->AcquireMesh(Render::MeshHandleDescriptor(objectModelAsset, material));
                 Transform modelToWorld = Transform::CreateTranslation(Vector3(x * spacing, y * spacing, 2.0f));
                 meshFP->SetTransform(meshHandle, modelToWorld);
                 m_meshHandles.push_back(AZStd::move(meshHandle));
             }
         }
 
-        auto planeMeshHandle = meshFP->AcquireMesh(Render::MeshHandleDescriptor{ planeModelAsset }, material);
+        auto planeMeshHandle = meshFP->AcquireMesh(Render::MeshHandleDescriptor(planeModelAsset, material));
         Vector3 planeNonUniformScale(numAlongXAxis * spacing, numAlongYAxis * spacing, 1.0f);
         Transform planeModelToWorld = Transform::CreateTranslation(Vector3(0.5f * numAlongXAxis * spacing, 0.5f * numAlongYAxis * spacing, 0.0f));
         meshFP->SetTransform(planeMeshHandle, planeModelToWorld, planeNonUniformScale);
@@ -224,6 +224,7 @@ namespace AtomSampleViewer
             dirLightFP->SetDirection(handle, lightTransform.GetBasis(1));
 
             dirLightFP->SetRgbIntensity(handle, Render::PhotometricColor<Render::PhotometricUnit::Lux>(m_directionalLightIntensity * DirectionalLightColor));
+            dirLightFP->SetShadowEnabled(handle, m_dirShadowEnabled);
             dirLightFP->SetCascadeCount(handle, s_cascadesCountDefault);
             dirLightFP->SetShadowmapSize(handle, s_shadowmapSizes[s_shadowmapSizeIndexDefault]);
             dirLightFP->SetDebugFlags(handle,

+ 3 - 2
Gem/Code/Source/CullingAndLodExampleComponent.h

@@ -12,7 +12,7 @@
 #include <Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h>
 #include <Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h>
 #include <Atom/Feature/CoreLights/ShadowConstants.h>
-#include <Atom/Feature/Mesh/MeshFeatureProcessor.h>
+#include <Atom/Feature/Mesh/MeshFeatureProcessorInterface.h>
 #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
 #include <Atom/RPI.Reflect/Model/ModelAsset.h>
 #include <AzCore/Component/TickBus.h>
@@ -100,7 +100,7 @@ namespace AtomSampleViewer
 
         // models
         AZStd::vector<AZ::Render::MeshFeatureProcessorInterface::MeshHandle> m_meshHandles;
-        AZStd::vector<AZ::Render::MeshFeatureProcessorInterface::ModelChangedEvent::Handler> m_modelChangedHandlers;
+        AZStd::vector<AZ::Render::MeshHandleDescriptor::ModelChangedEvent::Handler> m_modelChangedHandlers;
 
         // GUI
         ImGuiSidebar m_imguiSidebar;
@@ -116,6 +116,7 @@ namespace AtomSampleViewer
         static constexpr int s_shadowmapSizeIndexDefault = 3;
         static constexpr int s_cascadesCountDefault = 4;
         static constexpr float s_ratioLogarithmUniformDefault = 0.8f;
+        bool m_dirShadowEnabled = true;
         int m_directionalLightShadowmapSizeIndex = 0;
         int m_cascadeCount = 0;
         float m_ratioLogarithmUniform = 0.f;

+ 3 - 3
Gem/Code/Source/DecalExampleComponent.cpp

@@ -29,7 +29,7 @@ namespace AtomSampleViewer
 {
     namespace
     {
-        static constexpr const char* TargetMeshName = "objects/plane.azmodel";
+        static constexpr const char* TargetMeshName = "objects/plane.fbx.azmodel";
         static constexpr const char* TargetMaterialName = "materials/defaultpbr.azmaterial";
     }
 
@@ -54,7 +54,7 @@ namespace AtomSampleViewer
 
         // List of all assets this example needs.
         AZStd::vector<AZ::AssetCollectionAsyncLoader::AssetToLoadInfo> assetList = {
-            {"objects/plane.azmodel", azrtti_typeid<AZ::RPI::ModelAsset>()}, // The model
+            { TargetMeshName, azrtti_typeid<AZ::RPI::ModelAsset>() }, // The model
         };
 
         ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::PauseScript);
@@ -79,7 +79,7 @@ namespace AtomSampleViewer
     {
         const auto meshAsset = m_assetLoadManager.GetAsset<AZ::RPI::ModelAsset>(TargetMeshName);
         const auto materialAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::MaterialAsset>(TargetMaterialName, AZ::RPI::AssetUtils::TraceLevel::Assert);
-        m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor{ meshAsset }, AZ::RPI::Material::FindOrCreate(materialAsset));
+        m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor(meshAsset, AZ::RPI::Material::FindOrCreate(materialAsset)));
         ScaleObjectToFitDecals();
     }
 

+ 2 - 2
Gem/Code/Source/DepthOfFieldExampleComponent.cpp

@@ -44,7 +44,7 @@ namespace AtomSampleViewer
         m_directionalLightFeatureProcessor = m_scene->GetFeatureProcessor<Render::DirectionalLightFeatureProcessorInterface>();
 
         // Create the assets
-        m_bunnyModelAsset = RPI::AssetUtils::GetAssetByProductPath<RPI::ModelAsset>("objects/bunny.azmodel", RPI::AssetUtils::TraceLevel::Assert);
+        m_bunnyModelAsset = RPI::AssetUtils::GetAssetByProductPath<RPI::ModelAsset>("objects/bunny.fbx.azmodel", RPI::AssetUtils::TraceLevel::Assert);
         m_materialAsset = RPI::AssetUtils::GetAssetByProductPath<RPI::MaterialAsset>(DefaultPbrMaterialPath, RPI::AssetUtils::TraceLevel::Assert);
 
         CreateMeshes();
@@ -192,7 +192,7 @@ namespace AtomSampleViewer
 
         for (MeshHandle& meshHandle : m_meshHandles)
         {
-            meshHandle = GetMeshFeatureProcessor()->AcquireMesh(Render::MeshHandleDescriptor{ m_bunnyModelAsset }, materialInstance);
+            meshHandle = GetMeshFeatureProcessor()->AcquireMesh(Render::MeshHandleDescriptor(m_bunnyModelAsset, materialInstance));
 
             auto transform = AZ::Transform::CreateTranslation(translation);
             transform *= scaleTransform;

+ 23 - 21
Gem/Code/Source/DiffuseGIExampleComponent.cpp

@@ -157,11 +157,11 @@ namespace AtomSampleViewer
     {
         // load plane and cube models
         // all geometry in the CornellBox is created from planes and boxes
-        static constexpr const char PlaneModelPath[] = "objects/plane.azmodel";
+        static constexpr const char PlaneModelPath[] = "objects/plane.fbx.azmodel";
         AZ::Data::AssetId planeAssetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(PlaneModelPath, AZ::RPI::AssetUtils::TraceLevel::Error);
         m_planeModelAsset.Create(planeAssetId);
 
-        static constexpr const char CubeModelPath[] = "objects/cube.azmodel";
+        static constexpr const char CubeModelPath[] = "objects/cube.fbx.azmodel";
         AZ::Data::AssetId cubeAssetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(CubeModelPath, AZ::RPI::AssetUtils::TraceLevel::Error);
         m_cubeModelAsset.Create(cubeAssetId);
 
@@ -187,7 +187,7 @@ namespace AtomSampleViewer
         m_whiteMaterialAsset.Create(whiteMaterialAssetId);
 
         // Sponza models
-        static constexpr const char InteriorModelPath[] = "objects/sponza.azmodel";
+        static constexpr const char InteriorModelPath[] = "objects/sponza.fbx.azmodel";
         AZ::Data::AssetId interiorAssetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(InteriorModelPath, AZ::RPI::AssetUtils::TraceLevel::Error);
         m_sponzaModelAsset.Create(interiorAssetId);
 
@@ -231,9 +231,9 @@ namespace AtomSampleViewer
             transform.SetTranslation(-5.0f, 0.0f, 0.0f);
             transform *= AZ::Transform::CreateRotationY(AZ::Constants::HalfPi);
             AZ::Vector3 nonUniformScale(10.05f, 10.05f, 1.0f);
-            m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::LeftWall)] = GetMeshFeatureProcessor()->AcquireMesh(
-                AZ::Render::MeshHandleDescriptor{ m_planeModelAsset },
-                AZ::RPI::Material::FindOrCreate(GetCornellBoxMaterialAsset(m_leftWallColor)));
+            m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::LeftWall)] =
+                GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor(
+                    m_planeModelAsset, AZ::RPI::Material::FindOrCreate(GetCornellBoxMaterialAsset(m_leftWallColor))));
             GetMeshFeatureProcessor()->SetTransform(
                 m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::LeftWall)], transform, nonUniformScale);
         }
@@ -245,9 +245,9 @@ namespace AtomSampleViewer
             transform.SetTranslation(5.0f, 0.0f, 0.0f);
             transform *= AZ::Transform::CreateRotationY(-AZ::Constants::HalfPi);
             AZ::Vector3 nonUniformScale(10.05f, 10.05f, 1.0f);
-            m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::RightWall)] = GetMeshFeatureProcessor()->AcquireMesh(
-                AZ::Render::MeshHandleDescriptor{ m_planeModelAsset },
-                AZ::RPI::Material::FindOrCreate(GetCornellBoxMaterialAsset(m_rightWallColor)));
+            m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::RightWall)] =
+                GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor(
+                    m_planeModelAsset, AZ::RPI::Material::FindOrCreate(GetCornellBoxMaterialAsset(m_rightWallColor))));
             GetMeshFeatureProcessor()->SetTransform(
                 m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::RightWall)], transform, nonUniformScale);
         }
@@ -259,9 +259,9 @@ namespace AtomSampleViewer
             transform.SetTranslation(0.0f, 5.0f, 0.0f);
             transform *= AZ::Transform::CreateRotationX(AZ::Constants::HalfPi);
             AZ::Vector3 nonUniformScale(10.05f, 10.05f, 1.0f);
-            m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::BackWall)] = GetMeshFeatureProcessor()->AcquireMesh(
-                AZ::Render::MeshHandleDescriptor{ m_planeModelAsset },
-                AZ::RPI::Material::FindOrCreate(GetCornellBoxMaterialAsset(m_backWallColor)));
+            m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::BackWall)] =
+                GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor(
+                    m_planeModelAsset, AZ::RPI::Material::FindOrCreate(GetCornellBoxMaterialAsset(m_backWallColor))));
             GetMeshFeatureProcessor()->SetTransform(
                 m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::BackWall)], transform, nonUniformScale);
         }
@@ -273,9 +273,9 @@ namespace AtomSampleViewer
             transform.SetTranslation(0.0f, 0.0f, 5.0f);
             transform *= AZ::Transform::CreateRotationX(AZ::Constants::Pi);
             AZ::Vector3 nonUniformScale(10.05f, 10.05f, 1.0f);
-            m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::Ceiling)] = GetMeshFeatureProcessor()->AcquireMesh(
-                AZ::Render::MeshHandleDescriptor{ m_planeModelAsset },
-                AZ::RPI::Material::FindOrCreate(GetCornellBoxMaterialAsset(m_ceilingColor)));
+            m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::Ceiling)] =
+                GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor(
+                    m_planeModelAsset, AZ::RPI::Material::FindOrCreate(GetCornellBoxMaterialAsset(m_ceilingColor))));
             GetMeshFeatureProcessor()->SetTransform(
                 m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::Ceiling)], transform, nonUniformScale);
         }
@@ -286,9 +286,9 @@ namespace AtomSampleViewer
             AZ::Transform transform = AZ::Transform::CreateIdentity();
             transform.SetTranslation(0.0f, 0.0f, -5.0f);
             AZ::Vector3 nonUniformScale(10.05f, 10.05f, 1.0f);
-            m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::Floor)] = GetMeshFeatureProcessor()->AcquireMesh(
-                AZ::Render::MeshHandleDescriptor{ m_planeModelAsset },
-                AZ::RPI::Material::FindOrCreate(GetCornellBoxMaterialAsset(m_floorColor)));
+            m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::Floor)] =
+                GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor(
+                    m_planeModelAsset, AZ::RPI::Material::FindOrCreate(GetCornellBoxMaterialAsset(m_floorColor))));
             GetMeshFeatureProcessor()->SetTransform(
                 m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::Floor)], transform, nonUniformScale);
         }
@@ -300,7 +300,7 @@ namespace AtomSampleViewer
             transform *= AZ::Transform::CreateRotationZ(AZ::Constants::HalfPi * 0.2f);
             AZ::Vector3 nonUniformScale(3.0f, 3.0f, 6.0f);
             m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::LargeBox)] = GetMeshFeatureProcessor()->AcquireMesh(
-                AZ::Render::MeshHandleDescriptor{ m_cubeModelAsset }, AZ::RPI::Material::FindOrCreate(m_whiteMaterialAsset));
+                AZ::Render::MeshHandleDescriptor(m_cubeModelAsset, AZ::RPI::Material::FindOrCreate(m_whiteMaterialAsset)));
             GetMeshFeatureProcessor()->SetTransform(
                 m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::LargeBox)], transform, nonUniformScale);
         }
@@ -312,7 +312,7 @@ namespace AtomSampleViewer
             transform *= AZ::Transform::CreateRotationZ(-AZ::Constants::HalfPi * 0.2f);
             AZ::Vector3 nonUniformScale(3.0f, 3.0f, 3.0f);
             m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::SmallBox)] = GetMeshFeatureProcessor()->AcquireMesh(
-                AZ::Render::MeshHandleDescriptor{ m_cubeModelAsset }, AZ::RPI::Material::FindOrCreate(m_whiteMaterialAsset));
+                AZ::Render::MeshHandleDescriptor(m_cubeModelAsset, AZ::RPI::Material::FindOrCreate(m_whiteMaterialAsset)));
             GetMeshFeatureProcessor()->SetTransform(
                 m_meshHandles[aznumeric_cast<uint32_t>(CornellBoxMeshes::SmallBox)], transform, nonUniformScale);
         }
@@ -377,7 +377,8 @@ namespace AtomSampleViewer
         m_meshHandles.resize(aznumeric_cast<uint32_t>(SponzaMeshes::Count));
 
         AZ::Transform transform = AZ::Transform::CreateIdentity();
-        m_meshHandles[aznumeric_cast<uint32_t>(SponzaMeshes::Inside)] = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor{ m_sponzaModelAsset });
+        m_meshHandles[aznumeric_cast<uint32_t>(SponzaMeshes::Inside)] =
+            GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor(m_sponzaModelAsset));
         GetMeshFeatureProcessor()->SetTransform(m_meshHandles[aznumeric_cast<uint32_t>(SponzaMeshes::Inside)], transform);
         
         m_directionalLightPitch = AZ::DegToRad(-65.0f);
@@ -398,6 +399,7 @@ namespace AtomSampleViewer
             const auto lightTransform = AZ::Transform::CreateRotationZ(m_directionalLightYaw) * AZ::Transform::CreateRotationX(m_directionalLightPitch);
             directionalLightFeatureProcessor->SetDirection(m_directionalLightHandle, lightTransform.GetBasis(1));
             directionalLightFeatureProcessor->SetRgbIntensity(m_directionalLightHandle, AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Lux>(m_directionalLightIntensity * m_directionalLightColor));
+            directionalLightFeatureProcessor->SetShadowEnabled(m_directionalLightHandle, true);
             directionalLightFeatureProcessor->SetCascadeCount(m_directionalLightHandle, 4);
             directionalLightFeatureProcessor->SetShadowmapSize(m_directionalLightHandle, AZ::Render::ShadowmapSize::Size2048);
             directionalLightFeatureProcessor->SetViewFrustumCorrectionEnabled(m_directionalLightHandle, false);

+ 9 - 19
Gem/Code/Source/DynamicMaterialTestComponent.cpp

@@ -71,7 +71,7 @@ namespace AtomSampleViewer
     
     void DynamicMaterialTestComponent::PrepareCreateLatticeInstances(uint32_t instanceCount)
     {
-        const char* modelPath = "objects/shaderball_simple.azmodel";
+        const char* modelPath = "objects/shaderball_simple.fbx.azmodel";
 
         Data::AssetId modelAssetId;
         Data::AssetCatalogRequestBus::BroadcastResult(
@@ -95,26 +95,17 @@ namespace AtomSampleViewer
         AZ::Data::Asset<AZ::RPI::MaterialAsset>& materialAsset = m_materialConfigs[m_currentMaterialConfig].m_materialAsset;
         AZ::Data::Instance<AZ::RPI::Material> material = Material::Create(materialAsset);
          
-        Render::MeshHandleDescriptor meshDescriptor;
-        meshDescriptor.m_modelAsset = m_modelAsset;
+        Render::MeshHandleDescriptor meshDescriptor(m_modelAsset, material);
         meshDescriptor.m_isRayTracingEnabled = false;
-        auto meshHandle = GetMeshFeatureProcessor()->AcquireMesh(meshDescriptor, material);
+        meshDescriptor.m_modelChangedEventHandler =
+            AZ::Render::MeshHandleDescriptor::ModelChangedEvent::Handler{ [this](const AZ::Data::Instance<AZ::RPI::Model>& /*model*/)
+                                                                          {
+                                                                              m_loadedMeshCounter++;
+                                                                          } };
+
+        auto meshHandle = GetMeshFeatureProcessor()->AcquireMesh(meshDescriptor);
         GetMeshFeatureProcessor()->SetTransform(meshHandle, transform);
 
-        Data::Instance<RPI::Model> model = GetMeshFeatureProcessor()->GetModel(meshHandle);
-        if (model)
-        {
-            m_loadedMeshCounter++;
-        }
-        else
-        {
-            m_meshLoadEventHandlers.push_back(AZ::Render::MeshFeatureProcessorInterface::ModelChangedEvent::Handler
-                {
-                    [this](AZ::Data::Instance<AZ::RPI::Model> model) { m_loadedMeshCounter++; }
-                });
-            GetMeshFeatureProcessor()->ConnectModelChangeEventHandler(meshHandle, m_meshLoadEventHandlers.back());
-        }
-        
         m_meshHandles.push_back(AZStd::move(meshHandle));
         m_materials.push_back(material);
     }
@@ -130,7 +121,6 @@ namespace AtomSampleViewer
 
         m_loadedMeshCounter = 0;
         m_waitingForMeshes = false;
-        m_meshLoadEventHandlers.clear();
     }
 
     void DynamicMaterialTestComponent::InitMaterialConfigs()

+ 0 - 2
Gem/Code/Source/DynamicMaterialTestComponent.h

@@ -76,8 +76,6 @@ namespace AtomSampleViewer
 
         bool m_waitingForMeshes = false;
         uint32_t m_loadedMeshCounter = 0;
-        AZStd::vector<AZ::Render::MeshFeatureProcessorInterface::ModelChangedEvent::Handler> m_meshLoadEventHandlers;
-
         float m_currentTime = 0.0f;
     };
 } // namespace AtomSampleViewer

+ 16 - 16
Gem/Code/Source/ExposureExampleComponent.cpp

@@ -112,25 +112,25 @@ namespace AtomSampleViewer
     {
         using namespace AZ;
 
-        const char* sponzaPath = "objects/sponza.azmodel";
-        Data::Asset<RPI::ModelAsset> modelAsset = RPI::AssetUtils::GetAssetByProductPath<RPI::ModelAsset>(sponzaPath, RPI::AssetUtils::TraceLevel::Assert);
-        Data::Asset<RPI::MaterialAsset> materialAsset = RPI::AssetUtils::GetAssetByProductPath<RPI::MaterialAsset>(DefaultPbrMaterialPath, RPI::AssetUtils::TraceLevel::Assert);
-        m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(Render::MeshHandleDescriptor{ modelAsset }, AZ::RPI::Material::FindOrCreate(materialAsset));
-
+        const char* sponzaPath = "objects/sponza.fbx.azmodel";
+        Data::Asset<RPI::ModelAsset> modelAsset =
+            RPI::AssetUtils::GetAssetByProductPath<RPI::ModelAsset>(sponzaPath, RPI::AssetUtils::TraceLevel::Assert);
+        Data::Asset<RPI::MaterialAsset> materialAsset =
+            RPI::AssetUtils::GetAssetByProductPath<RPI::MaterialAsset>(DefaultPbrMaterialPath, RPI::AssetUtils::TraceLevel::Assert);
+
+        Render::MeshHandleDescriptor descriptor(modelAsset, AZ::RPI::Material::FindOrCreate(materialAsset));
+        descriptor.m_modelChangedEventHandler =
+            AZ::Render::MeshHandleDescriptor::ModelChangedEvent::Handler{ [this](const AZ::Data::Instance<AZ::RPI::Model>& model)
+                                                                          {
+                                                                              OnModelReady(model);
+                                                                          } };
+
+        m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(descriptor);
+ 
         // rotate the entity 180 degrees about Z (the vertical axis)
         // This makes it consistent with how it was positioned in the world when the world was Y-up.
         GetMeshFeatureProcessor()->SetTransform(m_meshHandle, Transform::CreateRotationZ(AZ::Constants::Pi));
 
-        Data::Instance<RPI::Model> model = GetMeshFeatureProcessor()->GetModel(m_meshHandle);
-        if (model)
-        {
-            OnModelReady(model);
-        }
-        else
-        {
-            GetMeshFeatureProcessor()->ConnectModelChangeEventHandler(m_meshHandle, m_meshChangedHandler);
-        }
-
         SetupLights();
     }
 
@@ -256,7 +256,7 @@ namespace AtomSampleViewer
 
 
                 float minimumExposure = m_exposureControlSettings->GetEyeAdaptationExposureMin();
-                if (ImGui::SliderFloat("Minumum Exposure", &minimumExposure, -16.0f, 16.0f, "%0.4f") || !m_isInitParameters)
+                if (ImGui::SliderFloat("Minimum Exposure", &minimumExposure, -16.0f, 16.0f, "%0.4f") || !m_isInitParameters)
                 {
                     m_exposureControlSettings->SetEyeAdaptationExposureMin(minimumExposure);
                     m_exposureControlSettings->OnConfigChanged();

+ 1 - 4
Gem/Code/Source/ExposureExampleComponent.h

@@ -73,10 +73,7 @@ namespace AtomSampleViewer
 
         // model
         AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_meshHandle;
-        AZ::Render::MeshFeatureProcessorInterface::ModelChangedEvent::Handler m_meshChangedHandler
-        {
-            [&](AZ::Data::Instance<AZ::RPI::Model> model) { OnModelReady(model); }
-        };
+
         bool m_sponzaAssetLoaded = false;
 
         // feature processors

+ 6 - 4
Gem/Code/Source/EyeMaterialExampleComponent.cpp

@@ -19,9 +19,9 @@
 
 namespace AtomSampleViewer
 {
-    static const char* MeshPath = "objects/eye.azmodel";
+    static const char* MeshPath = "objects/eye.fbx.azmodel";
     static const char* MaterialPath = "materials/eye/001_EyeBasic.azmaterial";
-    static const float DefaultCameraHeading = 40.0f;
+    static const float DefaultCameraHeadingDegrees = 129.6f;
     static const float DefaultCameraDistance = 2.0f;
     
     static const char* IrisColorName = "iris.baseColor.color";
@@ -92,7 +92,7 @@ namespace AtomSampleViewer
     void EyeMaterialExampleComponent::LoadMesh(AZ::Transform transform)
     {
         m_materialInstance = AZ::RPI::Material::Create(m_materialAsset);
-        m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor{ m_modelAsset }, m_materialInstance);
+        m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor(m_modelAsset, m_materialInstance));
         GetMeshFeatureProcessor()->SetTransform(m_meshHandle, transform);
     }
 
@@ -104,7 +104,9 @@ namespace AtomSampleViewer
             &AZ::Debug::CameraControllerRequestBus::Events::Enable,
             azrtti_typeid<AZ::Debug::ArcBallControllerComponent>());
         AZ::Debug::ArcBallControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::ArcBallControllerRequestBus::Events::SetDistance, DefaultCameraDistance);
-        AZ::Debug::ArcBallControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::ArcBallControllerRequestBus::Events::SetHeading, DefaultCameraHeading);
+        const float headingRadians = AZ::DegToRad(DefaultCameraHeadingDegrees);
+        AZ::Debug::ArcBallControllerRequestBus::Event(
+            GetCameraEntityId(), &AZ::Debug::ArcBallControllerRequestBus::Events::SetHeading, headingRadians);
 
         // Lighting
         m_defaultIbl.Init(m_scene);

+ 15 - 18
Gem/Code/Source/LightCullingExampleComponent.cpp

@@ -44,9 +44,9 @@ namespace AtomSampleViewer
     using namespace AZ::Render;
     using namespace AZ::RPI;
 
-    static const char* WorldModelName = "Objects/Sponza.azmodel";
+    static const char* WorldModelName = "Objects/Sponza.fbx.azmodel";
 
-    static const char* TransparentModelName = "Objects/ShaderBall_simple.azmodel";
+    static const char* TransparentModelName = "Objects/ShaderBall_simple.fbx.azmodel";
     static const char* TransparentMaterialName = "materials/DefaultPBRTransparent.azmaterial";
 
     static const char* DecalMaterialPath = "materials/Decal/airship_tail_01_decal.azmaterial";
@@ -204,7 +204,6 @@ namespace AtomSampleViewer
 
     void LightCullingExampleComponent::OnModelReady(AZ::Data::Instance<AZ::RPI::Model> model)
     {
-        m_meshChangedHandler.Disconnect();
         m_worldModelAssetLoaded = true;
         m_worldModelAABB = model->GetModelAsset()->GetAabb();
 
@@ -755,22 +754,19 @@ namespace AtomSampleViewer
 
     void LightCullingExampleComponent::CreateOpaqueModels()
     {
-        Data::Asset<RPI::ModelAsset> modelAsset = RPI::AssetUtils::GetAssetByProductPath<RPI::ModelAsset>(WorldModelName, RPI::AssetUtils::TraceLevel::Assert);
+        Data::Asset<RPI::ModelAsset> modelAsset =
+            RPI::AssetUtils::GetAssetByProductPath<RPI::ModelAsset>(WorldModelName, RPI::AssetUtils::TraceLevel::Assert);
 
-        auto meshFeatureProcessor = GetMeshFeatureProcessor();
+        Render::MeshHandleDescriptor descriptor(modelAsset);
+        descriptor.m_modelChangedEventHandler =
+            AZ::Render::MeshHandleDescriptor::ModelChangedEvent::Handler{ [this](const AZ::Data::Instance<AZ::RPI::Model>& model)
+                                                                          {
+                                                                              OnModelReady(model);
+                                                                          } };
 
-        m_meshHandle = meshFeatureProcessor->AcquireMesh(MeshHandleDescriptor{ modelAsset });
-        meshFeatureProcessor->SetTransform(m_meshHandle, Transform::CreateIdentity());
-        Data::Instance<RPI::Model> model = meshFeatureProcessor->GetModel(m_meshHandle);
-        // Loading in the world will probably take a while and I want to grab the AABB afterwards, so hook it up to a a ModelChangeEventHandler
-        if (model)
-        {
-            OnModelReady(model);
-        }
-        else
-        {
-            meshFeatureProcessor->ConnectModelChangeEventHandler(m_meshHandle, m_meshChangedHandler);
-        }
+        m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(descriptor);
+
+        GetMeshFeatureProcessor()->SetTransform(m_meshHandle, Transform::CreateIdentity());
     }
 
     void LightCullingExampleComponent::CreateTransparentModels()
@@ -787,7 +783,8 @@ namespace AtomSampleViewer
 
         for (const AZ::Vector3& position : TransparentModelPositions)
         {
-            AZ::Render::MeshFeatureProcessorInterface::MeshHandle meshHandle = GetMeshFeatureProcessor()->AcquireMesh(MeshHandleDescriptor{ transparentModelAsset }, materialInstance);
+            AZ::Render::MeshFeatureProcessorInterface::MeshHandle meshHandle =
+                GetMeshFeatureProcessor()->AcquireMesh(MeshHandleDescriptor(transparentModelAsset, materialInstance));
             GetMeshFeatureProcessor()->SetTransform(meshHandle, Transform::CreateTranslation(position));
             m_transparentMeshHandles.push_back(std::move(meshHandle));
         }

+ 1 - 4
Gem/Code/Source/LightCullingExampleComponent.h

@@ -190,10 +190,7 @@ namespace AtomSampleViewer
         float m_originalFarClipDistance = 0.f;
 
         AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_meshHandle;
-        AZ::Render::MeshFeatureProcessorInterface::ModelChangedEvent::Handler m_meshChangedHandler
-        {
-            [&](AZ::Data::Instance<AZ::RPI::Model> model) { OnModelReady(model); }
-        };
+
         bool m_worldModelAssetLoaded = false;
 
         AZ::Aabb m_worldModelAABB;

+ 15 - 18
Gem/Code/Source/MSAA_RPI_ExampleComponent.cpp

@@ -98,7 +98,7 @@ namespace AtomSampleViewer
         if (isNonMsaaPipeline != m_isNonMsaaPipeline)
         {      
             // set the number of MSAA samples and reset the RPI scene
-            SampleComponentManagerRequestBus::Broadcast(&SampleComponentManagerRequests::SetNumMSAASamples, m_numSamples);
+            SampleComponentManagerRequestBus::Broadcast(&SampleComponentManagerRequests::SetNumMSAASamples, static_cast<uint16_t>(m_numSamples));
             SampleComponentManagerRequestBus::Broadcast(&SampleComponentManagerRequests::ResetRPIScene);
 
             // reset internal sample scene related data
@@ -160,38 +160,35 @@ namespace AtomSampleViewer
         switch (m_modelType)
         {
         case 0:
-            return AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/cylinder.azmodel", traceLevel);
+            return AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/cylinder.fbx.azmodel", traceLevel);
         case 1:
-            return AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/cube.azmodel", traceLevel);
+            return AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/cube.fbx.azmodel", traceLevel);
         case 2:
-            return AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/shaderball_simple.azmodel", traceLevel);
+            return AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/shaderball_simple.fbx.azmodel", traceLevel);
         }
 
         AZ_Warning("MSAA_RPI_ExampleComponent", false, "Unsupported model type");
-        return AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/cylinder.azmodel", traceLevel);
+        return AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/cylinder.fbx.azmodel", traceLevel);
     }
 
     void MSAA_RPI_ExampleComponent::ActivateModel()
     {
-        m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor{ GetModelAsset() }, AZ::RPI::Material::FindOrCreate(GetMaterialAsset()));
-        GetMeshFeatureProcessor()->SetTransform(m_meshHandle, AZ::Transform::CreateIdentity());
+        ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::PauseScript);
 
-        AZ::Data::Instance<AZ::RPI::Model> model = GetMeshFeatureProcessor()->GetModel(m_meshHandle);
-        if (model)
-        {
-            OnModelReady(model);
-        }
-        else
-        {
-            ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::PauseScript);
-            GetMeshFeatureProcessor()->ConnectModelChangeEventHandler(m_meshHandle, m_meshChangedHandler);
-        }
+        AZ::Render::MeshHandleDescriptor descriptor(GetModelAsset(), AZ::RPI::Material::FindOrCreate(GetMaterialAsset()));
+        descriptor.m_modelChangedEventHandler =
+            AZ::Render::MeshHandleDescriptor::ModelChangedEvent::Handler{ [this](const AZ::Data::Instance<AZ::RPI::Model>& model)
+                                                                          {
+                                                                              OnModelReady(model);
+                                                                          } };
+
+        m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(descriptor);
+        GetMeshFeatureProcessor()->SetTransform(m_meshHandle, AZ::Transform::CreateIdentity());
     }
 
     void MSAA_RPI_ExampleComponent::OnModelReady(AZ::Data::Instance<AZ::RPI::Model> model)
     {
         AZ::Data::Asset<AZ::RPI::ModelAsset> modelAsset = model->GetModelAsset();
-        m_meshChangedHandler.Disconnect();
         ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::ResumeScript);
     }
 

+ 0 - 7
Gem/Code/Source/MSAA_RPI_ExampleComponent.h

@@ -63,13 +63,6 @@ namespace AtomSampleViewer
         // sample mesh
         int m_modelType = 0;
         AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_meshHandle;
-        AZ::Render::MeshFeatureProcessorInterface::ModelChangedEvent::Handler m_meshChangedHandler
-        {
-            [&](AZ::Data::Instance<AZ::RPI::Model> model)
-            {
-                OnModelReady(model);
-            }
-        };
 
         // original render pipeline when the sample was started
         AZ::RPI::RenderPipelinePtr m_originalPipeline;

+ 29 - 20
Gem/Code/Source/MeshExampleComponent.cpp

@@ -62,19 +62,6 @@ namespace AtomSampleViewer
         , m_modelBrowser("@user@/MeshExampleComponent/model_browser.xml")
         , m_imguiSidebar("@user@/MeshExampleComponent/sidebar.xml")
     {
-        m_changedHandler = AZ::Render::MeshFeatureProcessorInterface::ModelChangedEvent::Handler
-        {
-            [&](AZ::Data::Instance<AZ::RPI::Model> model)
-            {
-                ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::ResumeScript);
-
-                // This handler will be connected to the feature processor so that when the model is updated, the camera
-                // controller will reset. This ensures the camera is a reasonable distance from the model when it resizes.
-                ResetCameraController();
-
-                UpdateGroundPlane();
-            }
-        };
     }
 
     void MeshExampleComponent::DefaultWindowCreated()
@@ -89,7 +76,10 @@ namespace AtomSampleViewer
         pipelineDesc.m_name = "LowEndPipeline";
         pipelineDesc.m_materialPipelineTag = "LowEndPipeline";
         pipelineDesc.m_rootPassTemplate = "LowEndPipelineTemplate";
-        pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 4;
+        pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 1;
+        SampleComponentManagerRequestBus::BroadcastResult(
+            pipelineDesc.m_renderSettings.m_multisampleState.m_samples,
+            &SampleComponentManagerRequests::GetNumMSAASamples);
 
         m_lowEndPipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineDesc, *m_windowContext);
     }
@@ -106,7 +96,10 @@ namespace AtomSampleViewer
         pipelineDesc.m_name = "DeferredPipeline";
         pipelineDesc.m_materialPipelineTag = "DeferredPipeline";
         pipelineDesc.m_rootPassTemplate = "DeferredPipelineTemplate";
-        pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 4;
+        pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 1;
+        SampleComponentManagerRequestBus::BroadcastResult(
+            pipelineDesc.m_renderSettings.m_multisampleState.m_samples,
+            &SampleComponentManagerRequests::GetNumMSAASamples);
         pipelineDesc.m_allowModification = true; // MainPipeline allows modifications, so the DeferredPipeline must as well, to get a consistent result.
 
         m_deferredPipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineDesc, *m_windowContext);
@@ -232,7 +225,7 @@ namespace AtomSampleViewer
 
         AZ::Data::Asset<AZ::RPI::MaterialAsset> groundPlaneMaterialAsset = AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::MaterialAsset>(DefaultPbrMaterialPath, AZ::RPI::AssetUtils::TraceLevel::Error);
         m_groundPlaneMaterial = AZ::RPI::Material::FindOrCreate(groundPlaneMaterialAsset);
-        m_groundPlaneModelAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/plane.azmodel", AZ::RPI::AssetUtils::TraceLevel::Assert);
+        m_groundPlaneModelAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/plane.fbx.azmodel", AZ::RPI::AssetUtils::TraceLevel::Assert);
 
         AZ::TickBus::Handler::BusConnect();
         AZ::Render::Bootstrap::DefaultWindowNotificationBus::Handler::BusConnect();
@@ -472,10 +465,25 @@ namespace AtomSampleViewer
 
             m_modelAsset.Create(m_modelBrowser.GetSelectedAssetId());
             GetMeshFeatureProcessor()->ReleaseMesh(m_meshHandle);
-            m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor{ m_modelAsset }, m_customMaterialInstance);
-            GetMeshFeatureProcessor()->SetTransform(m_meshHandle, AZ::Transform::CreateIdentity());
-            GetMeshFeatureProcessor()->ConnectModelChangeEventHandler(m_meshHandle, m_changedHandler);
+
+            AZ::Render::MeshHandleDescriptor descriptor(m_modelAsset, m_customMaterialInstance);
+            descriptor.m_modelChangedEventHandler = AZ::Render::MeshHandleDescriptor::ModelChangedEvent::Handler{
+                [this](const AZ::Data::Instance<AZ::RPI::Model>& /*model*/)
+                {
+                    ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::ResumeScript);
+
+                    // This handler will be connected to the feature processor so that when the model is updated, the camera
+                    // controller will reset. This ensures the camera is a reasonable distance from the model when it resizes.
+                    ResetCameraController();
+
+                    UpdateGroundPlane();
+                }
+            };
+
+            m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(descriptor);
+
             GetMeshFeatureProcessor()->SetMeshLodConfiguration(m_meshHandle, m_lodConfig);
+            GetMeshFeatureProcessor()->SetTransform(m_meshHandle, AZ::Transform::CreateIdentity());
         }
         else
         {
@@ -485,7 +493,8 @@ namespace AtomSampleViewer
     
     void MeshExampleComponent::CreateGroundPlane()
     {
-        m_groundPlandMeshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor{ m_groundPlaneModelAsset }, m_groundPlaneMaterial);
+        m_groundPlandMeshHandle =
+            GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor(m_groundPlaneModelAsset, m_groundPlaneMaterial));
     }
 
     void MeshExampleComponent::UpdateGroundPlane()

+ 0 - 2
Gem/Code/Source/MeshExampleComponent.h

@@ -101,8 +101,6 @@ namespace AtomSampleViewer
         static const uint32_t CameraControllerCount = static_cast<uint32_t>(CameraControllerType::Count);
         static const char* CameraControllerNameTable[CameraControllerCount];
         CameraControllerType m_currentCameraControllerType = CameraControllerType::ArcBall;
-
-        AZ::Render::MeshFeatureProcessorInterface::ModelChangedEvent::Handler m_changedHandler;
         
         static constexpr float ArcballRadiusMinModifier = 0.01f;
         static constexpr float ArcballRadiusMaxModifier = 4.0f;

+ 271 - 0
Gem/Code/Source/MultiGPURPIExampleComponent.cpp

@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AtomSampleViewerOptions.h>
+#include <MultiGPURPIExampleComponent.h>
+
+#include <Atom/Component/DebugCamera/CameraComponent.h>
+#include <Atom/Component/DebugCamera/NoClipControllerComponent.h>
+
+#include <Atom/RHI/RHISystemInterface.h>
+
+#include <Atom/RPI.Public/Pass/CopyPass.h>
+#include <Atom/RPI.Public/Pass/PassFilter.h>
+#include <Atom/RPI.Public/RPISystemInterface.h>
+#include <Atom/RPI.Public/RenderPipeline.h>
+#include <Atom/RPI.Public/Scene.h>
+#include <Atom/RPI.Public/ViewProviderBus.h>
+#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
+#include <Atom/RPI.Reflect/Model/ModelAsset.h>
+
+#include <Atom/Feature/ImGui/ImGuiUtils.h>
+
+#include <AzCore/Math/MatrixUtils.h>
+
+#include <Automation/ScriptableImGui.h>
+#include <Automation/ScriptRunnerBus.h>
+
+#include <AzCore/Component/Entity.h>
+
+#include <AzFramework/Components/TransformComponent.h>
+#include <AzFramework/Scene/SceneSystemInterface.h>
+#include <AzFramework/Entity/GameEntityContextComponent.h>
+
+#include <EntityUtilityFunctions.h>
+#include <SampleComponentConfig.h>
+#include <SampleComponentManager.h>
+
+#include <Utils/Utils.h>
+
+#include <RHI/BasicRHIComponent.h>
+
+namespace AtomSampleViewer
+{
+    using namespace AZ;
+
+    void MultiGPURPIExampleComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<MultiGPURPIExampleComponent, AZ::Component>()
+                ->Version(0)
+                ;
+        }
+    }
+
+    MultiGPURPIExampleComponent::MultiGPURPIExampleComponent()
+    {
+    }
+
+    void MultiGPURPIExampleComponent::Activate()
+    {
+        AZ::TickBus::Handler::BusConnect();
+
+        AZ::Render::Bootstrap::DefaultWindowNotificationBus::Handler::BusConnect();
+
+        // save original render pipeline first and remove it from the scene
+        m_originalPipeline = m_scene->GetDefaultRenderPipeline();
+        m_scene->RemoveRenderPipeline(m_originalPipeline->GetId());
+
+        // add the multi-GPU pipeline
+        const AZStd::string pipelineName("MultiGPUPipeline");
+        AZ::RPI::RenderPipelineDescriptor pipelineDesc;
+        pipelineDesc.m_name = pipelineName;
+        pipelineDesc.m_rootPassTemplate = "MultiGPUPipeline";
+        m_pipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineDesc, *m_windowContext);
+        m_scene->AddRenderPipeline(m_pipeline);
+
+        const AZStd::string copyPipelineName("MultiGPUCopyTestPipeline");
+        AZ::RPI::RenderPipelineDescriptor copyPipelineDesc;
+        copyPipelineDesc.m_name = copyPipelineName;
+        copyPipelineDesc.m_rootPassTemplate = "MultiGPUCopyTestPipeline";
+        m_copyPipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(copyPipelineDesc, *m_windowContext);
+
+        m_imguiScope = AZ::Render::ImGuiActiveContextScope::FromPass({ "MultiGPUPipeline", "ImGuiPass" });
+    }
+
+    void MultiGPURPIExampleComponent::Deactivate()
+    {
+        // remove cb pipeline before adding original pipeline.
+        if (!m_pipeline)
+        {
+            return;
+        }
+
+        m_imguiScope = {}; // restores previous ImGui context.
+
+        if (m_currentlyUsingCopyPipline)
+        {
+            m_scene->RemoveRenderPipeline(m_copyPipeline->GetId());
+        }
+        else
+        {
+            m_scene->RemoveRenderPipeline(m_pipeline->GetId());
+        }
+        m_scene->AddRenderPipeline(m_originalPipeline);
+
+        m_pipeline = nullptr;
+        m_copyPipeline = nullptr;
+        m_useCopyPipeline = false;
+        m_currentlyUsingCopyPipline = false;
+        m_migrateRight = false;
+        m_rightMigrated = false;
+        m_migrateLeft = false;
+        m_leftMigrated = false;
+
+        AZ::Render::Bootstrap::DefaultWindowNotificationBus::Handler::BusDisconnect();
+
+        AZ::TickBus::Handler::BusDisconnect();
+    }
+
+    void MultiGPURPIExampleComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint timePoint)
+    {
+        if (m_currentlyUsingCopyPipline != m_useCopyPipeline)
+        {
+            AZ::RPI::RenderPipelinePtr prevPipeline = m_scene->GetDefaultRenderPipeline();
+            if (m_useCopyPipeline)
+            {
+                m_copyPipeline->GetRootPass()->SetEnabled(true);
+                m_scene->AddRenderPipeline(m_copyPipeline);
+                m_scene->RemoveRenderPipeline(prevPipeline->GetId());
+
+                m_imguiScope = {};
+                m_imguiScope = AZ::Render::ImGuiActiveContextScope::FromPass({ m_copyPipeline->GetId().GetCStr(), "ImGuiPass" });
+            }
+            else
+            {
+                m_pipeline->GetRootPass()->SetEnabled(true);
+                m_scene->AddRenderPipeline(m_pipeline);
+                m_scene->RemoveRenderPipeline(prevPipeline->GetId());
+
+                m_imguiScope = {};
+                m_imguiScope = AZ::Render::ImGuiActiveContextScope::FromPass({ m_pipeline->GetId().GetCStr(), "ImGuiPass" });
+            }
+            m_currentlyUsingCopyPipline = m_useCopyPipeline;
+        }
+
+        if (m_rightMigrated != m_migrateRight)
+        {
+            AZ::RPI::PassFilter trianglePassFilter = AZ::RPI::PassFilter::CreateWithPassName(Name("TrianglePass2"), m_scene);
+            AZ::RPI::PassFilter copyPassFilter = AZ::RPI::PassFilter::CreateWithPassName(Name("CopyPass"), m_scene);
+            AZ::RPI::PassFilter compositePassFilter = AZ::RPI::PassFilter::CreateWithPassName(Name("CompositePass"), m_scene);
+
+            RPI::RenderPass* trianglePass =
+                azrtti_cast<RPI::RenderPass*>(AZ::RPI::PassSystemInterface::Get()->FindFirstPass(trianglePassFilter));
+            RPI::Pass* copyPass = AZ::RPI::PassSystemInterface::Get()->FindFirstPass(copyPassFilter);
+            RPI::Pass* compositePass = AZ::RPI::PassSystemInterface::Get()->FindFirstPass(compositePassFilter);
+            AZ_Assert(trianglePass && copyPass && compositePass, "Couldn't find passes");
+
+            if (m_migrateRight)
+            {
+                trianglePass->SetDeviceIndex(0);
+                copyPass->SetEnabled(false);
+                compositePass->ChangeConnection(Name("Input2"), trianglePass, Name("Output"));
+            }
+            else
+            {
+                trianglePass->SetDeviceIndex(1);
+                copyPass->SetEnabled(true);
+                compositePass->ChangeConnection(Name("Input2"), copyPass, Name("Output"));
+            }
+
+            m_rightMigrated = m_migrateRight;
+        }
+
+        if (m_leftMigrated != m_migrateLeft)
+        {
+            AZ::RPI::PassFilter trianglePassFilter = AZ::RPI::PassFilter::CreateWithPassName(Name("TrianglePass1"), m_scene);
+            AZ::RPI::PassFilter compositePassFilter = AZ::RPI::PassFilter::CreateWithPassName(Name("CompositePass"), m_scene);
+
+            RPI::RenderPass* trianglePass =
+                azrtti_cast<RPI::RenderPass*>(AZ::RPI::PassSystemInterface::Get()->FindFirstPass(trianglePassFilter));
+            RPI::Pass* compositePass = AZ::RPI::PassSystemInterface::Get()->FindFirstPass(compositePassFilter);
+            AZ_Assert(trianglePass && compositePass, "Couldn't find passes");
+
+            if (m_migrateLeft)
+            {
+                trianglePass->SetDeviceIndex(1);
+
+                AZStd::shared_ptr<RPI::PassRequest> passRequest = AZStd::make_shared<RPI::PassRequest>();
+                passRequest->m_templateName = Name("CopyPassTemplate");
+                passRequest->m_passName = Name("CopyPassLeft");
+
+                AZStd::shared_ptr<RPI::CopyPassData> passData = AZStd::make_shared<RPI::CopyPassData>();
+                passData->m_sourceDeviceIndex = 1;
+                passData->m_destinationDeviceIndex = 0;
+                passRequest->m_passData = passData;
+
+                RPI::PassConnection passConnection;
+                passConnection.m_localSlot = Name{ "Input" };
+                passConnection.m_attachmentRef.m_pass = Name{ "TrianglePass1" };
+                passConnection.m_attachmentRef.m_attachment = Name{ "Output" };
+                passRequest->m_connections.emplace_back(passConnection);
+
+                RPI::PassDescriptor descriptor;
+                descriptor.m_passData = passData;
+                descriptor.m_passRequest = passRequest;
+                descriptor.m_passName = passRequest->m_passName;
+
+                auto copyPass = RPI::PassSystemInterface::Get()->CreatePassFromRequest(passRequest.get());
+
+                m_pipeline->AddPassAfter(copyPass, passConnection.m_attachmentRef.m_pass);
+
+                compositePass->ChangeConnection(Name("Input1"), copyPass.get(), Name("Output"));
+            }
+            else
+            {
+                trianglePass->SetDeviceIndex(0);
+
+                compositePass->ChangeConnection(Name("Input1"), trianglePass, Name("Output"));
+
+                AZ::RPI::PassFilter copyPassFilter = AZ::RPI::PassFilter::CreateWithPassName(Name("CopyPassLeft"), m_scene);
+                RHI::Ptr<RPI::Pass> copyPass = AZ::RPI::PassSystemInterface::Get()->FindFirstPass(copyPassFilter);
+                AZ_Assert(copyPass.get(), "Couldn't find copy pass");
+
+                copyPass->QueueForRemoval();
+            }
+
+            m_leftMigrated = m_migrateLeft;
+        }
+
+        if (m_imguiSidebar.Begin())
+        {
+            ImGui::Spacing();
+            ImGui::Checkbox("Use copy test pipeline", &m_useCopyPipeline);
+            if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
+            {
+                ImGui::SetTooltip("Add additional device to device copy passes to test the modes of the copy pass:\n"
+                                  "image to buffer\n"
+                                  "buffer to buffer\n"
+                                  "buffer to image\n"
+                                  "image to image\n");
+            }
+            if (!m_useCopyPipeline)
+            {
+                ImGui::Checkbox("Migrate right half (1 -> 0)", &m_migrateRight);
+                if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
+                {
+                    ImGui::SetTooltip("Migrate right half to the first GPU (default on second).");
+                }
+
+                ImGui::Checkbox("Migrate left half (0 -> 1)", &m_migrateLeft);
+                if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
+                {
+                    ImGui::SetTooltip("Migrate left half to the second GPU (default on first).");
+                }
+            }
+            m_imguiSidebar.End();
+        }
+    }
+
+    void MultiGPURPIExampleComponent::DefaultWindowCreated()
+    {
+        AZ::Render::Bootstrap::DefaultWindowBus::BroadcastResult(m_windowContext,
+                                                                 &AZ::Render::Bootstrap::DefaultWindowBus::Events::GetDefaultWindowContext);
+    }
+} // namespace AtomSampleViewer

+ 83 - 0
Gem/Code/Source/MultiGPURPIExampleComponent.h

@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <CommonSampleComponentBase.h>
+
+#include <Atom/Bootstrap/DefaultWindowBus.h>
+#include <Atom/Feature/ImGui/ImGuiUtils.h>
+
+#include <Atom/RPI.Public/Base.h>
+#include <Atom/RPI.Public/WindowContext.h>
+
+#include <AzCore/Asset/AssetCommon.h>
+#include <AzCore/Component/TickBus.h>
+
+#include <AzFramework/Windowing/WindowBus.h>
+#include <AzFramework/Windowing/NativeWindow.h>
+
+#include <Atom/Feature/CoreLights/DirectionalLightFeatureProcessorInterface.h>
+#include <Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h>
+#include <Atom/Feature/CoreLights/ShadowConstants.h>
+#include <Atom/Feature/SkyBox/SkyBoxFeatureProcessorInterface.h>
+#include <Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h>
+
+#include <Utils/ImGuiSidebar.h>
+#include <Utils/Utils.h>
+
+struct ImGuiContext;
+
+namespace AtomSampleViewer
+{
+    //! A sample component which render the same scene with different render pipelines in different windows
+    //! It has a imgui menu to switch on/off the second render pipeline as well as turn on/off different graphics features
+    //! There is also an option to have the second render pipeline to use the second camera. 
+    class MultiGPURPIExampleComponent final
+        : public CommonSampleComponentBase
+        , public AZ::TickBus::Handler
+        , public AZ::Render::Bootstrap::DefaultWindowNotificationBus::Handler
+    {
+    public:
+        AZ_COMPONENT(MultiGPURPIExampleComponent, "{F7DD0D21-A0EF-4B66-98FA-2DB6B19A8C35}", CommonSampleComponentBase);
+
+        static void Reflect(AZ::ReflectContext* context);
+
+        MultiGPURPIExampleComponent();
+        ~MultiGPURPIExampleComponent() final = default;
+
+        // AZ::Component
+        void Activate() override;
+        void Deactivate() override;
+        
+    private:
+        // AZ::TickBus::Handler overrides ...
+        void OnTick(float deltaTime, AZ::ScriptTimePoint timePoint) override;
+
+        // DefaultWindowNotificationBus::Handler overrides...
+        void DefaultWindowCreated() override;
+
+        AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_meshHandle;
+
+        AZ::RPI::RenderPipelinePtr m_pipeline;
+        AZ::RPI::RenderPipelinePtr m_copyPipeline;
+        AZ::RPI::RenderPipelinePtr m_originalPipeline;
+        AZStd::shared_ptr<AZ::RPI::WindowContext> m_windowContext;
+
+        AZ::Render::ImGuiActiveContextScope m_imguiScope;
+        ImGuiSidebar m_imguiSidebar;
+
+        bool m_useCopyPipeline = false;
+        bool m_currentlyUsingCopyPipline = false;
+        bool m_migrateRight = false;
+        bool m_rightMigrated = false;
+        bool m_migrateLeft = false;
+        bool m_leftMigrated = false;
+    };
+
+} // namespace AtomSampleViewer

+ 9 - 3
Gem/Code/Source/MultiRenderPipelineExampleComponent.cpp

@@ -110,10 +110,10 @@ namespace AtomSampleViewer
             RPI::AssetUtils::TraceLevel::Assert);
         auto floorAsset = RPI::AssetUtils::LoadAssetByProductPath<RPI::ModelAsset>(CubeModelFilePath,
             RPI::AssetUtils::TraceLevel::Assert);
-        m_floorMeshHandle = meshFeatureProcessor->AcquireMesh(Render::MeshHandleDescriptor{ floorAsset }, material);
+        m_floorMeshHandle = meshFeatureProcessor->AcquireMesh(Render::MeshHandleDescriptor(floorAsset, material));
         for (uint32_t bunnyIndex = 0; bunnyIndex < BunnyCount; bunnyIndex++)
         {
-            m_bunnyMeshHandles[bunnyIndex] = meshFeatureProcessor->AcquireMesh(Render::MeshHandleDescriptor{ bunnyAsset }, material);
+            m_bunnyMeshHandles[bunnyIndex] = meshFeatureProcessor->AcquireMesh(Render::MeshHandleDescriptor(bunnyAsset, material));
         }
 
         const Vector3 floorNonUniformScale{ 12.f, 12.f, 0.1f };
@@ -214,6 +214,7 @@ namespace AtomSampleViewer
         featureProcessor->SetDirection(handle, lightTransform.GetBasis(1));
         AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Lux> lightColor(Color::CreateOne() * 5.0f);
         featureProcessor->SetRgbIntensity(handle, lightColor);
+        featureProcessor->SetShadowEnabled(handle, true);
         featureProcessor->SetCascadeCount(handle, 4);
         featureProcessor->SetShadowmapSize(handle, Render::ShadowmapSize::Size2048);
         featureProcessor->SetViewFrustumCorrectionEnabled(handle, true);
@@ -304,7 +305,12 @@ namespace AtomSampleViewer
         pipelineDesc.m_mainViewTagName = "MainCamera";
         pipelineDesc.m_name = "SecondPipeline";
         pipelineDesc.m_rootPassTemplate = "MainPipeline";
-        pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 4;
+
+        pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 1;
+        SampleComponentManagerRequestBus::BroadcastResult(
+            pipelineDesc.m_renderSettings.m_multisampleState.m_samples,
+            &SampleComponentManagerRequests::GetNumMSAASamples);
+
         pipelineDesc.m_allowModification = true;
         m_secondPipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineDesc, *m_secondWindowContext);
 

+ 29 - 30
Gem/Code/Source/MultiSceneExampleComponent.cpp

@@ -96,7 +96,10 @@ namespace AtomSampleViewer
         pipelineDesc.m_mainViewTagName = "MainCamera";       // Surface shaders render to the "MainCamera" tag
         pipelineDesc.m_name = "SecondPipeline";              // Sets the debug name for this pipeline
         pipelineDesc.m_rootPassTemplate = "MainPipeline";    // References a template in AtomSampleViewer\Passes\MainPipeline.pass
-        pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 4;
+        pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 1;
+        SampleComponentManagerRequestBus::BroadcastResult(
+            pipelineDesc.m_renderSettings.m_multisampleState.m_samples,
+            &SampleComponentManagerRequests::GetNumMSAASamples);
         pipelineDesc.m_allowModification = true;
         m_pipeline = RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineDesc, *m_windowContext);
 
@@ -129,43 +132,38 @@ namespace AtomSampleViewer
         m_postProcessFeatureProcessor = m_scene->GetFeatureProcessor<Render::PostProcessFeatureProcessorInterface>();
 
         // Helper function to load meshes
-        const auto LoadMesh = [this](const char* modelPath) -> Render::MeshFeatureProcessorInterface::MeshHandle
+        auto LoadMesh = [this](
+                            const char* modelPath,
+                            const ModelChangedHandler& modelChangedHandler) -> Render::MeshFeatureProcessorInterface::MeshHandle
         {
             AZ_Assert(m_meshFeatureProcessor, "Cannot find mesh feature processor on scene");
 
-            auto meshAsset = RPI::AssetUtils::GetAssetByProductPath<RPI::ModelAsset>(modelPath, RPI::AssetUtils::TraceLevel::Assert);            
-            auto materialAsset = RPI::AssetUtils::LoadAssetByProductPath<RPI::MaterialAsset>(DefaultPbrMaterialPath,
-                RPI::AssetUtils::TraceLevel::Assert);
+            auto meshAsset = RPI::AssetUtils::GetAssetByProductPath<RPI::ModelAsset>(modelPath, RPI::AssetUtils::TraceLevel::Assert);
+            auto materialAsset =
+                RPI::AssetUtils::LoadAssetByProductPath<RPI::MaterialAsset>(DefaultPbrMaterialPath, RPI::AssetUtils::TraceLevel::Assert);
             auto material = AZ::RPI::Material::FindOrCreate(materialAsset);
-            Render::MeshFeatureProcessorInterface::MeshHandle meshHandle = m_meshFeatureProcessor->AcquireMesh(Render::MeshHandleDescriptor{ meshAsset }, material);
 
+            Render::MeshHandleDescriptor descriptor(meshAsset, material);
+            descriptor.m_modelChangedEventHandler = modelChangedHandler;
+            Render::MeshFeatureProcessorInterface::MeshHandle meshHandle = m_meshFeatureProcessor->AcquireMesh(descriptor);
             return meshHandle;
         };
 
         // Create the ShaderBalls
         {
-            for (uint32_t i = 0u; i < ShaderBallCount; i++)
+            m_shaderBallMeshHandles.resize(ShaderBallCount);
+            for (uint32_t i = 0u; i < ShaderBallCount; ++i)
             {
-                m_shaderBallMeshHandles.push_back(LoadMesh(ShaderBallModelFilePath));
-                auto updateShaderBallTransform = [this, i](Data::Instance<RPI::Model> model)
-                {
-                    const Aabb& aabb = model->GetModelAsset()->GetAabb();
-                    const Vector3 translation{ 0.0f, -aabb.GetMin().GetZ() * aznumeric_cast<float>(i), -aabb.GetMin().GetY() };
-                    const auto transform = Transform::CreateTranslation(translation);
-                    m_meshFeatureProcessor->SetTransform(m_shaderBallMeshHandles[i], transform);
-                };
-
-                // If the model is available already, set the tranform immediately, else utilize the EBus::Event feature
-                Data::Instance<RPI::Model> shaderBallModel = m_meshFeatureProcessor->GetModel(m_shaderBallMeshHandles[i]);
-                if (shaderBallModel)
-                {
-                    updateShaderBallTransform(shaderBallModel);
-                }
-                else
-                {
-                    m_shaderBallChangedHandles.push_back(ModelChangedHandler(updateShaderBallTransform));
-                    m_meshFeatureProcessor->ConnectModelChangeEventHandler(m_shaderBallMeshHandles[i], m_shaderBallChangedHandles.back());
-                }
+                m_shaderBallMeshHandles[i] = LoadMesh(
+                    ShaderBallModelFilePath,
+                    ModelChangedHandler{
+                        [this, i](const Data::Instance<RPI::Model>& model)
+                        {
+                            const Aabb& aabb = model->GetModelAsset()->GetAabb();
+                            const Vector3 translation{ 0.0f, -aabb.GetMin().GetZ() * aznumeric_cast<float>(i), -aabb.GetMin().GetY() };
+                            const auto transform = Transform::CreateTranslation(translation);
+                            m_meshFeatureProcessor->SetTransform(m_shaderBallMeshHandles[i], transform);
+                        } });
             }
         }
 
@@ -174,7 +172,7 @@ namespace AtomSampleViewer
             const Vector3 nonUniformScale{ 24.f, 24.f, 1.0f };
             const Vector3 translation{ 0.f, 0.f, 0.0f };
             const auto transform = Transform::CreateTranslation(translation);
-            m_floorMeshHandle = LoadMesh(CubeModelFilePath);
+            m_floorMeshHandle = LoadMesh(CubeModelFilePath, ModelChangedHandler{[](const Data::Instance<RPI::Model>&){}});
             m_meshFeatureProcessor->SetTransform(m_floorMeshHandle, transform, nonUniformScale);
         }
 
@@ -254,6 +252,7 @@ namespace AtomSampleViewer
             const auto lightDir = Transform::CreateLookAt(
                 helperPosition,
                 Vector3::CreateZero());
+            m_directionalLightFeatureProcessor->SetShadowEnabled(m_directionalLightHandle, true);
             m_directionalLightFeatureProcessor->SetDirection(m_directionalLightHandle, lightDir.GetBasis(1));
 
             m_directionalLightFeatureProcessor->SetShadowmapSize(m_directionalLightHandle, Render::ShadowmapSize::Size512);
@@ -313,7 +312,7 @@ namespace AtomSampleViewer
 
         // Release the probe
         m_reflectionProbeFeatureProcessor->RemoveReflectionProbe(m_reflectionProbeHandle);
-        m_reflectionProbeHandle = {};
+        m_reflectionProbeHandle = ReflectionProbeHandle::CreateNull();
 
         // Release all meshes
         for (auto& shaderBallMeshHandle : m_shaderBallMeshHandles)
@@ -434,7 +433,7 @@ namespace AtomSampleViewer
             auto material = AZ::RPI::Material::FindOrCreate(materialAsset);
             auto bunnyAsset = RPI::AssetUtils::LoadAssetByProductPath<RPI::ModelAsset>(BunnyModelFilePath,
                 RPI::AssetUtils::TraceLevel::Assert);
-            m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(Render::MeshHandleDescriptor{ bunnyAsset }, material);
+            m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(Render::MeshHandleDescriptor(bunnyAsset, material));
 
             GetMeshFeatureProcessor()->SetTransform(m_meshHandle, Transform::CreateRotationZ(Constants::Pi));
         }

+ 1 - 4
Gem/Code/Source/MultiSceneExampleComponent.h

@@ -35,7 +35,7 @@ namespace AtomSampleViewer
         : public AZ::TickBus::Handler
         , public AzFramework::WindowNotificationBus::Handler
     {
-        using ModelChangedHandler = AZ::Render::MeshFeatureProcessorInterface::ModelChangedEvent::Handler;
+        using ModelChangedHandler = AZ::Render::MeshHandleDescriptor::ModelChangedEvent::Handler;
         using PointLightHandle = AZ::Render::PointLightFeatureProcessorInterface::LightHandle;
         using DiskLightHandle = AZ::Render::DiskLightFeatureProcessorInterface::LightHandle;
         using DirectionalLightHandle = AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle;
@@ -86,9 +86,6 @@ namespace AtomSampleViewer
         AZStd::vector<AZ::Render::MeshFeatureProcessorInterface::MeshHandle> m_shaderBallMeshHandles;
         AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_floorMeshHandle;
 
-        // Model change handlers
-        AZStd::vector<ModelChangedHandler> m_shaderBallChangedHandles;
-
         // Various FeatureProcessor handles
         PointLightHandle m_pointLightHandle;
         DiskLightHandle m_diskLightHandle;

+ 4 - 1
Gem/Code/Source/MultiViewSingleSceneAuxGeomExampleComponent.cpp

@@ -75,7 +75,10 @@ namespace AtomSampleViewer
             pipelineDesc.m_mainViewTagName = "MainCamera";          //Surface shaders render to the "MainCamera" tag
             pipelineDesc.m_name = "SecondPipeline";                 //Sets the debug name for this pipeline
             pipelineDesc.m_rootPassTemplate = "MainPipeline";    //References a template in AtomSampleViewer\Passes\PassTemplates.azasset
-            pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 4;
+            pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 1;
+            SampleComponentManagerRequestBus::BroadcastResult(
+                pipelineDesc.m_renderSettings.m_multisampleState.m_samples,
+                &SampleComponentManagerRequests::GetNumMSAASamples);
             m_pipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineDesc, *m_windowContext);
 
             scene->AddRenderPipeline(m_pipeline);

+ 5 - 3
Gem/Code/Source/ParallaxMappingExampleComponent.cpp

@@ -73,8 +73,8 @@ namespace AtomSampleViewer
     void ParallaxMappingExampleComponent::Activate()
     {
         // Asset
-        m_planeAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/plane.azmodel", AZ::RPI::AssetUtils::TraceLevel::Assert);
-        m_boxAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/cube.azmodel", AZ::RPI::AssetUtils::TraceLevel::Assert);
+        m_planeAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/plane.fbx.azmodel", AZ::RPI::AssetUtils::TraceLevel::Assert);
+        m_boxAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/cube.fbx.azmodel", AZ::RPI::AssetUtils::TraceLevel::Assert);
         m_parallaxMaterialAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::MaterialAsset>("testdata/materials/parallaxrock.azmaterial", AZ::RPI::AssetUtils::TraceLevel::Assert);
         m_defaultMaterialAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::MaterialAsset>("materials/defaultpbr.azmaterial", AZ::RPI::AssetUtils::TraceLevel::Assert);
         m_parallaxMaterial = AZ::RPI::Material::Create(m_parallaxMaterialAsset);
@@ -157,7 +157,8 @@ namespace AtomSampleViewer
         AZ::Data::Instance<AZ::RPI::Material> material,
         AZ::Transform transform)
     {
-        AZ::Render::MeshFeatureProcessorInterface::MeshHandle meshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor{ modelAsset }, material);
+        AZ::Render::MeshFeatureProcessorInterface::MeshHandle meshHandle =
+            GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor(modelAsset, material));
         GetMeshFeatureProcessor()->SetTransform(meshHandle, transform);
         return meshHandle;
     }
@@ -166,6 +167,7 @@ namespace AtomSampleViewer
     {
         const AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle handle = m_directionalLightFeatureProcessor->AcquireLight();
 
+        m_directionalLightFeatureProcessor->SetShadowEnabled(handle, true);
         m_directionalLightFeatureProcessor->SetShadowmapSize(handle, AZ::Render::ShadowmapSize::Size2048);
         m_directionalLightFeatureProcessor->SetCascadeCount(handle, 4);
         m_directionalLightFeatureProcessor->SetShadowmapFrustumSplitSchemeRatio(handle, 0.5f);

+ 23 - 27
Gem/Code/Source/Passes/RayTracingAmbientOcclusionPass.cpp

@@ -6,21 +6,21 @@
  *
  */
 
-#include <Passes/RayTracingAmbientOcclusionPass.h>
+#include <Atom/Feature/TransformService/TransformServiceFeatureProcessorInterface.h>
 #include <Atom/RHI/CommandList.h>
-#include <Atom/RHI/DispatchRaysItem.h>
+#include <Atom/RHI/DeviceDispatchRaysItem.h>
 #include <Atom/RHI/Factory.h>
 #include <Atom/RHI/FrameScheduler.h>
 #include <Atom/RHI/RHISystemInterface.h>
 #include <Atom/RHI/ScopeProducerFunction.h>
-#include <Atom/RPI.Public/Buffer/BufferSystemInterface.h>
 #include <Atom/RPI.Public/Buffer/Buffer.h>
-#include <Atom/RPI.Public/RenderPipeline.h>
-#include <Atom/RPI.Public/Scene.h>
+#include <Atom/RPI.Public/Buffer/BufferSystemInterface.h>
 #include <Atom/RPI.Public/Pass/PassUtils.h>
 #include <Atom/RPI.Public/RPIUtils.h>
+#include <Atom/RPI.Public/RenderPipeline.h>
+#include <Atom/RPI.Public/Scene.h>
 #include <Atom/RPI.Public/View.h>
-#include <Atom/Feature/TransformService/TransformServiceFeatureProcessor.h>
+#include <Passes/RayTracingAmbientOcclusionPass.h>
 
 namespace AZ
 {
@@ -49,8 +49,6 @@ namespace AZ
 
         void RayTracingAmbientOcclusionPass::CreateRayTracingPipelineState()
         {
-            RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
-
             // load ray generation shader
             const char* rayGenerationShaderFilePath = "Shaders/RayTracing/RTAOGeneration.azshader";
             m_rayGenerationShader = RPI::LoadShader(rayGenerationShaderFilePath);
@@ -103,8 +101,8 @@ namespace AZ
                 ;
 
             // create the ray tracing pipeline state object
-            m_rayTracingPipelineState = RHI::Factory::Get().CreateRayTracingPipelineState();
-            m_rayTracingPipelineState->Init(*device.get(), &descriptor);
+            m_rayTracingPipelineState = aznew RHI::RayTracingPipelineState;
+            m_rayTracingPipelineState->Init(RHI::MultiDevice::AllDevices, descriptor);
         }
 
         void RayTracingAmbientOcclusionPass::FrameBeginInternal(FramePrepareParams params)
@@ -112,7 +110,7 @@ namespace AZ
             if (m_createRayTracingPipelineState)
             {
                 RPI::Scene* scene = m_pipeline->GetScene();
-                m_rayTracingFeatureProcessor = scene->GetFeatureProcessor<RayTracingFeatureProcessor>();
+                m_rayTracingFeatureProcessor = scene->GetFeatureProcessor<RayTracingFeatureProcessorInterface>();
 
                 CreateRayTracingPipelineState();
                 m_createRayTracingPipelineState = false;
@@ -120,12 +118,11 @@ namespace AZ
 
             if (!m_rayTracingShaderTable)
             {
-                RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
                 RHI::RayTracingBufferPools& rayTracingBufferPools = m_rayTracingFeatureProcessor->GetBufferPools();
 
                 // Build shader table once. Since we are not using local srg so we don't need to rebuild it even when scene changed 
-                m_rayTracingShaderTable = RHI::Factory::Get().CreateRayTracingShaderTable();
-                m_rayTracingShaderTable->Init(*device.get(), rayTracingBufferPools);
+                m_rayTracingShaderTable = aznew RHI::RayTracingShaderTable;
+                m_rayTracingShaderTable->Init(RHI::MultiDevice::AllDevices, rayTracingBufferPools);
 
                 AZStd::shared_ptr<RHI::RayTracingShaderTableDescriptor> descriptor = AZStd::make_shared<RHI::RayTracingShaderTableDescriptor>();
                 descriptor->Build(AZ::Name("RayTracingAOShaderTable"), m_rayTracingPipelineState)
@@ -162,7 +159,7 @@ namespace AZ
             RHI::ShaderInputConstantIndex constantIndex;
 
             // Bind scene TLAS buffer
-            const RHI::Ptr<RHI::Buffer> tlasBuffer = m_rayTracingFeatureProcessor->GetTlas()->GetTlasBuffer();
+            auto tlasBuffer = m_rayTracingFeatureProcessor->GetTlas()->GetTlasBuffer();
             if (tlasBuffer)
             {
                 // TLAS
@@ -170,7 +167,7 @@ namespace AZ
                 RHI::BufferViewDescriptor bufferViewDescriptor = RHI::BufferViewDescriptor::CreateRayTracingTLAS(tlasBufferByteCount);
 
                 bufferIndex = srgLayout->FindShaderInputBufferIndex(AZ::Name("m_scene"));
-                m_shaderResourceGroup->SetBufferView(bufferIndex, tlasBuffer->GetBufferView(bufferViewDescriptor).get());
+                m_shaderResourceGroup->SetBufferView(bufferIndex, tlasBuffer->BuildBufferView(bufferViewDescriptor).get());
             }
 
             // Bind constants
@@ -204,7 +201,7 @@ namespace AZ
         void RayTracingAmbientOcclusionPass::BuildCommandListInternal([[maybe_unused]] const RHI::FrameGraphExecuteContext& context)
         {
             RPI::Scene* scene = m_pipeline->GetScene();
-            RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor<RayTracingFeatureProcessor>();
+            RayTracingFeatureProcessorInterface* rayTracingFeatureProcessor = scene->GetFeatureProcessor<RayTracingFeatureProcessorInterface>();
             AZ_Assert(rayTracingFeatureProcessor, "RayTracingAmbientOcclusionPass requires the RayTracingFeatureProcessor");
 
             if (!rayTracingFeatureProcessor->GetSubMeshCount())
@@ -220,20 +217,19 @@ namespace AZ
             RPI::PassAttachment* outputAttachment = GetOutputBinding(0).GetAttachment().get();
             RHI::Size targetImageSize = outputAttachment->m_descriptor.m_image.m_size;
 
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] =
-            {
-                m_shaderResourceGroup->GetRHIShaderResourceGroup()
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_shaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
             };
 
-            RHI::DispatchRaysItem dispatchRaysItem;
-            dispatchRaysItem.m_width = targetImageSize.m_width;
-            dispatchRaysItem.m_height = targetImageSize.m_height;
-            dispatchRaysItem.m_depth = 1;
-            dispatchRaysItem.m_rayTracingPipelineState = m_rayTracingPipelineState.get();
-            dispatchRaysItem.m_rayTracingShaderTable = m_rayTracingShaderTable.get();
+            RHI::DeviceDispatchRaysItem dispatchRaysItem;
+            dispatchRaysItem.m_arguments.m_direct.m_width = targetImageSize.m_width;
+            dispatchRaysItem.m_arguments.m_direct.m_height = targetImageSize.m_height;
+            dispatchRaysItem.m_arguments.m_direct.m_depth = 1;
+            dispatchRaysItem.m_rayTracingPipelineState = m_rayTracingPipelineState->GetDeviceRayTracingPipelineState(context.GetDeviceIndex()).get();
+            dispatchRaysItem.m_rayTracingShaderTable = m_rayTracingShaderTable->GetDeviceRayTracingShaderTable(context.GetDeviceIndex()).get();
             dispatchRaysItem.m_shaderResourceGroupCount = 1;
             dispatchRaysItem.m_shaderResourceGroups = shaderResourceGroups;
-            dispatchRaysItem.m_globalPipelineState = m_globalPipelineState.get();
+            dispatchRaysItem.m_globalPipelineState = m_globalPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
 
             // submit the DispatchRays item
             context.GetCommandList()->Submit(dispatchRaysItem);

+ 2 - 3
Gem/Code/Source/Passes/RayTracingAmbientOcclusionPass.h

@@ -7,6 +7,7 @@
  */
 #pragma once
 
+#include <Atom/Feature/RayTracing/RayTracingFeatureProcessorInterface.h>
 #include <Atom/RHI/ScopeProducer.h>
 #include <Atom/RPI.Public/Pass/RenderPass.h>
 #include <Atom/RPI.Public/Buffer/Buffer.h>
@@ -17,8 +18,6 @@
 #include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
 #include <Atom/RPI.Public/Shader/Shader.h>
 
-#include <RayTracing/RayTracingFeatureProcessor.h>
-
 namespace AZ
 {
     namespace Render
@@ -71,7 +70,7 @@ namespace AZ
             // ray tracing global pipeline state
             RHI::ConstPtr<RHI::PipelineState> m_globalPipelineState;
 
-            Render::RayTracingFeatureProcessor* m_rayTracingFeatureProcessor = nullptr;
+            Render::RayTracingFeatureProcessorInterface* m_rayTracingFeatureProcessor = nullptr;
 
             uint32_t m_frameCount = 0;
 

+ 13 - 12
Gem/Code/Source/Performance/HighInstanceExampleComponent.cpp

@@ -68,19 +68,19 @@ namespace AtomSampleViewer
 
         m_expandedModelList =
         {
-            "materialeditor/viewportmodels/cone.azmodel",
-            "materialeditor/viewportmodels/cube.azmodel",
-            "materialeditor/viewportmodels/cylinder.azmodel",
-            "materialeditor/viewportmodels/platonicsphere.azmodel",
-            "materialeditor/viewportmodels/polarsphere.azmodel",
-            "materialeditor/viewportmodels/quadsphere.azmodel",
-            "materialeditor/viewportmodels/torus.azmodel",
-            "objects/cube.azmodel",
-            "objects/cylinder.azmodel",
+            "materialeditor/viewportmodels/cone.fbx.azmodel",
+            "materialeditor/viewportmodels/cube.fbx.azmodel",
+            "materialeditor/viewportmodels/cylinder.fbx.azmodel",
+            "materialeditor/viewportmodels/platonicsphere.fbx.azmodel",
+            "materialeditor/viewportmodels/polarsphere.fbx.azmodel",
+            "materialeditor/viewportmodels/quadsphere.fbx.azmodel",
+            "materialeditor/viewportmodels/torus.fbx.azmodel",
+            "objects/cube.fbx.azmodel",
+            "objects/cylinder.fbx.azmodel",
         };
         m_simpleModelList =
         {
-            "objects/cube.azmodel"
+            "objects/cube.fbx.azmodel"
         };
         m_modelBrowser.SetDefaultPinnedAssets(m_simpleModelList);
     }
@@ -242,7 +242,7 @@ namespace AtomSampleViewer
                 AZ::Data::Asset<AZ::RPI::ModelAsset> modelAsset;
                 modelAsset.Create(instanceData.m_modelAssetId);
 
-                instanceData.m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor{ modelAsset }, materialInstance);
+                instanceData.m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor(modelAsset, materialInstance));
                 GetMeshFeatureProcessor()->SetTransform(instanceData.m_meshHandle, instanceData.m_transform);
             }
         }
@@ -289,7 +289,7 @@ namespace AtomSampleViewer
         }
         else
         {
-            return AZ::RPI::AssetUtils::GetAssetIdForProductPath("testdata/objects/cube/cube.azmodel", AZ::RPI::AssetUtils::TraceLevel::Error);
+            return AZ::RPI::AssetUtils::GetAssetIdForProductPath("testdata/objects/cube/cube.fbx.azmodel", AZ::RPI::AssetUtils::TraceLevel::Error);
         }
     }
 
@@ -504,6 +504,7 @@ namespace AtomSampleViewer
             lightTransform.GetBasis(1));
 
         featureProcessor->SetRgbIntensity(handle, AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Lux>(AZ::Color::CreateOne() * m_testParameters.m_directionalLightIntensity));
+        featureProcessor->SetShadowEnabled(handle, true);
         featureProcessor->SetCascadeCount(handle, m_testParameters.m_numDirectionalLightShadowCascades);
         featureProcessor->SetShadowmapSize(handle, m_testParameters.m_shadowmapSize);
         featureProcessor->SetViewFrustumCorrectionEnabled(handle, false);

+ 1 - 1
Gem/Code/Source/Platform/Android/SSRExampleComponent_Traits_Platform.h

@@ -9,4 +9,4 @@
 // Mali devices have a memory limitation of vertex buffer (180MB), so low resolution mesh model is needed.
 // https://community.arm.com/developer/tools-software/graphics/b/blog/posts/memory-limits-with-vulkan-on-mali-gpus
 // [ATOM-14947]
-#define ATOMSAMPLEVIEWER_TRAIT_SSR_SAMPLE_HERMANUBIS_MODEL_NAME "objects/hermanubis/hermanubis_low.azmodel"
+#define ATOMSAMPLEVIEWER_TRAIT_SSR_SAMPLE_HERMANUBIS_MODEL_NAME "objects/hermanubis/hermanubis_low.fbx.azmodel"

+ 0 - 5
Gem/Code/Source/Platform/Android/SampleComponentManager_Android.cpp

@@ -33,9 +33,4 @@ namespace AtomSampleViewer
     {
         return "LowEndPipeline";
     }
-
-    int SampleComponentManager::GetDefaultNumMSAASamples()
-    {
-        return 1;
-    }
 } // namespace AtomSampleViewer

+ 6 - 1
Gem/Code/Source/Platform/Android/Utils_Android.cpp

@@ -18,7 +18,12 @@ namespace AtomSampleViewer
             return false;
         }
 
-        bool RunDiffTool(const AZStd::string& filePathA, const AZStd::string& filePathB)
+        AZStd::string GetDefaultDiffToolPath_Impl()
+        {
+            return AZStd::string("");
+        }
+
+        bool RunDiffTool_Impl(const AZStd::string& diffToolPath, const AZStd::string& filePathA, const AZStd::string& filePathB)
         {
             return false;
         }

+ 1 - 1
Gem/Code/Source/Platform/Linux/SSRExampleComponent_Traits_Platform.h

@@ -6,4 +6,4 @@
  *
  */
 
-#define ATOMSAMPLEVIEWER_TRAIT_SSR_SAMPLE_HERMANUBIS_MODEL_NAME "objects/hermanubis/hermanubis_high.azmodel"
+#define ATOMSAMPLEVIEWER_TRAIT_SSR_SAMPLE_HERMANUBIS_MODEL_NAME "objects/hermanubis/hermanubis_high.fbx.azmodel"

+ 0 - 5
Gem/Code/Source/Platform/Linux/SampleComponentManager_Linux.cpp

@@ -28,9 +28,4 @@ namespace AtomSampleViewer
     {
         return "MainPipeline";
     }
-
-    int SampleComponentManager::GetDefaultNumMSAASamples()
-    {
-        return 4;
-    }
 } // namespace AtomSampleViewer

+ 10 - 7
Gem/Code/Source/Platform/Linux/Utils_Linux.cpp

@@ -20,10 +20,14 @@ namespace AtomSampleViewer
             return true;
         }
 
-        bool RunDiffTool(const AZStd::string& filePathA, const AZStd::string& filePathB)
+        AZStd::string GetDefaultDiffToolPath_Impl()
+        {
+            return AZStd::string("/usr/bin/bcompare");
+        }
+
+        bool RunDiffTool_Impl(const AZStd::string& diffToolPath, const AZStd::string& filePathA, const AZStd::string& filePathB)
         {
             bool result = true;
-            AZStd::string executablePath = "/usr/bin/bcompare";
 
             // Fork a process to run Beyond Compare app
             pid_t childPid = fork();
@@ -31,15 +35,14 @@ namespace AtomSampleViewer
             if (childPid == 0)
             {
                 // In child process
-                char* args[] = { const_cast<char*>(executablePath.c_str()), const_cast<char*>(filePathA.c_str()),
+                char* args[] = { const_cast<char*>(diffToolPath.c_str()), const_cast<char*>(filePathA.c_str()),
                                  const_cast<char*>(filePathB.c_str()), static_cast<char*>(0) };
-                execv(executablePath.c_str(), args);
+                execv(diffToolPath.c_str(), args);
 
                 AZ_Error(
                     "RunDiffTool", false,
-                    "RunDiffTool: Unable to launch Beyond Compare %s : errno = %s . Make sure you have installed Beyond Compare command "
-                    "line tools.",
-                    executablePath.c_str(), strerror(errno));
+                    "RunDiffTool: Unable to launch Diff Tool %s : errno = %s .",
+                    diffToolPath.c_str(), strerror(errno));
 
                 _exit(errno);
             }

+ 1 - 1
Gem/Code/Source/Platform/Mac/SSRExampleComponent_Traits_Platform.h

@@ -6,4 +6,4 @@
  *
  */
 
-#define ATOMSAMPLEVIEWER_TRAIT_SSR_SAMPLE_HERMANUBIS_MODEL_NAME "objects/hermanubis/hermanubis_high.azmodel"
+#define ATOMSAMPLEVIEWER_TRAIT_SSR_SAMPLE_HERMANUBIS_MODEL_NAME "objects/hermanubis/hermanubis_high.fbx.azmodel"

+ 0 - 5
Gem/Code/Source/Platform/Mac/SampleComponentManager_Mac.cpp

@@ -28,9 +28,4 @@ namespace AtomSampleViewer
     {
         return "MainPipeline";
     }
-
-    int SampleComponentManager::GetDefaultNumMSAASamples()
-    {
-        return 4;
-    }
 } // namespace AtomSampleViewer

+ 12 - 8
Gem/Code/Source/Platform/Mac/Utils_Mac.cpp

@@ -21,21 +21,25 @@ namespace AtomSampleViewer
             return true;
         }
 
-        bool RunDiffTool(const AZStd::string& filePathA, const AZStd::string& filePathB)
+        AZStd::string GetDefaultDiffToolPath_Impl()
+        {
+            return AZStd::string("/usr/local/bin/bcompare");
+        }
+
+        bool RunDiffTool_Impl(const AZStd::string& diffToolPath, const AZStd::string& filePathA, const AZStd::string& filePathB)
         {
             bool result = true;
-            AZStd::string executablePath = "/usr/local/bin/bcompare";
-           
+
             //Fork a process to run Beyond Compare app
             pid_t childPid = fork();
-            
+
             if (childPid == 0)
             {
                 //In child process
-                char* args[] = { const_cast<char*>(executablePath.c_str()), const_cast<char*>(filePathA.c_str()), const_cast<char*>(filePathB.c_str()), static_cast<char*>(0)};
-                execv(executablePath.c_str(), args);
-                
-                AZ_TracePrintf("RunDiffTool", "RunDiffTool: Unable to launch Beyond Compare %s : errno = %s . Make sure you have installed Beyond Compare command line tools.", executablePath.c_str(), strerror(errno));
+                char* args[] = { const_cast<char*>(diffToolPath.c_str()), const_cast<char*>(filePathA.c_str()), const_cast<char*>(filePathB.c_str()), static_cast<char*>(0)};
+                execv(diffToolPath.c_str(), args);
+
+                AZ_TracePrintf("RunDiffTool", "RunDiffTool: Unable to launch Diff Tool %s : errno = %s .", diffToolPath.c_str(), strerror(errno));
                 std::cerr << strerror(errno) << std::endl;
 
                 _exit(errno);

+ 1 - 1
Gem/Code/Source/Platform/Windows/SSRExampleComponent_Traits_Platform.h

@@ -6,4 +6,4 @@
  *
  */
 
-#define ATOMSAMPLEVIEWER_TRAIT_SSR_SAMPLE_HERMANUBIS_MODEL_NAME "objects/hermanubis/hermanubis_high.azmodel"
+#define ATOMSAMPLEVIEWER_TRAIT_SSR_SAMPLE_HERMANUBIS_MODEL_NAME "objects/hermanubis/hermanubis_high.fbx.azmodel"

+ 0 - 11
Gem/Code/Source/Platform/Windows/SampleComponentManager_Windows.cpp

@@ -40,15 +40,4 @@ namespace AtomSampleViewer
         }
         return "MainPipeline";
     }
-
-    int SampleComponentManager::GetDefaultNumMSAASamples()
-    {
-        // Use sample count of 1 for VR pipelines
-        AZ::RPI::XRRenderingInterface* xrSystem = AZ::RPI::RPISystemInterface::Get()->GetXRSystem();
-        if (xrSystem)
-        {
-            return 1;
-        }
-        return 4;
-    }
 } // namespace AtomSampleViewer

+ 8 - 2
Gem/Code/Source/Platform/Windows/Utils_Windows.cpp

@@ -18,11 +18,17 @@ namespace AtomSampleViewer
             return true;
         }
 
-        bool RunDiffTool(const AZStd::string& filePathA, const AZStd::string& filePathB)
+        AZStd::string GetDefaultDiffToolPath_Impl()
+        {
+            return AZStd::string("C:\\Program Files\\Beyond Compare 4\\BCompare.exe");
+        }
+
+        bool RunDiffTool_Impl(const AZStd::string& diffToolPath, const AZStd::string& filePathA, const AZStd::string& filePathB)
         {
             bool result = false;
 
-            AZStd::wstring exeW = L"C:\\Program Files\\Beyond Compare 4\\BCompare.exe";
+            AZStd::wstring exeW;
+            AZStd::to_wstring(exeW, diffToolPath.c_str());
             AZStd::wstring filePathAW;
             AZStd::to_wstring(filePathAW, filePathA.c_str());
             AZStd::wstring filePathBW;

+ 1 - 1
Gem/Code/Source/Platform/iOS/SSRExampleComponent_Traits_Platform.h

@@ -6,4 +6,4 @@
  *
  */
 
-#define ATOMSAMPLEVIEWER_TRAIT_SSR_SAMPLE_HERMANUBIS_MODEL_NAME "objects/hermanubis/hermanubis_low.azmodel"
+#define ATOMSAMPLEVIEWER_TRAIT_SSR_SAMPLE_HERMANUBIS_MODEL_NAME "objects/hermanubis/hermanubis_low.fbx.azmodel"

+ 0 - 5
Gem/Code/Source/Platform/iOS/SampleComponentManager_iOS.cpp

@@ -33,9 +33,4 @@ namespace AtomSampleViewer
     {
         return "LowEndPipeline";
     }
-
-    int SampleComponentManager::GetDefaultNumMSAASamples()
-    {
-        return 1;
-    }
 } // namespace AtomSampleViewer

+ 6 - 1
Gem/Code/Source/Platform/iOS/Utils_iOS.cpp

@@ -18,7 +18,12 @@ namespace AtomSampleViewer
             return false;
         }
 
-        bool RunDiffTool(const AZStd::string& filePathA, const AZStd::string& filePathB)
+        AZStd::string GetDefaultDiffToolPath_Impl()
+        {
+            return "";
+        }
+
+        bool RunDiffTool_Impl(const AZStd::string& diffToolPath, const AZStd::string& filePathA, const AZStd::string& filePathB)
         {
             return false;
         }

+ 52 - 20
Gem/Code/Source/ProceduralSkinnedMesh.cpp

@@ -9,9 +9,12 @@
 #include <ProceduralSkinnedMesh.h>
 
 #include <AzCore/Math/MathUtils.h>
+#include <Atom/RPI.Reflect/Model/ModelAssetHelpers.h>
 
 const uint32_t maxInfluencesPerVertex = 4;
 
+using namespace AZ;
+
 namespace AtomSampleViewer
 {
     void ProceduralSkinnedMesh::Resize(SkinnedMeshConfig& skinnedMeshConfig)
@@ -19,6 +22,11 @@ namespace AtomSampleViewer
         m_verticesPerSegment = skinnedMeshConfig.m_verticesPerSegment;
         m_segmentCount = static_cast<uint32_t>(skinnedMeshConfig.m_segmentCount);
         m_vertexCount = m_segmentCount * m_verticesPerSegment;
+
+        m_alignedVertCountForRGBStream = aznumeric_cast<uint32_t>(RPI::ModelAssetHelpers::GetAlignedCount<float>(m_vertexCount, RHI::Format::R32G32B32_FLOAT, RPI::SkinnedMeshBufferAlignment));
+
+        m_alignedVertCountForRGBAStream = aznumeric_cast<uint32_t>(RPI::ModelAssetHelpers::GetAlignedCount<float>(m_vertexCount, RHI::Format::R32G32B32A32_FLOAT, RPI::SkinnedMeshBufferAlignment));
+        
         m_boneCount = AZ::GetMax(1u, static_cast<uint32_t>(skinnedMeshConfig.m_boneCount));
         m_influencesPerVertex = AZ::GetMax(0u, AZ::GetMin(static_cast<uint32_t>(skinnedMeshConfig.m_influencesPerVertex), AZ::GetMin(m_boneCount, maxInfluencesPerVertex)));
         m_subMeshCount = skinnedMeshConfig.m_subMeshCount;
@@ -130,13 +138,21 @@ namespace AtomSampleViewer
             }
         }
 
-        m_positions.resize(m_vertexCount);
-        m_normals.resize(m_vertexCount);
-        m_tangents.resize(m_vertexCount);
-        m_bitangents.resize(m_vertexCount);
-        // We pack 16 bit joint id's into 32 bit uints, so use half the number of joints for the uint count
-        m_blendIndices.resize(m_vertexCount * m_influencesPerVertex / 2);
-        m_blendWeights.resize(m_vertexCount * m_influencesPerVertex);
+        m_positions.resize(m_alignedVertCountForRGBStream);
+        m_normals.resize(m_alignedVertCountForRGBStream);
+        m_bitangents.resize(m_alignedVertCountForRGBStream);
+        
+        size_t alignedTangentVertCount = RPI::ModelAssetHelpers::GetAlignedCount<float>(m_vertexCount, RPI::TangentFormat, RPI::SkinnedMeshBufferAlignment);
+        m_tangents.resize(alignedTangentVertCount);
+        
+        // We pack 16 bit joint id's into 32 bit uints.
+        uint32_t numVertInfluences = m_vertexCount * m_influencesPerVertex;
+        size_t alignedIndicesVertCount = RPI::ModelAssetHelpers::GetAlignedCount<uint32_t>(numVertInfluences, RPI::SkinIndicesFormat, RPI::SkinnedMeshBufferAlignment);
+        m_blendIndices.resize(alignedIndicesVertCount);
+
+        size_t alignedWeightsVertCount = RPI::ModelAssetHelpers::GetAlignedCount<float>(numVertInfluences, RPI::SkinWeightFormat, RPI::SkinnedMeshBufferAlignment);
+        m_blendWeights.resize(alignedWeightsVertCount);
+
         m_uvs.resize(m_vertexCount);
 
         for (uint32_t vertexIndex = 0; vertexIndex < m_vertexCount; ++vertexIndex)
@@ -147,19 +163,19 @@ namespace AtomSampleViewer
 
             // Get the x and y positions from a unit circle
             float vertexAngle = (AZ::Constants::TwoPi / static_cast<float>(m_verticesPerSegment - 1)) * static_cast<float>(indexWithinTheCurrentSegment);
-            m_positions[vertexIndex][0] = cosf(vertexAngle) * m_radius;
-            m_positions[vertexIndex][1] = sinf(vertexAngle) * m_radius;
-            m_positions[vertexIndex][2] = m_segmentHeightOffsets[segmentIndex];
+            m_positions[(vertexIndex * RPI::PositionFloatsPerVert) + 0] = cosf(vertexAngle) * m_radius;
+            m_positions[(vertexIndex * RPI::PositionFloatsPerVert) + 1] = sinf(vertexAngle) * m_radius;
+            m_positions[(vertexIndex * RPI::PositionFloatsPerVert) + 2] = m_segmentHeightOffsets[segmentIndex];
 
             // Normals are flat on the z-plane and point away from the origin in the direction of the vertex position
-            m_normals[vertexIndex][0] = m_positions[vertexIndex][0];
-            m_normals[vertexIndex][1] = m_positions[vertexIndex][1];
-            m_normals[vertexIndex][2] = 0.0f;
+            m_normals[(vertexIndex * RPI::PositionFloatsPerVert) + 0] = m_positions[(vertexIndex * RPI::PositionFloatsPerVert) + 0];
+            m_normals[(vertexIndex * RPI::PositionFloatsPerVert) + 1] = m_positions[(vertexIndex * RPI::PositionFloatsPerVert) + 1];
+            m_normals[(vertexIndex * RPI::PositionFloatsPerVert) + 2] = 0.0f;
 
             // Bitangent is straight down
-            m_bitangents[vertexIndex][0] = 0.0f;
-            m_bitangents[vertexIndex][1] = 0.0f;
-            m_bitangents[vertexIndex][2] = -1.0f;
+            m_bitangents[(vertexIndex * RPI::PositionFloatsPerVert)+0] = 0.0f;
+            m_bitangents[(vertexIndex * RPI::PositionFloatsPerVert)+1] = 0.0f;
+            m_bitangents[(vertexIndex * RPI::PositionFloatsPerVert)+2] = -1.0f;
 
             for (size_t i = 0; i < m_influencesPerVertex; ++i)
             {
@@ -200,10 +216,11 @@ namespace AtomSampleViewer
             uint32_t leftVertex = vertexIndex;
             // The last vertex of the segment will have the first vertex of the segment as its neighbor, not just the next vertex (which would be in the next segment)
             uint32_t rightVertex = (leftVertex + 1) % m_verticesPerSegment;
-            m_tangents[vertexIndex][0] = m_positions[leftVertex][0] - m_positions[rightVertex][0];
-            m_tangents[vertexIndex][1] = m_positions[leftVertex][1] - m_positions[rightVertex][1];
-            m_tangents[vertexIndex][2] = 0.0f;
-            m_tangents[vertexIndex][3] = 1.0f;
+            m_tangents[(vertexIndex * RPI::TangentFloatsPerVert)+0] = m_positions[(leftVertex * RPI::PositionFloatsPerVert) + 0] - m_positions[(rightVertex * RPI::PositionFloatsPerVert)+0];
+            m_tangents[(vertexIndex * RPI::TangentFloatsPerVert)+1] = m_positions[(leftVertex * RPI::PositionFloatsPerVert) + 1] - m_positions[(rightVertex * RPI::PositionFloatsPerVert)+1];
+
+            m_tangents[(vertexIndex * RPI::TangentFloatsPerVert)+2] = 0.0f;
+            m_tangents[(vertexIndex * RPI::TangentFloatsPerVert)+3] = 1.0f;
         }
     }
 
@@ -337,4 +354,19 @@ namespace AtomSampleViewer
             m_segmentHeightOffsets[segmentIndex] = currentSegmentHeight - heightOffset;
         }
     }
+
+    uint32_t ProceduralSkinnedMesh::GetVertexCount() const
+    {
+        return m_vertexCount;
+    }
+
+    uint32_t ProceduralSkinnedMesh::GetAlignedVertCountForRGBStream() const
+    {
+        return m_alignedVertCountForRGBStream;
+    }
+
+    uint32_t ProceduralSkinnedMesh::GetAlignedVertCountForRGBAStream() const
+    {
+        return m_alignedVertCountForRGBAStream;
+    }
 }

+ 9 - 4
Gem/Code/Source/ProceduralSkinnedMesh.h

@@ -36,15 +36,18 @@ namespace AtomSampleViewer
         uint32_t GetSubMeshCount() const;
         float GetSubMeshYOffset() const;
 
+        uint32_t GetVertexCount() const;
+        uint32_t GetAlignedVertCountForRGBStream() const;
+        uint32_t GetAlignedVertCountForRGBAStream() const;
         static const uint32_t MaxInfluencesPerVertex = 4;
 
         // Mesh data that's used for rendering
         AZ::Aabb m_aabb = AZ::Aabb::CreateNull();
         AZStd::vector<uint32_t> m_indices;
-        AZStd::vector< AZStd::array<float, 3>> m_positions;
-        AZStd::vector< AZStd::array<float, 3>> m_normals;
-        AZStd::vector< AZStd::array<float, 4>> m_tangents;
-        AZStd::vector< AZStd::array<float, 3>> m_bitangents;
+        AZStd::vector<float> m_positions;
+        AZStd::vector<float> m_normals;
+        AZStd::vector<float> m_tangents;
+        AZStd::vector<float> m_bitangents;
         AZStd::vector<uint32_t> m_blendIndices;
         AZStd::vector<float> m_blendWeights;
         AZStd::vector<AZStd::array<float, 2>> m_uvs;
@@ -64,6 +67,8 @@ namespace AtomSampleViewer
 
         uint32_t m_boneCount = 0;
         uint32_t m_vertexCount = 0;
+        uint32_t m_alignedVertCountForRGBStream = 0;
+        uint32_t m_alignedVertCountForRGBAStream = 0;
         uint32_t m_verticesPerSegment = 0;
         uint32_t m_segmentCount = 0;
         uint32_t m_influencesPerVertex = 0;

+ 50 - 38
Gem/Code/Source/ProceduralSkinnedMeshUtils.cpp

@@ -12,6 +12,7 @@
 #include <Atom/RPI.Reflect/Buffer/BufferAssetCreator.h>
 #include <Atom/RPI.Reflect/ResourcePoolAssetCreator.h>
 #include <Atom/RPI.Reflect/Model/ModelAssetCreator.h>
+#include <Atom/RPI.Reflect/Model/ModelAssetHelpers.h>
 #include <Atom/RPI.Reflect/Model/ModelLodAssetCreator.h>
 #include <Atom/RPI.Reflect/Model/SkinJointIdPadding.h>
 #include <Atom/Feature/SkinnedMesh/SkinnedMeshInputBuffers.h>
@@ -99,51 +100,53 @@ namespace AtomSampleViewer
         modelCreator.SetName(AZStd::string("ProceduralSkinnedMesh_" + assetId.m_guid.ToString<AZStd::string>()));
 
         uint32_t submeshCount = proceduralMesh.GetSubMeshCount();
-        uint32_t verticesPerSubmesh = aznumeric_caster(proceduralMesh.m_positions.size());
+        uint32_t verticesPerSubmesh = proceduralMesh.GetVertexCount();
         uint32_t totalVertices = verticesPerSubmesh * submeshCount;
 
-        uint32_t jointIdCountPerSubmesh = verticesPerSubmesh * proceduralMesh.GetInfluencesPerVertex();
-        uint32_t extraJointIdCount = AZ::RPI::CalculateJointIdPaddingCount(jointIdCountPerSubmesh);
-        uint32_t extraPackedIdCount = extraJointIdCount / 2;
-
         // Copy the original buffer data n-times to create the data for extra sub-meshes
         DuplicateVertices(proceduralMesh.m_indices, aznumeric_caster(proceduralMesh.m_indices.size()), submeshCount);
-        DuplicateVertices(proceduralMesh.m_positions, verticesPerSubmesh, submeshCount);
-        DuplicateVertices(proceduralMesh.m_normals, verticesPerSubmesh, submeshCount);
-        DuplicateVertices(proceduralMesh.m_tangents, verticesPerSubmesh, submeshCount);
-        DuplicateVertices(proceduralMesh.m_bitangents, verticesPerSubmesh, submeshCount);
-        DuplicateVertices(proceduralMesh.m_uvs, verticesPerSubmesh, submeshCount);
-        DuplicateVertices(proceduralMesh.m_blendWeights, verticesPerSubmesh * proceduralMesh.GetInfluencesPerVertex(), submeshCount);
+        DuplicateVertices(proceduralMesh.m_positions, proceduralMesh.GetAlignedVertCountForRGBStream(), submeshCount);
+        DuplicateVertices(proceduralMesh.m_normals, proceduralMesh.GetAlignedVertCountForRGBStream(), submeshCount);
+        DuplicateVertices(proceduralMesh.m_bitangents, proceduralMesh.GetAlignedVertCountForRGBStream(), submeshCount);
+        
+        size_t alignedTangentVertCount = RPI::ModelAssetHelpers::GetAlignedCount<float>(verticesPerSubmesh, RPI::TangentFormat, RPI::SkinnedMeshBufferAlignment);
+        DuplicateVertices(proceduralMesh.m_tangents, aznumeric_cast<uint32_t>(alignedTangentVertCount), submeshCount);
 
-        // Insert the jointId padding first before duplicating
-        AZStd::vector<uint32_t> extraIds(extraPackedIdCount, 0);
+        DuplicateVertices(proceduralMesh.m_uvs, verticesPerSubmesh, submeshCount);
 
-        // Track the count of 32-byte 'elements' (packed) and offsets for creating sub-mesh views
-        uint32_t jointIdElementCountPerSubmesh = aznumeric_caster(proceduralMesh.m_blendIndices.size());
-        uint32_t jointIdOffsetElementsPerSubmesh = jointIdElementCountPerSubmesh + extraPackedIdCount;
+        uint32_t numBlendWeights = verticesPerSubmesh * proceduralMesh.GetInfluencesPerVertex();
+        size_t alignedWeightsVertCount = RPI::ModelAssetHelpers::GetAlignedCount<float>(numBlendWeights, RPI::SkinWeightFormat, RPI::SkinnedMeshBufferAlignment);
+        DuplicateVertices(proceduralMesh.m_blendWeights, aznumeric_cast<uint32_t>(alignedWeightsVertCount), submeshCount);
 
-        proceduralMesh.m_blendIndices.insert(proceduralMesh.m_blendIndices.end(), extraIds.begin(), extraIds.end());
-        DuplicateVertices(
-            proceduralMesh.m_blendIndices, aznumeric_caster(proceduralMesh.m_blendIndices.size()), submeshCount);
+        uint32_t numBlendIndices = verticesPerSubmesh * proceduralMesh.GetInfluencesPerVertex();
+        size_t alignedIndicesVertCount = RPI::ModelAssetHelpers::GetAlignedCount<uint32_t>(numBlendIndices, RPI::SkinIndicesFormat, RPI::SkinnedMeshBufferAlignment);
+        DuplicateVertices(proceduralMesh.m_blendIndices, aznumeric_cast<uint32_t>(alignedIndicesVertCount), submeshCount);
 
         // Offset duplicate positions in the +y direction, so each sub-mesh ends up in a unique position
         for (uint32_t subMeshIndex = 1; subMeshIndex < submeshCount; ++subMeshIndex)
         {
-            for (uint32_t i = 0; i < verticesPerSubmesh; ++i)
+            for (uint32_t i = 0; i < proceduralMesh.GetVertexCount(); i ++)
             {
-                proceduralMesh.m_positions[subMeshIndex*verticesPerSubmesh + i][1] +=
-                    aznumeric_cast<float>(subMeshIndex) * proceduralMesh.GetSubMeshYOffset();
+                uint32_t yPosPerSubMesh = (proceduralMesh.GetAlignedVertCountForRGBStream() * subMeshIndex) + (i*3) + 1;
+                proceduralMesh.m_positions[yPosPerSubMesh] +=
+                        aznumeric_cast<float>(subMeshIndex) * proceduralMesh.GetSubMeshYOffset();
             }
         }
 
+        size_t positionStreamSize = proceduralMesh.m_positions.size() / RHI::GetFormatComponentCount(RPI::PositionFormat);
+        size_t normalStreamSize = proceduralMesh.m_normals.size() / RHI::GetFormatComponentCount(RPI::NormalFormat);
+        size_t tangentStreamSize = proceduralMesh.m_tangents.size() / RHI::GetFormatComponentCount(RPI::TangentFormat);
+        size_t bitangentStreamSize = proceduralMesh.m_bitangents.size() / RHI::GetFormatComponentCount(RPI::BitangentFormat);
+        
         auto indexBuffer = CreateTypedBufferAsset(proceduralMesh.m_indices.data(), proceduralMesh.m_indices.size(), AZ::RHI::Format::R32_FLOAT);
-        auto positionBuffer = CreateTypedBufferAsset(proceduralMesh.m_positions.data(), proceduralMesh.m_positions.size(), AZ::RHI::Format::R32G32B32_FLOAT);
-        auto normalBuffer = CreateTypedBufferAsset(proceduralMesh.m_normals.data(), proceduralMesh.m_normals.size(), AZ::RHI::Format::R32G32B32_FLOAT);
-        auto tangentBuffer = CreateTypedBufferAsset(proceduralMesh.m_tangents.data(), proceduralMesh.m_tangents.size(), AZ::RHI::Format::R32G32B32A32_FLOAT);
-        auto bitangentBuffer = CreateTypedBufferAsset(proceduralMesh.m_bitangents.data(), proceduralMesh.m_bitangents.size(), AZ::RHI::Format::R32G32B32_FLOAT);
-        auto uvBuffer = CreateTypedBufferAsset(proceduralMesh.m_uvs.data(), proceduralMesh.m_uvs.size(), AZ::RHI::Format::R32G32_FLOAT);
+        
+        auto positionBuffer = CreateTypedBufferAsset(proceduralMesh.m_positions.data(), positionStreamSize, RPI::PositionFormat);
+        auto normalBuffer = CreateTypedBufferAsset(proceduralMesh.m_normals.data(), normalStreamSize, RPI::NormalFormat);
+        auto tangentBuffer = CreateTypedBufferAsset(proceduralMesh.m_tangents.data(), tangentStreamSize, RPI::TangentFormat);
+        auto bitangentBuffer = CreateTypedBufferAsset(proceduralMesh.m_bitangents.data(), bitangentStreamSize, RPI::BitangentFormat);
+        auto uvBuffer = CreateTypedBufferAsset(proceduralMesh.m_uvs.data(), proceduralMesh.m_uvs.size(), RPI::UVFormat);
         auto skinJointIdBuffer = CreateRawBufferAsset(proceduralMesh.m_blendIndices.data(), proceduralMesh.m_blendIndices.size(), sizeof(proceduralMesh.m_blendIndices[0]));
-        auto skinJointWeightBuffer = CreateTypedBufferAsset(proceduralMesh.m_blendWeights.data(), proceduralMesh.m_blendWeights.size(), AZ::RHI::Format::R32_FLOAT);
+        auto skinJointWeightBuffer = CreateTypedBufferAsset(proceduralMesh.m_blendWeights.data(), proceduralMesh.m_blendWeights.size(), RPI::SkinWeightFormat);
 
         //
         // Lod
@@ -183,6 +186,8 @@ namespace AtomSampleViewer
             // Get the element count and offset for this sub-mesh
             uint32_t elementCount = verticesPerSubmesh;
             uint32_t elementOffset = verticesPerSubmesh * submeshIndex;
+            uint32_t alignedRGBElementOffset = (proceduralMesh.GetAlignedVertCountForRGBStream()/RHI::GetFormatComponentCount(RHI::Format::R32G32B32_FLOAT)) * submeshIndex;
+            uint32_t alignedRGBAElementOffset = (proceduralMesh.GetAlignedVertCountForRGBAStream()/RHI::GetFormatComponentCount(RHI::Format::R32G32B32A32_FLOAT)) * submeshIndex;
 
             // Include any truncated vertices if this is the last mesh
             if (submeshIndex == submeshCount - 1)
@@ -190,17 +195,24 @@ namespace AtomSampleViewer
                 elementCount += totalVertices % verticesPerSubmesh;
             }
 
-            modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "POSITION" }, AZ::Name(), AZ::RPI::BufferAssetView{ positionBuffer, CreateSubmeshBufferViewDescriptor(positionBuffer, elementCount, elementOffset) });
-            modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "NORMAL" }, AZ::Name(), AZ::RPI::BufferAssetView{ normalBuffer, CreateSubmeshBufferViewDescriptor(normalBuffer, elementCount, elementOffset) });
-            modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "TANGENT" }, AZ::Name(), AZ::RPI::BufferAssetView{ tangentBuffer, CreateSubmeshBufferViewDescriptor(tangentBuffer, elementCount, elementOffset) });
-            modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "BITANGENT" }, AZ::Name(), AZ::RPI::BufferAssetView{ bitangentBuffer, CreateSubmeshBufferViewDescriptor(bitangentBuffer, elementCount, elementOffset) });
-            modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "UV" }, AZ::Name(), AZ::RPI::BufferAssetView{ uvBuffer, CreateSubmeshBufferViewDescriptor(uvBuffer, elementCount, elementOffset) });
-            modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "SKIN_JOINTINDICES" }, AZ::Name(), AZ::RPI::BufferAssetView{ skinJointIdBuffer, CreateSubmeshBufferViewDescriptor(skinJointIdBuffer, jointIdElementCountPerSubmesh, jointIdOffsetElementsPerSubmesh * submeshIndex) });
-
-            uint32_t jointWeightElementCount = elementCount * proceduralMesh.GetInfluencesPerVertex();
-            uint32_t jointWeightOffset = elementOffset * proceduralMesh.GetInfluencesPerVertex();
-            modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "SKIN_WEIGHTS" }, AZ::Name(), AZ::RPI::BufferAssetView{ skinJointWeightBuffer, CreateSubmeshBufferViewDescriptor(skinJointWeightBuffer, jointWeightElementCount, jointWeightOffset) });
+            modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "POSITION" }, AZ::Name(), AZ::RPI::BufferAssetView{ positionBuffer, CreateSubmeshBufferViewDescriptor(positionBuffer, elementCount, alignedRGBElementOffset) });
+            
+            modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "NORMAL" }, AZ::Name(), AZ::RPI::BufferAssetView{ normalBuffer, CreateSubmeshBufferViewDescriptor(normalBuffer, elementCount, alignedRGBElementOffset) });
+ 
 
+            modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "TANGENT" }, AZ::Name(), AZ::RPI::BufferAssetView{ tangentBuffer, CreateSubmeshBufferViewDescriptor(tangentBuffer, elementCount, alignedRGBAElementOffset) });
+                        
+            modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "BITANGENT" }, AZ::Name(), AZ::RPI::BufferAssetView{ bitangentBuffer, CreateSubmeshBufferViewDescriptor(bitangentBuffer, elementCount, alignedRGBElementOffset) });
+            
+            modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "UV" }, AZ::Name(), AZ::RPI::BufferAssetView{ uvBuffer, CreateSubmeshBufferViewDescriptor(uvBuffer, elementCount, elementOffset) });
+            
+            //Divide by 2 as we are storing 16bit data into 32bit integers
+            uint32_t numIndices = numBlendIndices/2;
+            modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "SKIN_JOINTINDICES" }, AZ::Name(), AZ::RPI::BufferAssetView{ skinJointIdBuffer, CreateSubmeshBufferViewDescriptor(skinJointIdBuffer, numIndices, aznumeric_cast<uint32_t>(alignedIndicesVertCount) * submeshIndex) });
+            
+            uint32_t jointWeightOffset = aznumeric_cast<uint32_t>(alignedWeightsVertCount) * submeshIndex;
+            modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "SKIN_WEIGHTS" }, AZ::Name(), AZ::RPI::BufferAssetView{ skinJointWeightBuffer, CreateSubmeshBufferViewDescriptor(skinJointWeightBuffer, numBlendWeights, jointWeightOffset) });
+            
             AZ::Aabb localAabb = proceduralMesh.m_aabb;
             modelLodCreator.SetMeshAabb(AZStd::move(localAabb));
 

+ 31 - 32
Gem/Code/Source/RHI/AlphaToCoverageExampleComponent.cpp

@@ -50,12 +50,12 @@ namespace AtomSampleViewer
     {
         using namespace AZ;
         RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
-        m_inputAssemblyBufferPool = RHI::Factory::Get().CreateBufferPool();
+        m_inputAssemblyBufferPool = aznew RHI::BufferPool();
 
         RHI::BufferPoolDescriptor bufferPoolDesc;
         bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
         bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-        m_inputAssemblyBufferPool->Init(*device, bufferPoolDesc);
+        m_inputAssemblyBufferPool->Init(bufferPoolDesc);
 
         m_depthImageAttachmentId = RHI::AttachmentId("A2C_Depth");
         m_multisamleDepthImageAttachmentId = RHI::AttachmentId("A2C_MSAA_Depth");
@@ -145,7 +145,7 @@ namespace AtomSampleViewer
         RectangleBufferData bufferData;
         SetFullScreenRect(bufferData.m_positions.data(), bufferData.m_uvs.data(), bufferData.m_indices.data());
 
-        m_rectangleInputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
+        m_rectangleInputAssemblyBuffer = aznew RHI::Buffer();
 
         RHI::ResultCode result = RHI::ResultCode::Success;
         RHI::BufferInitRequest request;
@@ -158,22 +158,31 @@ namespace AtomSampleViewer
             AZ_Error(AlphaToCoverage::SampleName, false, "Failed to initialize position buffer with error code %d", result);
             return;
         }
-        
-        m_rectangleStreamBufferViews[0] = {
+
+        m_geometryView.SetDrawArguments(RHI::DrawIndexed(0, 6, 0));
+
+        m_geometryView.SetIndexBufferView({
+            *m_rectangleInputAssemblyBuffer,
+            offsetof(RectangleBufferData, m_indices),
+            sizeof(RectangleBufferData::m_indices),
+            RHI::IndexFormat::Uint16
+        });
+
+        m_geometryView.AddStreamBufferView({
             *m_rectangleInputAssemblyBuffer,
             offsetof(RectangleBufferData, m_positions),
             sizeof(RectangleBufferData::m_positions),
             sizeof(VertexPosition)
-        };
+        });
         
-        m_rectangleStreamBufferViews[1] = {
+        m_geometryView.AddStreamBufferView({
             *m_rectangleInputAssemblyBuffer,
             offsetof(RectangleBufferData, m_uvs),
             sizeof(RectangleBufferData::m_uvs),
             sizeof(VertexUV)
-        };
+        });
         
-        RHI::ValidateStreamBufferViews(m_rectangleInputStreamLayout, m_rectangleStreamBufferViews);
+        RHI::ValidateStreamBufferViews(m_rectangleInputStreamLayout, m_geometryView, m_geometryView.GetFullStreamBufferIndices());
 
         m_shader = LoadShader(AlphaToCoverage::ShaderFilePath, AlphaToCoverage::SampleName);
         if (!m_shader)
@@ -353,7 +362,9 @@ namespace AtomSampleViewer
                 descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Clear;
                 descriptor.m_loadStoreAction.m_clearValue = RHI::ClearValue::CreateDepth(1.f);
                 descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::Store;
-                frameGraph.UseDepthStencilAttachment(descriptor, RHI::ScopeAttachmentAccess::ReadWrite);
+                frameGraph.UseDepthStencilAttachment(
+                    descriptor, RHI::ScopeAttachmentAccess::ReadWrite,
+                    AZ::RHI::ScopeAttachmentStage::EarlyFragmentTest | AZ::RHI::ScopeAttachmentStage::LateFragmentTest);
             }
 
             // Bind the resolve attachment
@@ -382,33 +393,21 @@ namespace AtomSampleViewer
             commandList->SetViewports(&m_viewport, 1);
             commandList->SetScissors(&m_scissor, 1);
 
-            RHI::DrawIndexed drawIndexed;
-            drawIndexed.m_indexCount = 6;
-            drawIndexed.m_instanceCount = 1;
-
             for (uint32_t rectIndex = context.GetSubmitRange().m_startIndex; rectIndex < context.GetSubmitRange().m_endIndex; ++rectIndex)
             {
-                const RHI::ShaderResourceGroup* shaderResourceGroups[] = {m_shaderResourceGroups[typeIndex][rectIndex]->GetRHIShaderResourceGroup()};
-
-                RHI::DrawItem drawItem;
-                drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_pipelineStates[typeIndex].get();
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[typeIndex][rectIndex]
+                                                                                     ->GetRHIShaderResourceGroup()
+                                                                                     ->GetDeviceShaderResourceGroup(
+                                                                                         context.GetDeviceIndex())
+                                                                                     .get() };
+
+                RHI::DeviceDrawItem drawItem;
+                drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+                drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
+                drawItem.m_pipelineState = m_pipelineStates[typeIndex]->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                 drawItem.m_shaderResourceGroups = shaderResourceGroups;
 
-                const RHI::IndexBufferView indexBufferView =
-                {
-                    *m_rectangleInputAssemblyBuffer,
-                    offsetof(RectangleBufferData, m_indices),
-                    sizeof(RectangleBufferData::m_indices),
-                    RHI::IndexFormat::Uint16
-                };
-                drawItem.m_indexBufferView = &indexBufferView;
-
-                AZStd::array<AZ::RHI::StreamBufferView, 2>& streamBufferViews = m_rectangleStreamBufferViews;
-                drawItem.m_streamBufferViewCount = static_cast<uint8_t>(streamBufferViews.size());
-                drawItem.m_streamBufferViews = streamBufferViews.data();
-
                 // Submit the rectangle draw item.
                 commandList->Submit(drawItem);
             }

+ 1 - 1
Gem/Code/Source/RHI/AlphaToCoverageExampleComponent.h

@@ -93,7 +93,7 @@ namespace AtomSampleViewer
         };
         AZ::RHI::Ptr<AZ::RHI::BufferPool> m_inputAssemblyBufferPool;
         AZ::RHI::Ptr<AZ::RHI::Buffer> m_rectangleInputAssemblyBuffer;
-        AZStd::array<AZ::RHI::StreamBufferView, 2> m_rectangleStreamBufferViews;
+        AZ::RHI::GeometryView m_geometryView;
         AZ::RHI::InputStreamLayout m_rectangleInputStreamLayout;
 
         // Shader Resource

+ 129 - 124
Gem/Code/Source/RHI/AsyncComputeExampleComponent.cpp

@@ -144,16 +144,14 @@ namespace AtomSampleViewer
 
     void AsyncComputeExampleComponent::CreateSceneRenderTargets()
     {
-        const RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
-
-        m_imagePool = RHI::Factory::Get().CreateImagePool();
+        m_imagePool = aznew RHI::ImagePool();
         RHI::ImagePoolDescriptor imagePoolDesc;
         imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::Color | RHI::ImageBindFlags::ShaderReadWrite;
-        m_imagePool->Init(*device, imagePoolDesc);
+        m_imagePool->Init(imagePoolDesc);
 
         for (auto& image : m_sceneImages)
         {
-            image = RHI::Factory::Get().CreateImage();
+            image = aznew RHI::Image();
             RHI::ImageInitRequest initImageRequest;
             RHI::ClearValue clearValue = RHI::ClearValue::CreateVector4Float(0, 0, 0, 0);
             initImageRequest.m_image = image.get();
@@ -169,13 +167,11 @@ namespace AtomSampleViewer
 
     void AsyncComputeExampleComponent::CreateQuad()
     {
-        const RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
-
-        m_quadBufferPool = RHI::Factory::Get().CreateBufferPool();
+        m_quadBufferPool = aznew RHI::BufferPool();
         RHI::BufferPoolDescriptor bufferPoolDesc;
         bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
         bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-        m_quadBufferPool->Init(*device, bufferPoolDesc);
+        m_quadBufferPool->Init(bufferPoolDesc);
 
         struct BufferData
         {
@@ -193,7 +189,7 @@ namespace AtomSampleViewer
             uv.m_uv[1] = 1.0f - uv.m_uv[1];
         }
 
-        m_quadInputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
+        m_quadInputAssemblyBuffer = aznew RHI::Buffer();
         RHI::ResultCode result = RHI::ResultCode::Success;
         RHI::BufferInitRequest request;
 
@@ -231,20 +227,26 @@ namespace AtomSampleViewer
             sizeof(VertexUV)
         };
 
-        m_quadStreamBufferViews[ShadowScope].push_back(positionsBufferView);
-        m_quadStreamBufferViews[ForwardScope].push_back(positionsBufferView);
-        m_quadStreamBufferViews[ForwardScope].push_back(normalsBufferView);
-        m_quadStreamBufferViews[CopyTextureScope].push_back(positionsBufferView);
-        m_quadStreamBufferViews[CopyTextureScope].push_back(uvsBufferView);
-        m_quadStreamBufferViews[LuminanceMapScope] = m_quadStreamBufferViews[CopyTextureScope];
+        m_geometryViews[ShadowScope].AddStreamBufferView(positionsBufferView);
+        m_geometryViews[ForwardScope].AddStreamBufferView(positionsBufferView);
+        m_geometryViews[ForwardScope].AddStreamBufferView(normalsBufferView);
+        m_geometryViews[CopyTextureScope].AddStreamBufferView(positionsBufferView);
+        m_geometryViews[CopyTextureScope].AddStreamBufferView(uvsBufferView);
+        m_geometryViews[LuminanceMapScope] = m_geometryViews[CopyTextureScope];
 
-        m_quadIndexBufferView =
+        AZ::RHI::IndexBufferView quadIndexBufferView =
         {
             *m_quadInputAssemblyBuffer,
             offsetof(BufferData, m_indices),
             sizeof(BufferData::m_indices),
             RHI::IndexFormat::Uint16
         };
+
+        for (u32 i = 0; i < AsyncComputeScopes::NumScopes; ++i)
+        {
+            m_geometryViews[i].SetDrawArguments(RHI::DrawIndexed(0, 6, 0));
+            m_geometryViews[i].SetIndexBufferView(quadIndexBufferView);
+        }
     }
 
     void AsyncComputeExampleComponent::LoadShaders()
@@ -286,7 +288,7 @@ namespace AtomSampleViewer
     void AsyncComputeExampleComponent::LoadModel()
     {
         // Load the asset
-        auto modelAsset = m_assetLoadManager->GetAsset<AZ::RPI::ModelAsset>("objects/shaderball_simple.azmodel");
+        auto modelAsset = m_assetLoadManager->GetAsset<AZ::RPI::ModelAsset>("objects/shaderball_simple.fbx.azmodel");
         AZ_Assert(modelAsset.IsReady(), "The model asset is supposed to be ready.");
 
         m_model = AZ::RPI::Model::FindOrCreate(modelAsset);
@@ -319,7 +321,8 @@ namespace AtomSampleViewer
 
                 if (!RHI::ValidateStreamBufferViews(
                         pipelineDesc.m_inputStreamLayout, 
-                        m_quadStreamBufferViews[ShadowScope]))
+                        m_geometryViews[ShadowScope],
+                        m_geometryViews[ShadowScope].GetFullStreamBufferIndices()))
                 {
                     AZ_Error(AsyncCompute::sampleName, false, "Invalid stream buffer views for terrain");
                     return;
@@ -338,7 +341,7 @@ namespace AtomSampleViewer
                 Data::Instance<AZ::RPI::ModelLod> modelLod = m_model->GetLods()[0];
                 modelLod->GetStreamsForMesh(
                     pipelineDesc.m_inputStreamLayout,
-                    m_modelStreamBufferViews[ShadowScope],
+                    m_modelStreamBufferIndices[ShadowScope],
                     nullptr,
                     shader->GetInputContract(),
                     0);
@@ -379,7 +382,8 @@ namespace AtomSampleViewer
 
                 if (!RHI::ValidateStreamBufferViews(
                     pipelineDesc.m_inputStreamLayout, 
-                    m_quadStreamBufferViews[ForwardScope]))
+                    m_geometryViews[ForwardScope],
+                    m_geometryViews[ForwardScope].GetFullStreamBufferIndices()))
                 {
                     AZ_Error(AsyncCompute::sampleName, false, "Invalid stream buffer views for terrain");
                     return;
@@ -398,7 +402,7 @@ namespace AtomSampleViewer
                 Data::Instance<AZ::RPI::ModelLod> modelLod = m_model->GetLods()[0];
                 modelLod->GetStreamsForMesh(
                     pipelineDesc.m_inputStreamLayout,
-                    m_modelStreamBufferViews[ForwardScope],
+                    m_modelStreamBufferIndices[ForwardScope],
                     nullptr,
                     shader->GetInputContract(),
                     0);
@@ -439,7 +443,8 @@ namespace AtomSampleViewer
 
             if (!RHI::ValidateStreamBufferViews(
                 pipelineDesc.m_inputStreamLayout,
-                m_quadStreamBufferViews[CopyTextureScope]))
+                m_geometryViews[CopyTextureScope],
+                m_geometryViews[CopyTextureScope].GetFullStreamBufferIndices()))
             {
                 AZ_Error(AsyncCompute::sampleName, false, "Invalid stream buffer views for LuminanceMap");
                 return;
@@ -474,7 +479,8 @@ namespace AtomSampleViewer
 
             if (!RHI::ValidateStreamBufferViews(
                 pipelineDesc.m_inputStreamLayout,
-                m_quadStreamBufferViews[LuminanceMapScope]))
+                m_geometryViews[LuminanceMapScope],
+                m_geometryViews[LuminanceMapScope].GetFullStreamBufferIndices()))
             {
                 AZ_Error(AsyncCompute::sampleName, false, "Invalid stream buffer views for LuminanceMap");
                 return;
@@ -534,7 +540,7 @@ namespace AtomSampleViewer
             {"Shaders/RHI/AsyncComputeLuminanceMap.azshader", azrtti_typeid<RPI::ShaderAsset>()},     // Vertex + Fragment
             {"Shaders/RHI/AsyncComputeLuminanceReduce.azshader", azrtti_typeid<RPI::ShaderAsset>()},  // Compute
             {"Shaders/RHI/AsyncComputeTonemapping.azshader", azrtti_typeid<RPI::ShaderAsset>()},    // Compute
-            {"objects/shaderball_simple.azmodel", azrtti_typeid<AZ::RPI::ModelAsset>()}, // The model
+            {"objects/shaderball_simple.fbx.azmodel", azrtti_typeid<AZ::RPI::ModelAsset>()}, // The model
         };
 
 
@@ -553,6 +559,10 @@ namespace AtomSampleViewer
         // Kickoff asynchronous asset loading, the activation will continue once all assets are available.
         m_assetLoadManager->LoadAssetsAsync(assetList, [&](AZStd::string_view assetName, [[maybe_unused]] bool success, size_t pendingAssetCount)
             {
+                if (m_fullyActivated)
+                {
+                    return;
+                }
                 AZ_Error(AsyncCompute::sampleName, success, "Error loading asset %s, a crash will occur when OnAllAssetsReadyActivate() is called!", assetName.data());
                 AZ_TracePrintf(AsyncCompute::sampleName, "Asset %s loaded %s. Wait for %zu more assets before full activation\n", assetName.data(), success ? "successfully" : "UNSUCCESSFULLY", pendingAssetCount);
                 m_imguiProgressList.RemoveItem(assetName);
@@ -600,9 +610,7 @@ namespace AtomSampleViewer
 
         m_quadBufferPool = nullptr;
         m_quadInputAssemblyBuffer = nullptr;
-        m_quadStreamBufferViews.fill(AZStd::vector<AZ::RHI::StreamBufferView>());
         m_terrainPipelineStates.fill(nullptr);
-        m_modelStreamBufferViews.fill(AZ::RPI::ModelLod::StreamBufferViewList());
         m_modelPipelineStates.fill(nullptr);
         m_model = nullptr;
         m_copyTexturePipelineState = nullptr;
@@ -779,7 +787,7 @@ namespace AtomSampleViewer
                     RHI::ImageScopeAttachmentDescriptor descriptor;
                     descriptor.m_attachmentId = source;
                     descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
-                    frameGraph.UseShaderAttachment(descriptor, RHI::ScopeAttachmentAccess::Read);
+                    frameGraph.UseShaderAttachment(descriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentStage::FragmentShader);
                 }
             }
 
@@ -791,7 +799,7 @@ namespace AtomSampleViewer
             auto& source = m_sceneIds[m_previousSceneImageIndex];
             auto& shaderResourceGroup = m_shaderResourceGroups[CopyTextureScope].front();
 
-            const RHI::ImageView* imageView = context.GetImageView(source);
+            const auto* imageView = context.GetImageView(source);
             shaderResourceGroup->SetImageView(m_copyTextureShaderInputImageIndex, imageView);
             shaderResourceGroup->Compile();
         };
@@ -803,21 +811,19 @@ namespace AtomSampleViewer
             commandList->SetScissors(&m_scissor, 1);
 
             {
-                // Quad
-                RHI::DrawIndexed drawIndexed;
-                drawIndexed.m_indexCount = 6;
-                drawIndexed.m_instanceCount = 1;
-
-                const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[CopyTextureScope].front()->GetRHIShaderResourceGroup() };
-
-                RHI::DrawItem drawItem;
-                drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_copyTexturePipelineState.get();
-                drawItem.m_indexBufferView = &m_quadIndexBufferView;
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[CopyTextureScope]
+                                                                                     .front()
+                                                                                     ->GetRHIShaderResourceGroup()
+                                                                                     ->GetDeviceShaderResourceGroup(
+                                                                                         context.GetDeviceIndex())
+                                                                                     .get() };
+
+                RHI::DeviceDrawItem drawItem;
+                drawItem.m_geometryView = m_geometryViews[CopyTextureScope].GetDeviceGeometryView(context.GetDeviceIndex());
+                drawItem.m_streamIndices = m_geometryViews[CopyTextureScope].GetFullStreamBufferIndices();
+                drawItem.m_pipelineState = m_copyTexturePipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                 drawItem.m_shaderResourceGroups = shaderResourceGroups;
-                drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_quadStreamBufferViews[CopyTextureScope].size());
-                drawItem.m_streamBufferViews = m_quadStreamBufferViews[CopyTextureScope].data();
                 commandList->Submit(drawItem);
             }
        };
@@ -848,7 +854,9 @@ namespace AtomSampleViewer
                 dsDesc.m_imageViewDescriptor.m_overrideFormat = RHI::Format::D32_FLOAT;
                 dsDesc.m_loadStoreAction.m_clearValue = RHI::ClearValue::CreateDepthStencil(1.0f, 0);
                 dsDesc.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Clear;
-                frameGraph.UseDepthStencilAttachment(dsDesc, RHI::ScopeAttachmentAccess::ReadWrite);
+                frameGraph.UseDepthStencilAttachment(
+                    dsDesc, RHI::ScopeAttachmentAccess::ReadWrite,
+                    RHI::ScopeAttachmentStage::EarlyFragmentTest | RHI::ScopeAttachmentStage::LateFragmentTest);
             }
 
             frameGraph.SetEstimatedItemCount(static_cast<uint32_t>(m_shaderResourceGroups[ShadowScope].size()));
@@ -871,38 +879,36 @@ namespace AtomSampleViewer
             {
                 if (i == 0)
                 {
-                    // Terrain
-                    RHI::DrawIndexed drawIndexed;
-                    drawIndexed.m_indexCount = 6;
-                    drawIndexed.m_instanceCount = 1;
-
-                    const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[ShadowScope][0]->GetRHIShaderResourceGroup() };
-
-                    RHI::DrawItem drawItem;
-                    drawItem.m_arguments = drawIndexed;
-                    drawItem.m_pipelineState = m_terrainPipelineStates[ShadowScope].get();
-                    drawItem.m_indexBufferView = &m_quadIndexBufferView;
+                    const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[ShadowScope][0]
+                                                                                         ->GetRHIShaderResourceGroup()
+                                                                                         ->GetDeviceShaderResourceGroup(
+                                                                                             context.GetDeviceIndex())
+                                                                                         .get() };
+
+                    RHI::DeviceDrawItem drawItem;
+                    drawItem.m_geometryView = m_geometryViews[ShadowScope].GetDeviceGeometryView(context.GetDeviceIndex());
+                    drawItem.m_streamIndices = m_geometryViews[ShadowScope].GetFullStreamBufferIndices();
+                    drawItem.m_pipelineState = m_terrainPipelineStates[ShadowScope]->GetDevicePipelineState(context.GetDeviceIndex()).get();
                     drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                     drawItem.m_shaderResourceGroups = shaderResourceGroups;
-                    drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_quadStreamBufferViews[ShadowScope].size());
-                    drawItem.m_streamBufferViews = m_quadStreamBufferViews[ShadowScope].data();
                     commandList->Submit(drawItem, i);
                 }
                 else
                 {
                     // Models
-                    const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[ShadowScope][i]->GetRHIShaderResourceGroup() };
-                    for (const auto& mesh : m_model->GetLods()[0]->GetMeshes())
+                    const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[ShadowScope][i]
+                                                                                         ->GetRHIShaderResourceGroup()
+                                                                                         ->GetDeviceShaderResourceGroup(
+                                                                                             context.GetDeviceIndex())
+                                                                                         .get() };
+                    for (auto& mesh : m_model->GetLods()[0]->GetMeshes())
                     {
-                        RHI::DrawItem drawItem;
-                        drawItem.m_arguments = mesh.m_drawArguments;
-                        drawItem.m_pipelineState = m_modelPipelineStates[ShadowScope].get();
-                        drawItem.m_indexBufferView = &mesh.m_indexBufferView;
+                        RHI::DeviceDrawItem drawItem;
+                        drawItem.m_geometryView = mesh.GetDeviceGeometryView(context.GetDeviceIndex());
+                        drawItem.m_streamIndices = m_modelStreamBufferIndices[ShadowScope];
+                        drawItem.m_pipelineState = m_modelPipelineStates[ShadowScope]->GetDevicePipelineState(context.GetDeviceIndex()).get();
                         drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                         drawItem.m_shaderResourceGroups = shaderResourceGroups;
-                        drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_modelStreamBufferViews[ShadowScope].size());
-                        drawItem.m_streamBufferViews = m_modelStreamBufferViews[ShadowScope].data();
-
                         commandList->Submit(drawItem, i);
                     }
                 }
@@ -938,7 +944,9 @@ namespace AtomSampleViewer
 
             // Binds depth buffer from depth pass
             {
-                frameGraph.UseShaderAttachment(RHI::ImageScopeAttachmentDescriptor(m_shadowAttachmentId), RHI::ScopeAttachmentAccess::Read);
+                frameGraph.UseShaderAttachment(
+                    RHI::ImageScopeAttachmentDescriptor(m_shadowAttachmentId), RHI::ScopeAttachmentAccess::Read,
+                    RHI::ScopeAttachmentStage::FragmentShader);
             }
 
             // Binds DepthStencil image
@@ -950,7 +958,8 @@ namespace AtomSampleViewer
                 dsDesc.m_loadStoreAction.m_clearValue = RHI::ClearValue::CreateDepthStencil(0, 0);
                 dsDesc.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Clear;
                 dsDesc.m_loadStoreAction.m_loadActionStencil = RHI::AttachmentLoadAction::Clear;
-                frameGraph.UseDepthStencilAttachment(dsDesc, RHI::ScopeAttachmentAccess::Write);
+                frameGraph.UseDepthStencilAttachment(
+                    dsDesc, RHI::ScopeAttachmentAccess::Write, RHI::ScopeAttachmentStage::EarlyFragmentTest | RHI::ScopeAttachmentStage::LateFragmentTest);
             }
 
             frameGraph.SetEstimatedItemCount(static_cast<uint32_t>(m_shaderResourceGroups[ForwardScope].size()));
@@ -958,7 +967,7 @@ namespace AtomSampleViewer
 
         const auto compileFunction = [this](const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
         {
-            const RHI::ImageView* imageView = context.GetImageView(m_shadowAttachmentId);
+            const auto* imageView = context.GetImageView(m_shadowAttachmentId);
 
             for (const auto& shaderResourceGroup : m_shaderResourceGroups[ForwardScope])
             {
@@ -972,7 +981,7 @@ namespace AtomSampleViewer
             RHI::CommandList* commandList = context.GetCommandList();
 
             // Bind ViewSrg
-            commandList->SetShaderResourceGroupForDraw(*m_viewShaderResourceGroup->GetRHIShaderResourceGroup());
+            commandList->SetShaderResourceGroupForDraw(*m_viewShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get());
 
             // Set persistent viewport and scissor state.
             const auto& imageSize = m_sceneImages[m_currentSceneImageIndex]->GetDescriptor().m_size;
@@ -986,47 +995,41 @@ namespace AtomSampleViewer
                 if (i == 0)
                 {
                     // Terrain
-                    const RHI::ShaderResourceGroup* shaderResourceGroups[] =
-                    {
-                        m_shaderResourceGroups[ForwardScope][0]->GetRHIShaderResourceGroup(),
-                        m_viewShaderResourceGroup->GetRHIShaderResourceGroup()
+                    const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                        m_shaderResourceGroups[ForwardScope][0]
+                            ->GetRHIShaderResourceGroup()
+                            ->GetDeviceShaderResourceGroup(context.GetDeviceIndex())
+                            .get(),
+                        m_viewShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
                     };
 
-                    RHI::DrawIndexed drawIndexed;
-                    drawIndexed.m_indexCount = 6;
-                    drawIndexed.m_instanceCount = 1;
-
-                    RHI::DrawItem drawItem;
-                    drawItem.m_arguments = drawIndexed;
-                    drawItem.m_pipelineState = m_terrainPipelineStates[ForwardScope].get();
-                    drawItem.m_indexBufferView = &m_quadIndexBufferView;
+                    RHI::DeviceDrawItem drawItem;
+                    drawItem.m_geometryView = m_geometryViews[ForwardScope].GetDeviceGeometryView(context.GetDeviceIndex());
+                    drawItem.m_streamIndices = m_geometryViews[ForwardScope].GetFullStreamBufferIndices();
+                    drawItem.m_pipelineState = m_terrainPipelineStates[ForwardScope]->GetDevicePipelineState(context.GetDeviceIndex()).get();
                     drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                     drawItem.m_shaderResourceGroups = shaderResourceGroups;
-                    drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_quadStreamBufferViews[ForwardScope].size());
-                    drawItem.m_streamBufferViews = m_quadStreamBufferViews[ForwardScope].data();
-
                     commandList->Submit(drawItem, i);
                 }
                 else
                 {
                     // Model
-                    const RHI::ShaderResourceGroup* shaderResourceGroups[] =
-                    {
-                        m_shaderResourceGroups[ForwardScope][i]->GetRHIShaderResourceGroup(),
-                        m_viewShaderResourceGroup->GetRHIShaderResourceGroup()
+                    const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                        m_shaderResourceGroups[ForwardScope][i]
+                            ->GetRHIShaderResourceGroup()
+                            ->GetDeviceShaderResourceGroup(context.GetDeviceIndex())
+                            .get(),
+                        m_viewShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
                     };
 
-                    for (const auto& mesh : m_model->GetLods()[0]->GetMeshes())
+                    for (auto& mesh : m_model->GetLods()[0]->GetMeshes())
                     {
-                        RHI::DrawItem drawItem;
-                        drawItem.m_arguments = mesh.m_drawArguments;
-                        drawItem.m_pipelineState = m_modelPipelineStates[ForwardScope].get();
-                        drawItem.m_indexBufferView = &mesh.m_indexBufferView;
+                        RHI::DeviceDrawItem drawItem;
+                        drawItem.m_geometryView = mesh.GetDeviceGeometryView(context.GetDeviceIndex());
+                        drawItem.m_streamIndices = m_modelStreamBufferIndices[ForwardScope];
+                        drawItem.m_pipelineState = m_modelPipelineStates[ForwardScope]->GetDevicePipelineState(context.GetDeviceIndex()).get();
                         drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                         drawItem.m_shaderResourceGroups = shaderResourceGroups;
-                        drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_modelStreamBufferViews[ForwardScope].size());
-                        drawItem.m_streamBufferViews = m_modelStreamBufferViews[ForwardScope].data();
-
                         commandList->Submit(drawItem, i);
                     }
                 }
@@ -1055,12 +1058,14 @@ namespace AtomSampleViewer
                 RHI::ImageScopeAttachmentDescriptor inputOuputDescriptor;
                 inputOuputDescriptor.m_attachmentId = m_sceneIds[m_previousSceneImageIndex];
                 inputOuputDescriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
-                frameGraph.UseShaderAttachment(inputOuputDescriptor, RHI::ScopeAttachmentAccess::ReadWrite);
+                frameGraph.UseShaderAttachment(
+                    inputOuputDescriptor, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::ComputeShader);
 
                 RHI::ImageScopeAttachmentDescriptor luminanceDescriptor;
                 luminanceDescriptor.m_attachmentId = m_averageLuminanceAttachmentId;
                 luminanceDescriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
-                frameGraph.UseShaderAttachment(luminanceDescriptor, RHI::ScopeAttachmentAccess::Read);
+                frameGraph.UseShaderAttachment(
+                    luminanceDescriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentStage::ComputeShader);
             }
 
             frameGraph.SetEstimatedItemCount(1);
@@ -1069,8 +1074,8 @@ namespace AtomSampleViewer
 
         const auto compileFunction = [this](const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
         {
-            const RHI::ImageView* hdrSceneView = context.GetImageView(m_sceneIds[m_previousSceneImageIndex]);
-            const RHI::ImageView* luminanceView = context.GetImageView(m_averageLuminanceAttachmentId);
+            const auto* hdrSceneView = context.GetImageView(m_sceneIds[m_previousSceneImageIndex]);
+            const auto* luminanceView = context.GetImageView(m_averageLuminanceAttachmentId);
 
             for (const auto& shaderResourceGroup : m_shaderResourceGroups[TonemappingScope])
             {
@@ -1084,8 +1089,8 @@ namespace AtomSampleViewer
         {
             RHI::CommandList* commandList = context.GetCommandList();
 
-            RHI::DispatchItem dispatchItem;
-            decltype(dispatchItem.m_shaderResourceGroups) shaderResourceGroups = { { m_shaderResourceGroups[TonemappingScope][0]->GetRHIShaderResourceGroup() } };
+            RHI::DeviceDispatchItem dispatchItem;
+            decltype(dispatchItem.m_shaderResourceGroups) shaderResourceGroups = { { m_shaderResourceGroups[TonemappingScope][0]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get() } };
 
             RHI::DispatchDirect dispatchArgs;
 
@@ -1100,7 +1105,7 @@ namespace AtomSampleViewer
             AZ_Assert(dispatchArgs.m_threadsPerGroupZ == 1, "If the shader source changes, this logic should change too.");
 
             dispatchItem.m_arguments = dispatchArgs;
-            dispatchItem.m_pipelineState = m_tonemappingPipelineState.get();
+            dispatchItem.m_pipelineState = m_tonemappingPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
             dispatchItem.m_shaderResourceGroupCount = 1;
             dispatchItem.m_shaderResourceGroups = shaderResourceGroups;
 
@@ -1135,7 +1140,7 @@ namespace AtomSampleViewer
                 RHI::ImageScopeAttachmentDescriptor sceneDescriptor;
                 sceneDescriptor.m_attachmentId = m_sceneIds[m_previousSceneImageIndex];
                 sceneDescriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
-                frameGraph.UseShaderAttachment(sceneDescriptor, RHI::ScopeAttachmentAccess::Read);
+                frameGraph.UseShaderAttachment(sceneDescriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentStage::FragmentShader);
             }
 
             frameGraph.SetEstimatedItemCount(1);
@@ -1143,7 +1148,7 @@ namespace AtomSampleViewer
 
         const auto compileFunction = [this](const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
         {
-            const RHI::ImageView* imageView = context.GetImageView(m_sceneIds[m_previousSceneImageIndex]);
+            const auto* imageView = context.GetImageView(m_sceneIds[m_previousSceneImageIndex]);
 
             for (const auto& shaderResourceGroup : m_shaderResourceGroups[LuminanceMapScope])
             {
@@ -1163,20 +1168,18 @@ namespace AtomSampleViewer
 
             {
                 // Quad
-                RHI::DrawIndexed drawIndexed;
-                drawIndexed.m_indexCount = 6;
-                drawIndexed.m_instanceCount = 1;
-
-                const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[LuminanceMapScope][0]->GetRHIShaderResourceGroup() };
-
-                RHI::DrawItem drawItem;
-                drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_luminancePipelineState.get();
-                drawItem.m_indexBufferView = &m_quadIndexBufferView;
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[LuminanceMapScope][0]
+                                                                                     ->GetRHIShaderResourceGroup()
+                                                                                     ->GetDeviceShaderResourceGroup(
+                                                                                         context.GetDeviceIndex())
+                                                                                     .get() };
+
+                RHI::DeviceDrawItem drawItem;
+                drawItem.m_geometryView = m_geometryViews[LuminanceMapScope].GetDeviceGeometryView(context.GetDeviceIndex());
+                drawItem.m_streamIndices = m_geometryViews[LuminanceMapScope].GetFullStreamBufferIndices();
+                drawItem.m_pipelineState = m_luminancePipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                 drawItem.m_shaderResourceGroups = shaderResourceGroups;
-                drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_quadStreamBufferViews[LuminanceMapScope].size());
-                drawItem.m_streamBufferViews = m_quadStreamBufferViews[LuminanceMapScope].data();
                 commandList->Submit(drawItem);
             }
         };
@@ -1225,12 +1228,14 @@ namespace AtomSampleViewer
                     RHI::ImageScopeAttachmentDescriptor inputDescriptor;
                     inputDescriptor.m_attachmentId = inputAttachmentId;
                     inputDescriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
-                    frameGraph.UseShaderAttachment(inputDescriptor, RHI::ScopeAttachmentAccess::Read);
+                    frameGraph.UseShaderAttachment(
+                        inputDescriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentStage::ComputeShader);
 
                     RHI::ImageScopeAttachmentDescriptor outputDescriptor;
                     outputDescriptor.m_attachmentId = outputAttachmentId;
                     outputDescriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::DontCare;
-                    frameGraph.UseShaderAttachment(outputDescriptor, RHI::ScopeAttachmentAccess::ReadWrite);
+                    frameGraph.UseShaderAttachment(
+                        outputDescriptor, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::ComputeShader);
                 }
 
                 frameGraph.SetEstimatedItemCount(1);
@@ -1239,8 +1244,8 @@ namespace AtomSampleViewer
 
             const auto compileFunction = [this, inputAttachmentId, outputAttachmentId, i](const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
             {
-                const RHI::ImageView* inputView = context.GetImageView(inputAttachmentId);
-                const RHI::ImageView* outputView = context.GetImageView(outputAttachmentId);
+                const auto* inputView = context.GetImageView(inputAttachmentId);
+                const auto* outputView = context.GetImageView(outputAttachmentId);
 
                 const auto& shaderResourceGroup = m_shaderResourceGroups[LuminanceReduceScope][i];
                 shaderResourceGroup->SetImageView(m_luminanceReduceShaderInputImageIndex, inputView);
@@ -1252,8 +1257,8 @@ namespace AtomSampleViewer
             {
                 RHI::CommandList* commandList = context.GetCommandList();
 
-                RHI::DispatchItem dispatchItem;
-                decltype(dispatchItem.m_shaderResourceGroups) shaderResourceGroups = { { m_shaderResourceGroups[LuminanceReduceScope][i]->GetRHIShaderResourceGroup() } };
+                RHI::DeviceDispatchItem dispatchItem;
+                decltype(dispatchItem.m_shaderResourceGroups) shaderResourceGroups = { { m_shaderResourceGroups[LuminanceReduceScope][i]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get() } };
 
                 RHI::DispatchDirect dispatchArgs;
 
@@ -1267,7 +1272,7 @@ namespace AtomSampleViewer
                 dispatchArgs.m_totalNumberOfThreadsZ = 1;
 
                 dispatchItem.m_arguments = dispatchArgs;
-                dispatchItem.m_pipelineState = m_luminanceReducePipelineState.get();
+                dispatchItem.m_pipelineState = m_luminanceReducePipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 dispatchItem.m_shaderResourceGroupCount = 1;
                 dispatchItem.m_shaderResourceGroups = shaderResourceGroups;
 

+ 2 - 3
Gem/Code/Source/RHI/AsyncComputeExampleComponent.h

@@ -122,14 +122,13 @@ namespace AtomSampleViewer
         // Quad related variables
         AZ::RHI::Ptr<AZ::RHI::BufferPool> m_quadBufferPool;
         AZ::RHI::Ptr<AZ::RHI::Buffer> m_quadInputAssemblyBuffer;
-        AZ::RHI::IndexBufferView m_quadIndexBufferView;
-        AZStd::array<AZStd::vector<AZ::RHI::StreamBufferView>, NumScopes> m_quadStreamBufferViews;
+        AZStd::array<AZ::RHI::GeometryView, NumScopes> m_geometryViews;
 
         // Terrain related variables
         AZStd::array<AZ::RHI::ConstPtr<AZ::RHI::PipelineState>, NumScopes> m_terrainPipelineStates;
 
         // Model related variables
-        AZStd::array<AZ::RPI::ModelLod::StreamBufferViewList, NumScopes> m_modelStreamBufferViews;
+        AZStd::array<AZ::RHI::StreamBufferIndices, NumScopes> m_modelStreamBufferIndices;
         AZStd::array<AZ::RHI::ConstPtr<AZ::RHI::PipelineState>, NumScopes> m_modelPipelineStates;
         AZ::Data::Instance<AZ::RPI::Model> m_model;
 

+ 8 - 4
Gem/Code/Source/RHI/BasicRHIComponent.cpp

@@ -24,7 +24,7 @@ namespace AtomSampleViewer
     RHISamplePass::RHISamplePass(const AZ::RPI::PassDescriptor& descriptor)
         : AZ::RPI::RenderPass(descriptor)
     {
-        m_flags.m_hasPipelineViewTag = true;
+        m_flags.m_bindViewSrg = true;
         m_pipelineViewTag = "MainCamera";
     }
 
@@ -511,7 +511,11 @@ namespace AtomSampleViewer
         return image;
     }
 
-    void BasicRHIComponent::CreateImage3dData(AZStd::vector<uint8_t>& data, AZ::RHI::ImageSubresourceLayout& layout, AZ::RHI::Format& format, AZStd::vector<const char*>&& imageAssetPaths)
+    void BasicRHIComponent::CreateImage3dData(
+        AZStd::vector<uint8_t>& data,
+        AZ::RHI::DeviceImageSubresourceLayout& layout,
+        AZ::RHI::Format& format,
+        AZStd::vector<const char*>&& imageAssetPaths)
     {
         using namespace AZ;
 
@@ -532,7 +536,7 @@ namespace AtomSampleViewer
         AZStd::vector<RHI::ImageDescriptor> imageDescriptors;
         imageDescriptors.reserve(imageAssetPaths.size());
 
-        AZStd::vector<RHI::ImageSubresourceLayout> imageSubresourceLayouts;
+        AZStd::vector<RHI::DeviceImageSubresourceLayout> imageSubresourceLayouts;
         imageSubresourceLayouts.reserve(imageAssetPaths.size());
 
         AZStd::vector<Data::Asset<RPI::ImageMipChainAsset>> imageMipAssets;
@@ -552,7 +556,7 @@ namespace AtomSampleViewer
             mipAsset.BlockUntilLoadComplete();
             imageMipAssets.emplace_back(mipAsset);
 
-            const RHI::ImageSubresourceLayout subImageLayout = mipAsset->GetSubImageLayout(0);
+            const auto subImageLayout = mipAsset->GetSubImageLayout(0);
             imageSubresourceLayouts.emplace_back(subImageLayout);
         }
 

+ 5 - 1
Gem/Code/Source/RHI/BasicRHIComponent.h

@@ -100,7 +100,11 @@ namespace AtomSampleViewer
         ~BasicRHIComponent() override = default;
 
         // Creates a 3D image from 2D images. All 2D images are required to have the same format and layout
-        static void CreateImage3dData(AZStd::vector<uint8_t>& data, AZ::RHI::ImageSubresourceLayout& layout, AZ::RHI::Format& format, AZStd::vector<const char*>&& imageAssetPaths);
+        static void CreateImage3dData(
+            AZStd::vector<uint8_t>& data,
+            AZ::RHI::DeviceImageSubresourceLayout& layout,
+            AZ::RHI::Format& format,
+            AZStd::vector<const char*>&& imageAssetPaths);
 
         void SetOutputInfo(uint32_t width, uint32_t height, AZ::RHI::Format format, AZ::RHI::AttachmentId attachmentId);
 

+ 669 - 205
Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.cpp

@@ -43,11 +43,18 @@ namespace AtomSampleViewer
                                 "textures/streaming/streaming3.dds.streamingimage",
                                 "textures/streaming/streaming4.dds.streamingimage",
                                 "textures/streaming/streaming5.dds.streamingimage",
-                                "textures/streaming/streaming6.dds.streamingimage",
                                 "textures/streaming/streaming7.dds.streamingimage",
+                                "textures/streaming/streaming8.dds.streamingimage",
+        };
+
+        const char* CubeMapImages[] = {
+            "lightingpresets/default_iblskyboxcm.exr.streamingimage", 
+            "lightingpresets/lowcontrast/artist_workshop_4k_iblskyboxcm.exr.streamingimage", 
+            "lightingpresets/lowcontrast/blouberg_sunrise_1_4k_iblskyboxcm.exr.streamingimage"
         };
 
         const uint32_t ImageCount = AZ::RHI::ArraySize(Images);
+        const uint32_t CubeMapImageCount = AZ::RHI::ArraySize(CubeMapImages);
 
         // Randomizer, used to generate unique diffuse colors for the materials
         AZ::SimpleLcgRandom g_randomizer;
@@ -57,6 +64,11 @@ namespace AtomSampleViewer
             return g_randomizer.GetRandom() % ImageCount;
         }
 
+        static uint32_t RandomCubeMapValue()
+        {
+            return g_randomizer.GetRandom() % CubeMapImageCount;
+        }
+
         template<typename Asset, typename Instance>
         Data::Instance<Instance> CreateResourceImmediate(Data::AssetId assetId)
         {
@@ -72,57 +84,60 @@ namespace AtomSampleViewer
             return assetInstance;
         }
 
-        // Simple material structures
+        // Material type to test read only buffer, read write buffer, read write texture
         struct BindlessMaterial0
         {
             BindlessMaterial0()
             {
-                const uint32_t colorUint = g_randomizer.GetRandom();
-                AZ::Color color;
-                color.FromU32(colorUint);
-
-                m_diffuseColor = color;
+                m_colorBufferId = g_randomizer.GetRandom() % 2; //We only have two read only buffers (m_colorBuffer1 and m_colorBuffer2)
+                m_colorBufferMultiplierBufferId = 2; //We only have one read write buffer so the id will 2.
+                m_colorImageMultiplierBufferId = ImageCount + CubeMapImageCount;
             }
 
             const uint32_t m_materialIndex = 0u;
-            AZ::Color m_diffuseColor;
+            // id to to read only buffer
+            uint32_t m_colorBufferId;
+            // id to read write buffer
+            uint32_t m_colorBufferMultiplierBufferId;
+            // id to read write texture
+            uint32_t m_colorImageMultiplierBufferId;
         };
 
+        // Material type to test unbounded array in a non-bindless srg
         struct BindlessMaterial1
         {
             BindlessMaterial1()
             {
-                const uint32_t colorUint = g_randomizer.GetRandom();
-                AZ::Color color;
-                color.FromU32(colorUint);
-
                 m_diffuseTextureIndex = RandomValue();
             }
-
             const uint32_t m_materialIndex = 1u;
-            AZ::Color m_diffuseColor;
+            // id to read only texture
             uint32_t m_diffuseTextureIndex;
         };
 
+        // Material type to test read only texture via Bindless srg
         struct BindlessMaterial2
         {
             BindlessMaterial2()
             {
-                const uint32_t colorUint = g_randomizer.GetRandom();
-                AZ::Color color;
-                color.FromU32(colorUint);
-                m_diffuseColor = color;
-
                 m_diffuseTextureIndex = RandomValue();
-                m_normalTextureIndex = RandomValue();
-                m_specularTextureIndex = RandomValue();
             }
 
             const uint32_t m_materialIndex = 2u;
-            AZ::Color m_diffuseColor;
+            // id to read only texture
             uint32_t m_diffuseTextureIndex;
-            uint32_t m_normalTextureIndex;
-            uint32_t m_specularTextureIndex;
+        };
+
+        // Material type to test read only cubemap texture
+        struct BindlessMaterial3
+        {
+            BindlessMaterial3()
+            {
+                m_cubemapTextureIndex = ImageCount + RandomCubeMapValue();
+            }
+            const uint32_t m_materialIndex = 3u;
+            // id to read only cube map texture
+            uint32_t m_cubemapTextureIndex;
         };
     };
 
@@ -200,25 +215,45 @@ namespace AtomSampleViewer
         CreateObjects();
     }
 
-    void BindlessPrototypeExampleComponent::CreateBufferPool()
+    void BindlessPrototypeExampleComponent::CreatePools()
     {
-        m_bufferPool = RHI::Factory::Get().CreateBufferPool();
-        m_bufferPool->SetName(Name("BindlessBufferPool"));
+        //Create Buffer pool for read only buffers
+        {
+            m_bufferPool = aznew RHI::BufferPool();
+            m_bufferPool->SetName(Name("BindlessBufferPool"));
+            RHI::BufferPoolDescriptor bufferPoolDesc;
+            bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::ShaderRead;
+            bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
+            bufferPoolDesc.m_hostMemoryAccess = RHI::HostMemoryAccess::Write;
+            bufferPoolDesc.m_budgetInBytes = m_poolSizeInBytes;
+            [[maybe_unused]] RHI::ResultCode resultCode = m_bufferPool->Init(bufferPoolDesc);
+            AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to create Material Buffer Pool");
+        }
 
-        RHI::BufferPoolDescriptor bufferPoolDesc;
-        bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::ShaderRead;
-        bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-        bufferPoolDesc.m_hostMemoryAccess = RHI::HostMemoryAccess::Write;
-        bufferPoolDesc.m_budgetInBytes = m_poolSizeInBytes;
+        // Create Buffer pool for read write buffers
+        {
+            m_computeBufferPool = aznew RHI::BufferPool();
+            RHI::BufferPoolDescriptor bufferPoolDesc;
+            bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::ShaderReadWrite;
+            bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
+            bufferPoolDesc.m_hostMemoryAccess = RHI::HostMemoryAccess::Write;
+            [[maybe_unused]] RHI::ResultCode result = m_computeBufferPool->Init(bufferPoolDesc);
+            AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialized compute buffer pool");
+        }
 
-        const RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
-        [[maybe_unused]] RHI::ResultCode resultCode = m_bufferPool->Init(*device, bufferPoolDesc);
-        AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to create Material Buffer Pool");
-    };
+        // Create Image pool for read write images
+        {
+            RHI::ImagePoolDescriptor imagePoolDesc;
+            imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::ShaderReadWrite;
+            m_rwImagePool = aznew RHI::ImagePool();
+            [[maybe_unused]] RHI::ResultCode result = m_rwImagePool->Init(imagePoolDesc);
+            AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize output image pool");
+        }
+    }
 
     void BindlessPrototypeExampleComponent::FloatBuffer::CreateBufferFromPool(const uint32_t byteCount)
     {
-        m_buffer = RHI::Factory::Get().CreateBuffer();
+        m_buffer = aznew RHI::Buffer();
         m_buffer->SetName(Name("FloatBuffer"));
         RHI::BufferInitRequest bufferRequest;
         bufferRequest.m_descriptor.m_bindFlags = RHI::BufferBindFlags::ShaderRead;
@@ -227,7 +262,54 @@ namespace AtomSampleViewer
 
         [[maybe_unused]] RHI::ResultCode resultCode = m_bufferPool->InitBuffer(bufferRequest);
         AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to create Material Buffer");
-    };
+    }
+
+    void BindlessPrototypeExampleComponent::CreateIndirectBuffer(
+        const Name& bufferName,
+        AZ::RHI::Ptr<AZ::RHI::Buffer>& indirectionBuffer,
+        AZ::RHI::Ptr<AZ::RHI::BufferView>& bufferView, 
+        size_t byteSize)
+    {
+        indirectionBuffer = aznew RHI::Buffer();
+        indirectionBuffer->SetName(bufferName);
+        RHI::BufferInitRequest bufferRequest;
+        bufferRequest.m_descriptor.m_bindFlags = RHI::BufferBindFlags::ShaderRead;
+        bufferRequest.m_descriptor.m_byteCount = byteSize;
+        bufferRequest.m_buffer = indirectionBuffer.get();
+        [[maybe_unused]] RHI::ResultCode resultCode = m_bufferPool->InitBuffer(bufferRequest);
+        AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to create Indirection Buffer");
+
+        RHI::BufferViewDescriptor viewDesc =
+            RHI::BufferViewDescriptor::CreateRaw(0, aznumeric_cast<uint32_t>(bufferRequest.m_descriptor.m_byteCount));
+        bufferView = indirectionBuffer->BuildBufferView(viewDesc);
+    }
+
+    void BindlessPrototypeExampleComponent::CreateColorBuffer(
+        const Name& bufferName,
+        const AZ::Vector4& colorVal, 
+        AZ::RHI::Ptr<AZ::RHI::Buffer>& buffer, 
+        AZ::RHI::Ptr<AZ::RHI::BufferView>& bufferView)
+    {
+        AZStd::array<float, 4> randColors;
+        randColors[0] = colorVal.GetX();
+        randColors[1] = colorVal.GetY();
+        randColors[2] = colorVal.GetZ();
+        randColors[3] = colorVal.GetW();
+
+        buffer = aznew RHI::Buffer();
+        buffer->SetName(bufferName);
+        RHI::BufferInitRequest bufferRequest;
+        bufferRequest.m_descriptor.m_bindFlags = RHI::BufferBindFlags::ShaderRead;
+        bufferRequest.m_descriptor.m_byteCount = sizeof(float) * 4;
+        bufferRequest.m_buffer = buffer.get();
+        bufferRequest.m_initialData = randColors.data();
+        [[maybe_unused]] RHI::ResultCode resultCode = m_bufferPool->InitBuffer(bufferRequest);
+        AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to create m_colorBuffer1 Buffer");
+
+        RHI::BufferViewDescriptor viewDesc =
+            RHI::BufferViewDescriptor::CreateRaw(0, aznumeric_cast<uint32_t>(bufferRequest.m_descriptor.m_byteCount));
+        bufferView = buffer->BuildBufferView(viewDesc);
+    }
 
     void BindlessPrototypeExampleComponent::ClearObjects()
     {
@@ -259,17 +341,26 @@ namespace AtomSampleViewer
             const AZ::Name LightHandleId{ "m_lightHandle" };
 
             const uint32_t lodModel = 0u;
-            const ModelLod::Mesh& mesh = m_model->GetLods()[lodModel]->GetMeshes()[subMeshIdx];
+            ModelLod::Mesh& mesh = m_model->GetLods()[lodModel]->GetMeshes()[subMeshIdx];
+
+            auto uvAssetBufferView{ m_model->GetModelAsset()->GetLodAssets()[lodModel]->GetMeshes()[subMeshIdx].GetSemanticBufferAssetView(
+                AZ::Name{ "UV" }) };
+            auto rpiUVBuffer{ AZ::RPI::Buffer::FindOrCreate(uvAssetBufferView->GetBufferAsset()) };
+            const auto* uvBufferView = rpiUVBuffer->GetBufferView();
+            uint32_t uvBufferByteOffset =
+                uvAssetBufferView->GetBufferViewDescriptor().m_elementSize * uvAssetBufferView->GetBufferViewDescriptor().m_elementOffset;
 
             m_subMeshInstanceArray.resize(m_subMeshInstanceArray.size() + 1);
             SubMeshInstance& subMeshInstance = m_subMeshInstanceArray.back();
 
             subMeshInstance.m_perSubMeshSrg = CreateShaderResourceGroup(m_shader, "HandleSrg", InternalBP::SampleName);
             subMeshInstance.m_mesh = &mesh;
+            subMeshInstance.m_uvBufferIndex = uvBufferView->GetDeviceBufferView(RHI::MultiDevice::DefaultDeviceIndex)->GetBindlessReadIndex();
+            subMeshInstance.m_uvBufferByteOffset = uvBufferByteOffset;
 
             // Set the buffer stream
             RHI::InputStreamLayout layout;
-            m_model->GetLods()[lodModel]->GetStreamsForMesh(layout, subMeshInstance.bufferStreamViewArray, nullptr, m_shader->GetInputContract(), subMeshIdx);
+            m_model->GetLods()[lodModel]->GetStreamsForMesh(layout, subMeshInstance.m_streamIndices, nullptr, m_shader->GetInputContract(), subMeshIdx);
         }
     }
 
@@ -309,7 +400,7 @@ namespace AtomSampleViewer
         for (FloatBufferHandle& handle : m_materialHandleArray)
         {
             // Generate a random material type
-            const uint32_t MaterialTypeCount = 3u;
+            const uint32_t MaterialTypeCount = 4u;
             const uint32_t materialTypeIndex = InternalBP::g_randomizer.GetRandom() % MaterialTypeCount;
 
             // Allocate a material
@@ -325,6 +416,11 @@ namespace AtomSampleViewer
             {
                 AllocateMaterial<InternalBP::BindlessMaterial2>(handle);
             }
+            else if (materialTypeIndex == 3u)
+            {
+                AllocateMaterial<InternalBP::BindlessMaterial3>(handle);
+            }
+
             AZ_Assert(handle.IsValid(), "Allocated descriptor is invalid");
         }
     }
@@ -351,6 +447,9 @@ namespace AtomSampleViewer
             AZ_Assert(m_shader, "Shader isn't loaded correctly");
         }
 
+        // Load compute shaders required for compute passes needed to write to read write resources
+        LoadComputeShaders();
+
         // Set the camera
         {
             const auto& viewport = m_windowContext->GetViewport();
@@ -373,7 +472,7 @@ namespace AtomSampleViewer
 
         // Load the Model 
         {
-            const char* modelPath = "objects/shaderball_simple.azmodel";
+            const char* modelPath = "objects/shaderball_simple.fbx.azmodel";
 
             Data::AssetId modelAssetId;
             Data::AssetCatalogRequestBus::BroadcastResult(
@@ -397,8 +496,8 @@ namespace AtomSampleViewer
 #endif
                          }));
 
-        // Create the BufferPool
-        CreateBufferPool();
+        // Create all the needed pools
+        CreatePools();
 
         m_bindlessSrg->CompileSrg(m_samplerSrgName);
 
@@ -407,13 +506,9 @@ namespace AtomSampleViewer
             const uint32_t byteCount = m_bufferFloatCount * static_cast<uint32_t>(sizeof(float));
             m_floatBuffer = std::make_unique<FloatBuffer>(FloatBuffer(m_bufferPool, byteCount));
 
-            AZ::RHI::Ptr<AZ::RHI::BufferView> bufferView = RHI::Factory::Get().CreateBufferView();
-            {
-                bufferView->SetName(Name(m_floatBufferSrgName));
-                RHI::BufferViewDescriptor bufferViewDesc = RHI::BufferViewDescriptor::CreateStructured(0u, m_bufferFloatCount, sizeof(float));
-                [[maybe_unused]] RHI::ResultCode resultCode = bufferView->Init(*m_floatBuffer->m_buffer, bufferViewDesc);
-                AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to initialize buffer view");
-            }
+            RHI::BufferViewDescriptor bufferViewDesc = RHI::BufferViewDescriptor::CreateStructured(0u, m_bufferFloatCount, sizeof(float));
+            AZ::RHI::Ptr<AZ::RHI::BufferView> bufferView = m_floatBuffer->m_buffer->BuildBufferView(bufferViewDesc);
+            bufferView->SetName(Name(m_floatBufferSrgName));
             m_bindlessSrg->SetBufferView(m_floatBufferSrgName, floatBufferId, bufferView.get());
 
             // Compile the float buffer SRG
@@ -424,8 +519,8 @@ namespace AtomSampleViewer
         {
             const uint32_t meshIndex = 0u;
             RHI::InputStreamLayout layout;
-            ModelLod::StreamBufferViewList streamBufferView;
-            m_model->GetLods()[m_modelLod]->GetStreamsForMesh(layout, streamBufferView, nullptr, m_shader->GetInputContract(), meshIndex);
+            RHI::StreamBufferIndices streamBufferIndices;
+            m_model->GetLods()[m_modelLod]->GetStreamsForMesh(layout, streamBufferIndices, nullptr, m_shader->GetInputContract(), meshIndex);
             // Set the pipeline state
             {
                 RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
@@ -459,7 +554,6 @@ namespace AtomSampleViewer
         }
 
         //Load appropriate textures used by the unbounded texture array
-        AZStd::vector<const RHI::ImageView*> imageViews;
         for (uint32_t textureIdx = 0u; textureIdx < InternalBP::ImageCount; textureIdx++)
         {
             AZ::Data::Instance<AZ::RPI::StreamingImage> image = LoadStreamingImage(InternalBP::Images[textureIdx], InternalBP::SampleName);
@@ -467,22 +561,54 @@ namespace AtomSampleViewer
             m_imageViews.push_back(image->GetImageView());
         }
 
-        // Set the indirect buffer for the images
+        // Load appropriate cubemap textures used by the unbounded texture array
+        for (uint32_t textureIdx = 0u; textureIdx < InternalBP::CubeMapImageCount; textureIdx++)
         {
-            m_indirectionBuffer = RHI::Factory::Get().CreateBuffer();
-            m_indirectionBuffer->SetName(Name("IndirectionBuffer"));
-            RHI::BufferInitRequest bufferRequest;
-            bufferRequest.m_descriptor.m_bindFlags = RHI::BufferBindFlags::ShaderRead;
-            bufferRequest.m_descriptor.m_byteCount = sizeof(uint32_t) * InternalBP::ImageCount;
-            bufferRequest.m_buffer = m_indirectionBuffer.get();
-            [[maybe_unused]] RHI::ResultCode resultCode = m_bufferPool->InitBuffer(bufferRequest);
-            AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to create Indirection Buffer");
-
-            RHI::BufferViewDescriptor viewDesc =
-                RHI::BufferViewDescriptor::CreateRaw(0, aznumeric_cast<uint32_t>(bufferRequest.m_descriptor.m_byteCount));
-            m_indirectionBufferView = m_indirectionBuffer->GetBufferView(viewDesc);
+            AZ::Data::Instance<AZ::RPI::StreamingImage> image = LoadStreamingImage(InternalBP::CubeMapImages[textureIdx], InternalBP::SampleName);
+            m_cubemapImages.push_back(image);
         }
         
+        // Create the indirect buffer to hold indices for bindless images-> ImageCount 2d texture + CubeMapImageCount cubemap textures + m_computeImage (read write texture that was written into by the compute pass)
+        CreateIndirectBuffer(Name("ImageIndirectionBuffer"), m_imageIndirectionBuffer, m_imageIndirectionBufferView, sizeof(uint32_t) * (InternalBP::ImageCount + InternalBP::CubeMapImageCount + 1));
+        // Create the indirect buffer to hold indices for bindless buffers -> 2 read only buffers that contain color values + m_computeBuffer which is the read write buffer that was written into by the compute pass
+        CreateIndirectBuffer(Name("BufferIndirectionBuffer"), m_bufferIndirectionBuffer, m_bufferIndirectionBufferView, sizeof(uint32_t) * sizeof(uint32_t) * 3);
+
+        //Read only buffer with green-ish color
+        CreateColorBuffer(Name("ColorBuffer1"), AZ::Vector4(0.1, 0.4, 0.1, 1.0), m_colorBuffer1, m_colorBuffer1View);
+        // Read only buffer with blue-ish color
+        CreateColorBuffer(Name("ColorBuffer2"), AZ::Vector4(0.1, 0.1, 0.4, 1.0), m_colorBuffer2, m_colorBuffer2View);
+
+        // Set the color multiplier buffer
+        {
+            m_computeBuffer = aznew RHI::Buffer();
+            m_computeBuffer->SetName(Name("m_colorBufferMultiplier"));
+            uint32_t bufferSize = sizeof(uint32_t);//RHI ::GetFormatSize(RHI::Format::R32G32B32A32_FLOAT);
+
+            RHI::BufferInitRequest request;
+            request.m_buffer = m_computeBuffer.get();
+            request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::ShaderReadWrite, bufferSize };
+            [[maybe_unused]] RHI::ResultCode result = m_computeBufferPool->InitBuffer(request);
+            AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialized compute buffer");
+
+            m_rwBufferViewDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, bufferSize);
+            m_computeBufferView = m_computeBuffer->BuildBufferView(m_rwBufferViewDescriptor);
+        }
+
+        // Set the image version of color multiplier buffer
+        {
+            m_computeImage = aznew RHI::Image();
+
+            RHI::ImageInitRequest request;
+            request.m_image = m_computeImage.get();
+            request.m_descriptor =
+                RHI::ImageDescriptor::Create2D(RHI::ImageBindFlags::ShaderReadWrite, 1, 1, RHI::Format::R32G32B32A32_FLOAT);
+            [[maybe_unused]] RHI::ResultCode result = m_rwImagePool->InitImage(request);
+            AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize output image");
+
+            m_rwImageViewDescriptor = RHI::ImageViewDescriptor::Create(RHI::Format::R32G32B32A32_FLOAT, 0, 0);
+            m_computeImageView = m_computeImage->BuildImageView(m_rwImageViewDescriptor);
+        }
+
 #if ATOMSAMPLEVIEWER_TRAIT_BINDLESS_PROTOTYPE_SUPPORTS_DIRECT_BOUND_UNBOUNDED_ARRAY
        // Set the images
        {
@@ -507,123 +633,10 @@ namespace AtomSampleViewer
         // Create the objects
         CreateObjects();
 
-        // Creates a scope for rendering the model.
-        {
-            struct ScopeData
-            {
-                //UserDataParam - Empty for this samples
-            };
-
-            const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
-            {
-                // Binds the swap chain as a color attachment. Clears it to white.
-                {
-                    RHI::ImageScopeAttachmentDescriptor descriptor;
-                    descriptor.m_attachmentId = m_outputAttachmentId;
-                    descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
-                    frameGraph.UseColorAttachment(descriptor);
-                }
-
-                // Create & Binds DepthStencil image
-                {
-                    // Get the depth stencil
-                    m_depthStencilID = AZ::RHI::AttachmentId{ "DepthStencilID" };
-
-                    const RHI::ImageDescriptor imageDescriptor = RHI::ImageDescriptor::Create2D(
-                        RHI::ImageBindFlags::DepthStencil, m_outputWidth, m_outputHeight, AZ::RHI::Format::D32_FLOAT);
-                    const RHI::TransientImageDescriptor transientImageDescriptor(m_depthStencilID, imageDescriptor);
-                    frameGraph.GetAttachmentDatabase().CreateTransientImage(transientImageDescriptor);
-
-                    RHI::ImageScopeAttachmentDescriptor depthStencilDescriptor;
-                    depthStencilDescriptor.m_attachmentId = m_depthStencilID;
-                    depthStencilDescriptor.m_imageViewDescriptor.m_overrideFormat = AZ::RHI::Format::D32_FLOAT;
-                    depthStencilDescriptor.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateDepth(0.0f);
-                    depthStencilDescriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Clear;
-                    depthStencilDescriptor.m_loadStoreAction.m_loadActionStencil = RHI::AttachmentLoadAction::Clear;
-                    frameGraph.UseDepthStencilAttachment(depthStencilDescriptor, RHI::ScopeAttachmentAccess::ReadWrite);
-                }
-
-                // Submit the sub mesh count
-                frameGraph.SetEstimatedItemCount(static_cast<uint32_t>(m_subMeshInstanceArray.size()));
-            };
-
-            const auto compileFunction = [this]([[maybe_unused]] const AZ::RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
-            {
-                // Set the handles for the individual SRGs per sub mesh 
-                for (const ObjectInterval& objectInterval : m_objectIntervalArray)
-                {
-                    for (uint32_t subMeshIdx = objectInterval.m_min; subMeshIdx < objectInterval.m_max; subMeshIdx++)
-                    {
-                        // Update the constant data
-                        SubMeshInstance& subMesh = m_subMeshInstanceArray[subMeshIdx];
-                        // Set the view handle
-                        bool set = subMesh.m_perSubMeshSrg->SetConstant(subMesh.m_viewHandleIndex, m_worldToClipHandle);
-                        AZ_Assert(set, "Failed to set the view constant");
-                        // Set the light handle
-                        set = subMesh.m_perSubMeshSrg->SetConstant(subMesh.m_lightHandleIndex, m_lightDirectionHandle);
-                        AZ_Assert(set, "Failed to set the view constant");
-                        // Set the object handle
-                        set = subMesh.m_perSubMeshSrg->SetConstant(subMesh.m_objecHandleIndex, objectInterval.m_objectHandle);
-                        AZ_Assert(set, "Failed to set the object constant");
-                        // Set the material handle
-                        const uint32_t materialHandleIndex = subMeshIdx % m_materialCount;
-                        const FloatBufferHandle materialHandle = m_materialHandleArray[materialHandleIndex];
-                        set = subMesh.m_perSubMeshSrg->SetConstant(subMesh.m_materialHandleIndex, materialHandle);
-                        AZ_Assert(set, "Failed to set the material constant");
-
-                        subMesh.m_perSubMeshSrg->Compile();
-                    }
-                }
-            };
-
-            const auto executeFunction = [this]([[maybe_unused]] const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
-            {
-                RHI::CommandList* commandList = context.GetCommandList();
-
-                // Set persistent viewport and scissor state.
-                commandList->SetViewports(&m_viewport, 1u);
-                commandList->SetScissors(&m_scissor, 1u);
-
-                // Submit the drawcommands to the CommandList.
-                for (uint32_t instanceIdx = context.GetSubmitRange().m_startIndex; instanceIdx < context.GetSubmitRange().m_endIndex; instanceIdx++)
-                {
-                    const SubMeshInstance& subMesh = m_subMeshInstanceArray[instanceIdx];
-
-                    const RHI::ShaderResourceGroup* shaderResourceGroups[] = {
-                        m_bindlessSrg->GetSrg(m_samplerSrgName)->GetRHIShaderResourceGroup(),
-                        subMesh.m_perSubMeshSrg->GetRHIShaderResourceGroup(),
-                        m_bindlessSrg->GetSrg(m_floatBufferSrgName)->GetRHIShaderResourceGroup(),
-#if ATOMSAMPLEVIEWER_TRAIT_BINDLESS_PROTOTYPE_SUPPORTS_DIRECT_BOUND_UNBOUNDED_ARRAY
-                        m_bindlessSrg->GetSrg(m_imageSrgName)->GetRHIShaderResourceGroup(),
-#endif
-                        m_bindlessSrg->GetSrg(m_indirectionBufferSrgName)->GetRHIShaderResourceGroup(),
-                    };
-                    RHI::DrawItem drawItem;
-                    drawItem.m_arguments = subMesh.m_mesh->m_drawArguments;
-                    drawItem.m_pipelineState = m_pipelineState.get();
-                    drawItem.m_indexBufferView = &subMesh.m_mesh->m_indexBufferView;
-                    drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
-                    drawItem.m_shaderResourceGroups = shaderResourceGroups;
-                    drawItem.m_streamBufferViewCount = static_cast<uint8_t>(subMesh.bufferStreamViewArray.size());
-                    drawItem.m_streamBufferViews = subMesh.bufferStreamViewArray.data();
-
-                    // Submit the triangle draw item.
-                    commandList->Submit(drawItem, instanceIdx);
-                }
-            };
-
-            m_scopeProducers.emplace_back(
-                aznew RHI::ScopeProducerFunction<
-                ScopeData,
-                decltype(prepareFunction),
-                decltype(compileFunction),
-                decltype(executeFunction)>(
-                    m_scopeId,
-                    ScopeData{},
-                    prepareFunction,
-                    compileFunction,
-                    executeFunction));
-        }
+        // Create all the scopes for this sample
+        CreateBufferComputeScope();
+        CreateImageComputeScope();
+        CreateBindlessScope();
 
         // Connect the busses
         AZ::RHI::RHISystemNotificationBus::Handler::BusConnect();
@@ -633,13 +646,31 @@ namespace AtomSampleViewer
     void BindlessPrototypeExampleComponent::Deactivate()
     {
         m_imguiSidebar.Deactivate();
-
         m_model = nullptr;
-
         m_shader = nullptr;
         m_pipelineState = nullptr;
-        m_bindlessSrg = nullptr;
+
         m_floatBuffer = nullptr;
+        m_computeBuffer = nullptr;
+        m_computeImage = nullptr;
+        m_colorBuffer1 = nullptr;
+        m_colorBuffer2 = nullptr;
+        m_imageIndirectionBuffer = nullptr;
+        m_bufferIndirectionBuffer = nullptr;
+
+        m_colorBuffer1View = nullptr;
+        m_colorBuffer2View = nullptr;
+        m_computeBufferView = nullptr;
+        m_computeImageView = nullptr;
+        m_imageIndirectionBufferView = nullptr;
+        m_bufferIndirectionBufferView = nullptr;
+
+        m_bufferPool = nullptr;
+        m_computeBufferPool = nullptr;
+
+        m_bufferDispatchSRG = nullptr;
+        m_imageDispatchSRG = nullptr;
+        m_bindlessSrg = nullptr;
     }
 
     void BindlessPrototypeExampleComponent::OnTick(float deltaTime, [[maybe_unused]] ScriptTimePoint scriptTime)
@@ -707,25 +738,83 @@ namespace AtomSampleViewer
 
         Data::Instance<AZ::RPI::ShaderResourceGroup> indirectionBufferSrg = m_bindlessSrg->GetSrg(m_indirectionBufferSrgName);
 
-        RHI::BufferMapRequest mapRequest{ *m_indirectionBuffer, 0, sizeof(uint32_t) * InternalBP::ImageCount };
-        RHI::BufferMapResponse mapResponse;
-        m_bufferPool->MapBuffer(mapRequest, mapResponse);
+        // Indirect buffer that will contain indices for all read only textures and read write textures within the bindless heap
+        {
+            //Read only textures = InternalBP::Images , InternalBP::CubeMapImages, m_computeImageView
+            RHI::BufferMapRequest mapRequest{ *m_imageIndirectionBuffer, 0,
+                                              sizeof(uint32_t) * (InternalBP::ImageCount + InternalBP::CubeMapImageCount + 1) };
+            RHI::BufferMapResponse mapResponse;
+            m_bufferPool->MapBuffer(mapRequest, mapResponse);
+
+            AZStd::vector<const RHI::ImageView*> views;
+            AZStd::vector<bool> isViewReadOnly;
 
-        AZStd::vector<const RHI::ImageView*> views;
-        for(AZ::Data::Instance<AZ::RPI::StreamingImage> image : m_images)
+            //Add read only 2d texture views
+            for (AZ::Data::Instance<AZ::RPI::StreamingImage> image : m_images)
+            {
+                views.push_back(image->GetImageView());
+                isViewReadOnly.push_back(true);
+            }
+
+            //Add read only cube map texture views
+            for (AZ::Data::Instance<AZ::RPI::StreamingImage> image : m_cubemapImages)
+            {
+                views.push_back(image->GetImageView());
+                isViewReadOnly.push_back(true);
+            }
+
+            //Ad read write texture view
+            views.push_back(m_computeImageView.get());
+            isViewReadOnly.push_back(false);
+
+            AZStd::unordered_map<int, uint32_t*> mappedData;
+            for (auto [deviceIndex, data] : mapResponse.m_data)
+            {
+                mappedData[deviceIndex] = static_cast<uint32_t*>(data);
+            }
+
+            // Populate the indirect buffer with indices of the views that reside within the bindless heap
+            uint32_t arrayIndex = 0;
+            auto indirectionBufferIndex = indirectionBufferSrg->FindShaderInputBufferIndex(AZ::Name{ "m_imageIndirectionBuffer" });
+            indirectionBufferSrg->SetBindlessViews(
+                indirectionBufferIndex, m_imageIndirectionBufferView.get(), views, mappedData, isViewReadOnly, arrayIndex);
+
+            m_bufferPool->UnmapBuffer(*m_imageIndirectionBuffer);
+        }
+
+        // Indirect buffer that will contain indices for all read only buffers and read write buffers within the bindless heap
         {
-            views.push_back(image->GetImageView());
+            RHI::BufferMapRequest mapRequest{ *m_bufferIndirectionBuffer, 0, sizeof(uint32_t) * 3 };
+            RHI::BufferMapResponse mapResponse;
+            m_bufferPool->MapBuffer(mapRequest, mapResponse);
+
+            AZStd::vector<const RHI::BufferView*> views;
+            AZStd::vector<bool> isViewReadOnly;
+           
+            // Add read only buffer views
+            views.push_back(m_colorBuffer1View.get());
+            isViewReadOnly.push_back(true);
+            views.push_back(m_colorBuffer2View.get());
+            isViewReadOnly.push_back(true);
+
+            //Add read write buffer view
+            views.push_back(m_computeBufferView.get());
+            isViewReadOnly.push_back(false);
+
+            AZStd::unordered_map<int, uint32_t*> mappedData;
+            for (auto [deviceIndex, data] : mapResponse.m_data)
+            {
+                mappedData[deviceIndex] = static_cast<uint32_t*>(data);
+            }
+
+            //Populate the indirect buffer with indices of the views that reside within the bindless heap
+            uint32_t arrayIndex = 0;
+            auto indirectionBufferIndex = indirectionBufferSrg->FindShaderInputBufferIndex(AZ::Name{ "m_bufferIndirectionBuffer" });
+            indirectionBufferSrg->SetBindlessViews(
+                indirectionBufferIndex, m_bufferIndirectionBufferView.get(), views, mappedData, isViewReadOnly, arrayIndex);
+
+            m_bufferPool->UnmapBuffer(*m_bufferIndirectionBuffer);
         }
-        
-        bool readOnlyTexture = true;
-        uint32_t arrayIndex = 0;
-        auto indirectionBufferIndex = indirectionBufferSrg->FindShaderInputBufferIndex(AZ::Name{ "m_indirectionBuffer" });
-        indirectionBufferSrg->SetBindlessViews(
-            indirectionBufferIndex, m_indirectionBufferView.get(),
-            views, static_cast<uint32_t*>(mapResponse.m_data),
-            readOnlyTexture, arrayIndex);
-
-        m_bufferPool->UnmapBuffer(*m_indirectionBuffer);
         indirectionBufferSrg->Compile();
 
         BasicRHIComponent::OnFramePrepare(frameGraphBuilder);
@@ -835,13 +924,388 @@ namespace AtomSampleViewer
         [[maybe_unused]] RHI::ResultCode result = m_bufferPool->MapBuffer(mapRequest, response);
         // ResultCode::Unimplemented is used by Null Renderer and hence is a valid use case
         AZ_Assert(result == RHI::ResultCode::Success || result == RHI::ResultCode::Unimplemented, "Failed to map object buffer]");
-        if (response.m_data)
+        if (!response.m_data.empty())
         {
-            memcpy(response.m_data, data, mapRequest.m_byteCount);
+            for(auto& [_, responseData] : response.m_data)
+            {
+                memcpy(responseData, data, mapRequest.m_byteCount);
+            }
             m_bufferPool->UnmapBuffer(*m_buffer);
             return true;
         }
 
         return false;
     }
+
+    void BindlessPrototypeExampleComponent::LoadComputeShaders()
+    {
+        using namespace AZ;
+        //Load the compute shader related to the compute pass that will write a value to a read write buffer
+        {
+            const char* shaderFilePath = "Shaders/RHI/BindlessBufferComputeDispatch.azshader";
+
+            const auto shader = LoadShader(shaderFilePath, InternalBP::SampleName);
+            if (shader == nullptr)
+            {
+                return;
+            }
+
+            RHI::PipelineStateDescriptorForDispatch pipelineDesc;
+            shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId).ConfigurePipelineState(pipelineDesc);
+
+            const auto& numThreads = shader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, Name("numthreads"));
+            if (numThreads)
+            {
+                const RHI::ShaderStageAttributeArguments& args = *numThreads;
+                m_bufferNumThreadsX = args[0].type() == azrtti_typeid<int>() ? AZStd::any_cast<int>(args[0]) : m_bufferNumThreadsX;
+                m_bufferNumThreadsY = args[1].type() == azrtti_typeid<int>() ? AZStd::any_cast<int>(args[1]) : m_bufferNumThreadsY;
+                m_bufferNumThreadsZ = args[2].type() == azrtti_typeid<int>() ? AZStd::any_cast<int>(args[2]) : m_bufferNumThreadsZ;
+            }
+            else
+            {
+                AZ_Error(InternalBP::SampleName, false, "Did not find expected numthreads attribute");
+            }
+
+            m_bufferDispatchPipelineState = shader->AcquirePipelineState(pipelineDesc);
+            if (!m_bufferDispatchPipelineState)
+            {
+                AZ_Error(InternalBP::SampleName, false, "Failed to acquire default pipeline state for shader '%s'", shaderFilePath);
+                return;
+            }
+            m_bufferDispatchSRG = CreateShaderResourceGroup(shader, "BufferSrg", InternalBP::SampleName);
+        }
+
+        // Load the compute shader related to the compute pass that will write a value to a read write texture
+        {
+            const char* shaderFilePath = "Shaders/RHI/BindlessImageComputeDispatch.azshader";
+
+            const auto shader = LoadShader(shaderFilePath, InternalBP::SampleName);
+            if (shader == nullptr)
+            {
+                return;
+            }
+
+            RHI::PipelineStateDescriptorForDispatch pipelineDesc;
+            shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId).ConfigurePipelineState(pipelineDesc);
+
+            const auto& numThreads = shader->GetAsset()->GetAttribute(RHI::ShaderStage::Compute, Name("numthreads"));
+            if (numThreads)
+            {
+                const RHI::ShaderStageAttributeArguments& args = *numThreads;
+                m_imageNumThreadsX = args[0].type() == azrtti_typeid<int>() ? AZStd::any_cast<int>(args[0]) : m_imageNumThreadsX;
+                m_imageNumThreadsY = args[1].type() == azrtti_typeid<int>() ? AZStd::any_cast<int>(args[1]) : m_imageNumThreadsY;
+                m_imageNumThreadsZ = args[2].type() == azrtti_typeid<int>() ? AZStd::any_cast<int>(args[2]) : m_imageNumThreadsZ;
+            }
+            else
+            {
+                AZ_Error(InternalBP::SampleName, false, "Did not find expected numthreads attribute");
+            }
+
+            m_imageDispatchPipelineState = shader->AcquirePipelineState(pipelineDesc);
+            if (!m_imageDispatchPipelineState)
+            {
+                AZ_Error(InternalBP::SampleName, false, "Failed to acquire default pipeline state for shader '%s'", shaderFilePath);
+                return;
+            }
+            m_imageDispatchSRG = CreateShaderResourceGroup(shader, "ImageSrg", InternalBP::SampleName);
+        }
+    }
+
+    void BindlessPrototypeExampleComponent::CreateBufferComputeScope()
+    {
+        using namespace AZ;
+
+        struct ScopeData
+        {
+            // UserDataParam - Empty for this samples
+        };
+
+        const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
+        {
+            // attach compute buffer
+            {
+                [[maybe_unused]] RHI::ResultCode result =
+                    frameGraph.GetAttachmentDatabase().ImportBuffer(m_bufferAttachmentId, m_computeBuffer);
+                AZ_Error(
+                    InternalBP::SampleName, result == RHI::ResultCode::Success, "Failed to import compute buffer with error %d", result);
+
+                RHI::BufferScopeAttachmentDescriptor desc;
+                desc.m_attachmentId = m_bufferAttachmentId;
+                desc.m_bufferViewDescriptor = m_rwBufferViewDescriptor;
+                desc.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f);
+
+                frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::ComputeShader);
+
+                const Name computeBufferId{ "m_colorBufferMultiplier" };
+                RHI::ShaderInputBufferIndex computeBufferIndex = m_bufferDispatchSRG->FindShaderInputBufferIndex(computeBufferId);
+                AZ_Error(
+                    InternalBP::SampleName, computeBufferIndex.IsValid(), "Failed to find shader input buffer %s.",
+                    computeBufferId.GetCStr());
+                m_bufferDispatchSRG->SetBufferView(computeBufferIndex, m_computeBufferView.get());
+                m_bufferDispatchSRG->Compile();
+            }
+
+            frameGraph.SetEstimatedItemCount(1);
+        };
+
+        RHI::EmptyCompileFunction<ScopeData> compileFunction;
+
+        const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
+        {
+            RHI::CommandList* commandList = context.GetCommandList();
+
+            // Set persistent viewport and scissor state.
+            commandList->SetViewports(&m_viewport, 1);
+            commandList->SetScissors(&m_scissor, 1);
+
+            AZStd::array<const RHI::DeviceShaderResourceGroup*, 8> shaderResourceGroups;
+            shaderResourceGroups[0] = m_bufferDispatchSRG->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
+
+            RHI::DeviceDispatchItem dispatchItem;
+            RHI::DispatchDirect dispatchArgs;
+
+            dispatchArgs.m_threadsPerGroupX = aznumeric_cast<uint16_t>(m_bufferNumThreadsX);
+            dispatchArgs.m_threadsPerGroupY = aznumeric_cast<uint16_t>(m_bufferNumThreadsY);
+            dispatchArgs.m_threadsPerGroupZ = aznumeric_cast<uint16_t>(m_bufferNumThreadsZ);
+            dispatchArgs.m_totalNumberOfThreadsX = 1;
+            dispatchArgs.m_totalNumberOfThreadsY = 1;
+            dispatchArgs.m_totalNumberOfThreadsZ = 1;
+
+            dispatchItem.m_arguments = dispatchArgs;
+            dispatchItem.m_pipelineState = m_bufferDispatchPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+            dispatchItem.m_shaderResourceGroupCount = 1;
+            dispatchItem.m_shaderResourceGroups = shaderResourceGroups;
+
+            commandList->Submit(dispatchItem);
+        };
+
+        m_scopeProducers.emplace_back(
+            aznew RHI::ScopeProducerFunction<ScopeData, decltype(prepareFunction), decltype(compileFunction), decltype(executeFunction)>(
+                RHI::ScopeId{ "ComputeBuffer" }, ScopeData{}, prepareFunction, compileFunction, executeFunction));
+    }
+
+    void BindlessPrototypeExampleComponent::CreateImageComputeScope()
+    {
+        using namespace AZ;
+
+        struct ScopeData
+        {
+            // UserDataParam - Empty for this samples
+        };
+
+        const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
+        {
+            // attach compute buffer
+            {
+                [[maybe_unused]] RHI::ResultCode result =
+                    frameGraph.GetAttachmentDatabase().ImportImage(m_imageAttachmentId, m_computeImage);
+                AZ_Error(
+                    InternalBP::SampleName, result == RHI::ResultCode::Success, "Failed to import compute buffer with error %d", result);
+
+                RHI::ImageScopeAttachmentDescriptor desc;
+                desc.m_attachmentId = m_imageAttachmentId;
+                desc.m_imageViewDescriptor = m_rwImageViewDescriptor;
+                desc.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f);
+
+                frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::ComputeShader);
+
+                const Name computeBufferId{ "m_colorImageMultiplier" };
+                RHI::ShaderInputImageIndex computeBufferIndex = m_imageDispatchSRG->FindShaderInputImageIndex(computeBufferId);
+                AZ_Error(
+                    InternalBP::SampleName, computeBufferIndex.IsValid(), "Failed to find shader input buffer %s.",
+                    computeBufferId.GetCStr());
+                m_imageDispatchSRG->SetImageView(computeBufferIndex, m_computeImageView.get());
+                m_imageDispatchSRG->Compile();
+            }
+
+            frameGraph.SetEstimatedItemCount(1);
+        };
+
+        RHI::EmptyCompileFunction<ScopeData> compileFunction;
+
+        const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
+        {
+            RHI::CommandList* commandList = context.GetCommandList();
+
+            // Set persistent viewport and scissor state.
+            commandList->SetViewports(&m_viewport, 1);
+            commandList->SetScissors(&m_scissor, 1);
+
+            AZStd::array<const RHI::DeviceShaderResourceGroup*, 8> shaderResourceGroups;
+            shaderResourceGroups[0] = m_imageDispatchSRG->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
+
+            RHI::DeviceDispatchItem dispatchItem;
+            RHI::DispatchDirect dispatchArgs;
+
+            dispatchArgs.m_threadsPerGroupX = aznumeric_cast<uint16_t>(m_imageNumThreadsX);
+            dispatchArgs.m_threadsPerGroupY = aznumeric_cast<uint16_t>(m_imageNumThreadsY);
+            dispatchArgs.m_threadsPerGroupZ = aznumeric_cast<uint16_t>(m_imageNumThreadsZ);
+            dispatchArgs.m_totalNumberOfThreadsX = 1;
+            dispatchArgs.m_totalNumberOfThreadsY = 1;
+            dispatchArgs.m_totalNumberOfThreadsZ = 1;
+
+            dispatchItem.m_arguments = dispatchArgs;
+            dispatchItem.m_pipelineState = m_imageDispatchPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+            dispatchItem.m_shaderResourceGroupCount = 1;
+            dispatchItem.m_shaderResourceGroups = shaderResourceGroups;
+
+            commandList->Submit(dispatchItem);
+        };
+
+        m_scopeProducers.emplace_back(
+            aznew RHI::ScopeProducerFunction<ScopeData, decltype(prepareFunction), decltype(compileFunction), decltype(executeFunction)>(
+                RHI::ScopeId{ "ComputeImage" }, ScopeData{}, prepareFunction, compileFunction, executeFunction));
+    }
+
+    void BindlessPrototypeExampleComponent::CreateBindlessScope()
+    {
+        // Creates a scope for rendering the model.
+        {
+            struct ScopeData
+            {
+                // UserDataParam - Empty for this samples
+            };
+
+            const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
+            {
+                // Binds the swap chain as a color attachment. Clears it to white.
+                {
+                    RHI::ImageScopeAttachmentDescriptor descriptor;
+                    descriptor.m_attachmentId = m_outputAttachmentId;
+                    descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
+                    frameGraph.UseColorAttachment(descriptor);
+                }
+
+                // Create & Binds DepthStencil image
+                {
+                    // Get the depth stencil
+                    m_depthStencilID = AZ::RHI::AttachmentId{ "DepthStencilID" };
+
+                    const RHI::ImageDescriptor imageDescriptor = RHI::ImageDescriptor::Create2D(
+                        RHI::ImageBindFlags::DepthStencil, m_outputWidth, m_outputHeight, AZ::RHI::Format::D32_FLOAT);
+                    const RHI::TransientImageDescriptor transientImageDescriptor(m_depthStencilID, imageDescriptor);
+                    frameGraph.GetAttachmentDatabase().CreateTransientImage(transientImageDescriptor);
+
+                    RHI::ImageScopeAttachmentDescriptor depthStencilDescriptor;
+                    depthStencilDescriptor.m_attachmentId = m_depthStencilID;
+                    depthStencilDescriptor.m_imageViewDescriptor.m_overrideFormat = AZ::RHI::Format::D32_FLOAT;
+                    depthStencilDescriptor.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateDepth(0.0f);
+                    depthStencilDescriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Clear;
+                    depthStencilDescriptor.m_loadStoreAction.m_loadActionStencil = RHI::AttachmentLoadAction::Clear;
+                    frameGraph.UseDepthStencilAttachment(
+                        depthStencilDescriptor, RHI::ScopeAttachmentAccess::ReadWrite,
+                        RHI::ScopeAttachmentStage::EarlyFragmentTest | RHI::ScopeAttachmentStage::LateFragmentTest);
+                }
+
+                {
+                    RHI::BufferScopeAttachmentDescriptor desc;
+                    desc.m_attachmentId = m_bufferAttachmentId;
+                    desc.m_bufferViewDescriptor = m_rwBufferViewDescriptor;
+                    desc.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f);
+
+                    frameGraph.UseShaderAttachment(
+                        desc, RHI::ScopeAttachmentAccess::ReadWrite,
+                        RHI::ScopeAttachmentStage::VertexShader | RHI::ScopeAttachmentStage::FragmentShader);
+                }
+
+                {
+                    RHI::ImageScopeAttachmentDescriptor desc;
+                    desc.m_attachmentId = m_imageAttachmentId;
+                    desc.m_imageViewDescriptor = m_rwImageViewDescriptor;
+                    desc.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f);
+
+                    frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::FragmentShader);
+                }
+                // Submit the sub mesh count
+                frameGraph.SetEstimatedItemCount(static_cast<uint32_t>(m_subMeshInstanceArray.size()));
+            };
+
+            const auto compileFunction =
+                [this]([[maybe_unused]] const AZ::RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
+            {
+                // Set the handles for the individual SRGs per sub mesh
+                for (const ObjectInterval& objectInterval : m_objectIntervalArray)
+                {
+                    for (uint32_t subMeshIdx = objectInterval.m_min; subMeshIdx < objectInterval.m_max; subMeshIdx++)
+                    {
+                        // Update the constant data
+                        SubMeshInstance& subMesh = m_subMeshInstanceArray[subMeshIdx];
+                        // Set the view handle
+                        [[maybe_unused]] bool set = subMesh.m_perSubMeshSrg->SetConstant(subMesh.m_viewHandleIndex, m_worldToClipHandle);
+                        AZ_Assert(set, "Failed to set the view constant");
+                        // Set the light handle
+                        set = subMesh.m_perSubMeshSrg->SetConstant(subMesh.m_lightHandleIndex, m_lightDirectionHandle);
+                        AZ_Assert(set, "Failed to set the view constant");
+                        // Set the object handle
+                        set = subMesh.m_perSubMeshSrg->SetConstant(subMesh.m_objecHandleIndex, objectInterval.m_objectHandle);
+                        AZ_Assert(set, "Failed to set the object constant");
+                        // Set the material handle
+                        const uint32_t materialHandleIndex = subMeshIdx % m_materialCount;
+                        const FloatBufferHandle materialHandle = m_materialHandleArray[materialHandleIndex];
+                        set = subMesh.m_perSubMeshSrg->SetConstant(subMesh.m_materialHandleIndex, materialHandle);
+                        AZ_Assert(set, "Failed to set the material constant");
+
+                        set = subMesh.m_perSubMeshSrg->SetConstant(subMesh.m_uvBufferHandleIndex, subMesh.m_uvBufferIndex);
+                        AZ_Assert(set, "Failed to set the UV buffer index");
+                        set = subMesh.m_perSubMeshSrg->SetConstant(subMesh.m_uvBufferByteOffsetHandleIndex, subMesh.m_uvBufferByteOffset);
+                        AZ_Assert(set, "Failed to set the UV buffer byte index");
+
+                        subMesh.m_perSubMeshSrg->Compile();
+                    }
+                }
+            };
+
+            const auto executeFunction =
+                [this]([[maybe_unused]] const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
+            {
+                RHI::CommandList* commandList = context.GetCommandList();
+
+                // Set persistent viewport and scissor state.
+                commandList->SetViewports(&m_viewport, 1u);
+                commandList->SetScissors(&m_scissor, 1u);
+
+                // Submit the drawcommands to the CommandList.
+                for (uint32_t instanceIdx = context.GetSubmitRange().m_startIndex; instanceIdx < context.GetSubmitRange().m_endIndex;
+                     instanceIdx++)
+                {
+                    const SubMeshInstance& subMesh = m_subMeshInstanceArray[instanceIdx];
+
+                    const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                        m_bindlessSrg->GetSrg(m_samplerSrgName)
+                            ->GetRHIShaderResourceGroup()
+                            ->GetDeviceShaderResourceGroup(context.GetDeviceIndex())
+                            .get(),
+                        subMesh.m_perSubMeshSrg->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get(),
+                        m_bindlessSrg->GetSrg(m_floatBufferSrgName)
+                            ->GetRHIShaderResourceGroup()
+                            ->GetDeviceShaderResourceGroup(context.GetDeviceIndex())
+                            .get(),
+#if ATOMSAMPLEVIEWER_TRAIT_BINDLESS_PROTOTYPE_SUPPORTS_DIRECT_BOUND_UNBOUNDED_ARRAY
+                        m_bindlessSrg->GetSrg(m_imageSrgName)
+                            ->GetRHIShaderResourceGroup()
+                            ->GetDeviceShaderResourceGroup(context.GetDeviceIndex())
+                            .get(),
+#endif
+                        m_bindlessSrg->GetSrg(m_indirectionBufferSrgName)
+                            ->GetRHIShaderResourceGroup()
+                            ->GetDeviceShaderResourceGroup(context.GetDeviceIndex())
+                            .get(),
+                    };
+                    RHI::DeviceDrawItem drawItem;
+                    drawItem.m_geometryView = subMesh.m_mesh->GetDeviceGeometryView(context.GetDeviceIndex());
+                    drawItem.m_streamIndices = subMesh.m_streamIndices;
+                    drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                    drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
+                    drawItem.m_shaderResourceGroups = shaderResourceGroups;
+                    // Submit the triangle draw item.
+                    commandList->Submit(drawItem, instanceIdx);
+                }
+            };
+
+            m_scopeProducers.emplace_back(
+                aznew
+                    RHI::ScopeProducerFunction<ScopeData, decltype(prepareFunction), decltype(compileFunction), decltype(executeFunction)>(
+                        m_scopeId, ScopeData{}, prepareFunction, compileFunction, executeFunction));
+        }
+    }
 }; // namespace AtomSampleViewer

+ 86 - 11
Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.h

@@ -35,14 +35,15 @@ namespace AZ
 namespace AtomSampleViewer
 {
     
-    //! This sample tests a naive approach of achieving bindless resources.
+    //! This sample tests various resource types: read only textures, read only buffers, read only cube 
+    //! map textures, read write textures and read write buffers.
     //! This sample utilizes a FloatBuffer, which is a StructuredBuffer with the element type of Float, which
     //! is 4-byte aligned.
     //! All data that is used in this sample is allocated in this buffer, like: materials, objects, 
     //! transforms, etc. This allows for various types of data to be stored within the same buffer. The data
     //! is accessible with handles, which acts as an offset within the FloatBuffer to access data.
     //!
-    //! The texture views are stored in a descriptor which holds an image view array. 
+    //! All the bindless heap indices for views to various resource types are passed in via an indirect buffer.
     //!
     class BindlessPrototypeExampleComponent final
         : public BasicRHIComponent
@@ -125,13 +126,18 @@ namespace AtomSampleViewer
             AZ::RHI::ShaderInputNameIndex m_objecHandleIndex = "m_perObjectHandle";
             AZ::RHI::ShaderInputNameIndex m_materialHandleIndex = "m_materialHandle";
             AZ::RHI::ShaderInputNameIndex m_lightHandleIndex = "m_lightHandle";
+            AZ::RHI::ShaderInputNameIndex m_uvBufferHandleIndex = "m_uvBufferIndex";
+            AZ::RHI::ShaderInputNameIndex m_uvBufferByteOffsetHandleIndex = "m_uvBufferByteOffset";
 
             AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> m_perSubMeshSrg;
 
-            const AZ::RPI::ModelLod::Mesh* m_mesh;
-            AZ::RPI::ModelLod::StreamBufferViewList bufferStreamViewArray;
+            AZ::RPI::ModelLod::Mesh* m_mesh;
+            AZ::RHI::StreamBufferIndices m_streamIndices;
 
             AZ::Matrix4x4 m_modelMatrix;
+
+            uint32_t m_uvBufferIndex = 0;
+            uint32_t m_uvBufferByteOffset = 0;
         };
 
     public:
@@ -170,6 +176,20 @@ namespace AtomSampleViewer
         // Creates the materials
         void CreateMaterials();
 
+        // Create read only buffers that has color values
+        void CreateColorBuffer(
+            const AZ::Name& bufferName,
+            const AZ::Vector4& colorVal,
+            AZ::RHI::Ptr<AZ::RHI::Buffer>& buffer,
+            AZ::RHI::Ptr<AZ::RHI::BufferView>& bufferView);
+
+        // Create indirect buffer that will contain index of the view into the bindless heap
+        void CreateIndirectBuffer(
+            const AZ::Name& bufferName,
+            AZ::RHI::Ptr<AZ::RHI::Buffer>& indirectionBuffer,
+            AZ::RHI::Ptr<AZ::RHI::BufferView>& bufferView,
+            size_t byteSize);
+
         // Helper function to allocate or update data in the FloatBuffer
         template<typename T>
         void AllocateMaterial(FloatBufferHandle& handle)
@@ -179,12 +199,23 @@ namespace AtomSampleViewer
             AZ_Assert(result, "Failed to allocate FloatBuffer");
         }
 
-        // Create the BufferPol
-        void CreateBufferPool();
+        // Create all the needed pools
+        void CreatePools();
 
-        //Recreate objects
+        // Recreate objects
         void Recreate();
         
+        // Load compute shaders related to writing to rwtexture and rwbuffer
+        void LoadComputeShader(const char* shaderFilePath);
+        void LoadComputeShaders();
+
+        // Create compute passes for writing to rwbuffer and rwtexture
+        void CreateBufferComputeScope();
+        void CreateImageComputeScope();
+
+        // Create the pass that will read all the various resources and use them to render meshes via a raster pass
+        void CreateBindlessScope();
+
         // ImGui sidebar
         ImGuiSidebar m_imguiSidebar;
 
@@ -220,6 +251,8 @@ namespace AtomSampleViewer
 
         // Image array holding all of the StreamImages
         AZStd::vector<AZ::Data::Instance<AZ::RPI::StreamingImage>> m_images;
+        // Image array holding all of the Stream cubemap images
+        AZStd::vector<AZ::Data::Instance<AZ::RPI::StreamingImage>> m_cubemapImages;
 
         AZStd::vector<const AZ::RHI::ImageView*> m_imageViews;
 
@@ -246,11 +279,30 @@ namespace AtomSampleViewer
         AZ::RHI::Ptr<AZ::RHI::BufferPool> m_bufferPool = nullptr;
 
         // Indirection buffer holding uint indices of texture resources
-        AZ::RHI::Ptr<AZ::RHI::Buffer> m_indirectionBuffer = nullptr;
+        AZ::RHI::Ptr<AZ::RHI::Buffer> m_imageIndirectionBuffer = nullptr;
+        // Indirection buffer holding uint indices of buffer resources
+        AZ::RHI::Ptr<AZ::RHI::Buffer> m_bufferIndirectionBuffer = nullptr;
+        // View associated with the buffer holding indices to bindless images
+        AZ::RHI::Ptr<AZ::RHI::BufferView> m_imageIndirectionBufferView = nullptr;
+        // View associated with the buffer holding indices to bindless buffers
+        AZ::RHI::Ptr<AZ::RHI::BufferView> m_bufferIndirectionBufferView = nullptr;
+
+        // Color buffer holding color related floats
+        AZ::RHI::Ptr<AZ::RHI::Buffer> m_colorBuffer1 = nullptr;
+        AZ::RHI::Ptr<AZ::RHI::Buffer> m_colorBuffer2 = nullptr;
+        // Views related to the buffers declared above
+        AZ::RHI::Ptr<AZ::RHI::BufferView> m_colorBuffer1View = nullptr;
+        AZ::RHI::Ptr<AZ::RHI::BufferView> m_colorBuffer2View = nullptr;
+
+        // Thread count for compute shaders.
+        int m_bufferNumThreadsX = 1;
+        int m_bufferNumThreadsY = 1;
+        int m_bufferNumThreadsZ = 1;
+
+        int m_imageNumThreadsX = 1;
+        int m_imageNumThreadsY = 1;
+        int m_imageNumThreadsZ = 1;
 
-        // View associated with the indirection buffer
-        AZ::RHI::Ptr<AZ::RHI::BufferView> m_indirectionBufferView = nullptr;
-        
         // Pool size in bytes, 1MB
         static constexpr uint32_t m_poolSizeInBytes = 1u << 20u;
 
@@ -274,6 +326,29 @@ namespace AtomSampleViewer
         static constexpr int32_t m_maxObjectPerAxis = 10u;
         // Total object count
         static constexpr uint32_t m_objectCount = m_maxObjectPerAxis * m_maxObjectPerAxis * m_maxObjectPerAxis;
+
+        // Compute pass related PSOs
+        AZ::RHI::ConstPtr<AZ::RHI::PipelineState> m_bufferDispatchPipelineState;
+        AZ::RHI::ConstPtr<AZ::RHI::PipelineState> m_imageDispatchPipelineState;
+        
+        // Compute pass related buffer pool which will create a rwbuffer
+        AZ::RHI::Ptr<AZ::RHI::BufferPool> m_computeBufferPool;
+        AZ::RHI::Ptr<AZ::RHI::Buffer> m_computeBuffer;
+        AZ::RHI::Ptr<AZ::RHI::BufferView> m_computeBufferView;
+        AZ::RHI::BufferViewDescriptor m_rwBufferViewDescriptor;
+
+         // Compute pass related image pool which will create a rwimage
+        AZ::RHI::Ptr<AZ::RHI::ImagePool> m_rwImagePool;
+        AZ::RHI::Ptr<AZ::RHI::Image> m_computeImage;
+        AZ::RHI::Ptr<AZ::RHI::ImageView> m_computeImageView;
+        AZ::RHI::ImageViewDescriptor m_rwImageViewDescriptor;
+
+        // Compute pass related SRGs
+        AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> m_bufferDispatchSRG;
+        AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> m_imageDispatchSRG;
+        AZ::RHI::AttachmentId m_bufferAttachmentId = AZ::RHI::AttachmentId("bufferAttachmentId");
+        AZ::RHI::AttachmentId m_imageAttachmentId = AZ::RHI::AttachmentId("imageAttachmentId");
+        
     };
 
 } // namespace AtomSampleViewer

+ 34 - 40
Gem/Code/Source/RHI/ComputeExampleComponent.cpp

@@ -9,8 +9,6 @@
 #include <Atom/RHI/CommandList.h>
 #include <Atom/RHI/Factory.h>
 #include <Atom/RHI/FrameScheduler.h>
-#include <Atom/RHI/Image.h>
-#include <Atom/RHI/ImagePool.h>
 #include <Atom/RHI/ScopeProducerFunction.h>
 #include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
 #include <Atom/RHI.Reflect/RenderAttachmentLayoutBuilder.h>
@@ -103,17 +101,17 @@ namespace AtomSampleViewer
         using namespace AZ;
         RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
 
-        m_inputAssemblyBufferPool = RHI::Factory::Get().CreateBufferPool();
+        m_inputAssemblyBufferPool = aznew RHI::BufferPool();
 
         RHI::BufferPoolDescriptor bufferPoolDesc;
         bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
         bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-        m_inputAssemblyBufferPool->Init(*device, bufferPoolDesc);
+        m_inputAssemblyBufferPool->Init(bufferPoolDesc);
 
         BufferData bufferData;
         SetFullScreenRect(bufferData.m_positions.data(), bufferData.m_uvs.data(), bufferData.m_indices.data());
 
-        m_inputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
+        m_inputAssemblyBuffer = aznew RHI::Buffer();
 
         RHI::BufferInitRequest request;
         request.m_buffer = m_inputAssemblyBuffer.get();
@@ -121,36 +119,35 @@ namespace AtomSampleViewer
         request.m_initialData = &bufferData;
         m_inputAssemblyBufferPool->InitBuffer(request);
 
-        m_streamBufferViews[0] =
-        {
+        m_geometryView.SetDrawArguments(RHI::DrawIndexed(0, 6, 0));
+
+        m_geometryView.AddStreamBufferView({
             *m_inputAssemblyBuffer,
             offsetof(BufferData, m_positions),
             sizeof(BufferData::m_positions),
             sizeof(VertexPosition)
-        };
+        });
 
-        m_streamBufferViews[1] =
-        {
+        m_geometryView.AddStreamBufferView({
             *m_inputAssemblyBuffer,
             offsetof(BufferData, m_uvs),
             sizeof(BufferData::m_uvs),
             sizeof(VertexUV)
-        };
+        });
 
-        m_indexBufferView =
-        {
+        m_geometryView.SetIndexBufferView({
             *m_inputAssemblyBuffer,
             offsetof(BufferData, m_indices),
             sizeof(BufferData::m_indices),
             RHI::IndexFormat::Uint16
-        };
+        });
 
         RHI::InputStreamLayoutBuilder layoutBuilder;
         layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
         layoutBuilder.AddBuffer()->Channel("UV", RHI::Format::R32G32_FLOAT);
         m_inputStreamLayout = layoutBuilder.End();
 
-        RHI::ValidateStreamBufferViews(m_inputStreamLayout, m_streamBufferViews);
+        RHI::ValidateStreamBufferViews(m_inputStreamLayout, m_geometryView, m_geometryView.GetFullStreamBufferIndices());
     }
 
     void ComputeExampleComponent::LoadComputeShader()
@@ -234,18 +231,18 @@ namespace AtomSampleViewer
         using namespace AZ;
         RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
 
-        RHI::ResultCode result = RHI::ResultCode::Success;
-        m_computeBufferPool = RHI::Factory::Get().CreateBufferPool();
+        [[maybe_unused]] RHI::ResultCode result = RHI::ResultCode::Success;
+        m_computeBufferPool = aznew RHI::BufferPool();
 
         RHI::BufferPoolDescriptor bufferPoolDesc;
         bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::ShaderReadWrite;
         bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
         bufferPoolDesc.m_hostMemoryAccess = RHI::HostMemoryAccess::Write;
 
-        result = m_computeBufferPool->Init(*device, bufferPoolDesc);
+        result = m_computeBufferPool->Init(bufferPoolDesc);
         AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialized compute buffer pool");
 
-        m_computeBuffer = RHI::Factory::Get().CreateBuffer();
+        m_computeBuffer = aznew RHI::Buffer();
         uint32_t bufferSize = m_bufferWidth * m_bufferHeight * RHI::GetFormatSize(RHI::Format::R32G32B32A32_FLOAT);
 
         RHI::BufferInitRequest request;
@@ -256,13 +253,13 @@ namespace AtomSampleViewer
 
         
         m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(0, m_bufferWidth * m_bufferHeight, RHI::GetFormatSize(RHI::Format::R32G32B32A32_FLOAT));
-        m_computeBufferView = m_computeBuffer->GetBufferView(m_bufferViewDescriptor);
+        m_computeBufferView = m_computeBuffer->BuildBufferView(m_bufferViewDescriptor);
                   
         if(!m_computeBufferView.get())
         {
             AZ_Assert(false, "Failed to initialized compute buffer view");
         }
-        AZ_Assert(m_computeBufferView->IsFullView(), "compute Buffer View initialization failed to cover in full the Compute Buffer");
+        AZ_Assert(m_computeBufferView->GetDeviceBufferView(RHI::MultiDevice::DefaultDeviceIndex)->IsFullView(), "compute Buffer View initialization failed to cover in full the Compute Buffer");
 
     }
 
@@ -287,7 +284,7 @@ namespace AtomSampleViewer
                 desc.m_bufferViewDescriptor = m_bufferViewDescriptor;
                 desc.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f);
 
-                frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite);
+                frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::ComputeShader);
 
                 const Name computeBufferId{ "m_computeBuffer" };
                 RHI::ShaderInputBufferIndex computeBufferIndex = m_dispatchSRGs[1]->FindShaderInputBufferIndex(computeBufferId);
@@ -309,11 +306,11 @@ namespace AtomSampleViewer
             commandList->SetViewports(&m_viewport, 1);
             commandList->SetScissors(&m_scissor, 1);
 
-            AZStd::array <const RHI::ShaderResourceGroup*, 8> shaderResourceGroups;
-            shaderResourceGroups[0] = m_dispatchSRGs[0]->GetRHIShaderResourceGroup();
-            shaderResourceGroups[1] = m_dispatchSRGs[1]->GetRHIShaderResourceGroup();
-            
-            RHI::DispatchItem dispatchItem;
+            AZStd::array<const RHI::DeviceShaderResourceGroup*, 8> shaderResourceGroups;
+            shaderResourceGroups[0] = m_dispatchSRGs[0]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
+            shaderResourceGroups[1] = m_dispatchSRGs[1]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
+
+            RHI::DeviceDispatchItem dispatchItem;
             RHI::DispatchDirect dispatchArgs;
 
             dispatchArgs.m_threadsPerGroupX = aznumeric_cast<uint16_t>(m_numThreadsX);
@@ -324,7 +321,7 @@ namespace AtomSampleViewer
             dispatchArgs.m_totalNumberOfThreadsZ = 1;
 
             dispatchItem.m_arguments = dispatchArgs;
-            dispatchItem.m_pipelineState = m_dispatchPipelineState.get();
+            dispatchItem.m_pipelineState = m_dispatchPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
             dispatchItem.m_shaderResourceGroupCount = 2;
             dispatchItem.m_shaderResourceGroups = shaderResourceGroups;
 
@@ -371,7 +368,7 @@ namespace AtomSampleViewer
                 desc.m_bufferViewDescriptor = m_bufferViewDescriptor;
                 desc.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f);
 
-                frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite);
+                frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::FragmentShader);
 
                 const Name computeBufferId{ "m_computeBuffer" };
                 RHI::ShaderInputBufferIndex computeBufferIndex = m_drawSRGs[1]->FindShaderInputBufferIndex(computeBufferId);
@@ -394,18 +391,15 @@ namespace AtomSampleViewer
             commandList->SetViewports(&m_viewport, 1);
             commandList->SetScissors(&m_scissor, 1);
 
-            RHI::DrawIndexed drawIndexed;
-            drawIndexed.m_indexCount = 6;
-            drawIndexed.m_instanceCount = 1;
-
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_drawSRGs[0]->GetRHIShaderResourceGroup(), m_drawSRGs[1]->GetRHIShaderResourceGroup() };
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_drawSRGs[0]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get(),
+                m_drawSRGs[1]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+            };
 
-            RHI::DrawItem drawItem;
-            drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_drawPipelineState.get();
-            drawItem.m_indexBufferView = &m_indexBufferView;
-            drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
-            drawItem.m_streamBufferViews = m_streamBufferViews.data();
+            RHI::DeviceDrawItem drawItem;
+            drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+            drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
+            drawItem.m_pipelineState = m_drawPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
             drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
             drawItem.m_shaderResourceGroups = shaderResourceGroups;
 

+ 1 - 2
Gem/Code/Source/RHI/ComputeExampleComponent.h

@@ -83,8 +83,7 @@ namespace AtomSampleViewer
         AZ::RHI::Ptr<AZ::RHI::BufferPool> m_inputAssemblyBufferPool;
         AZ::RHI::Ptr<AZ::RHI::Buffer> m_inputAssemblyBuffer;
 
-        AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
-        AZ::RHI::IndexBufferView m_indexBufferView;
+        AZ::RHI::GeometryView m_geometryView;
         AZ::RHI::InputStreamLayout m_inputStreamLayout;
 
         // ----------------------------

+ 25 - 29
Gem/Code/Source/RHI/CopyQueueComponent.cpp

@@ -78,12 +78,12 @@ namespace AtomSampleViewer
         AZ::RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
 
         {
-            m_bufferPool = RHI::Factory::Get().CreateBufferPool();
+            m_bufferPool = aznew RHI::BufferPool();
 
             RHI::BufferPoolDescriptor bufferPoolDesc;
             bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
             bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-            m_bufferPool->Init(*device, bufferPoolDesc);
+            m_bufferPool->Init(bufferPoolDesc);
 
             UpdateVertexPositions(0);
 
@@ -94,9 +94,9 @@ namespace AtomSampleViewer
             SetVertexUV(m_bufferData.m_uvs.data(), 2, 1.0f, 1.0f);
             SetVertexUV(m_bufferData.m_uvs.data(), 3, 1.0f, 0.0f);
 
-            m_positionBuffer = RHI::Factory::Get().CreateBuffer();
-            m_indexBuffer = RHI::Factory::Get().CreateBuffer();
-            m_uvBuffer = RHI::Factory::Get().CreateBuffer();
+            m_positionBuffer = aznew RHI::Buffer();
+            m_indexBuffer = aznew RHI::Buffer();
+            m_uvBuffer = aznew RHI::Buffer();
 
             RHI::ResultCode result = RHI::ResultCode::Success;
             RHI::BufferInitRequest request;
@@ -138,26 +138,35 @@ namespace AtomSampleViewer
                 return;
             }
 
-            m_streamBufferViews[0] = {
+            m_geometryView.SetDrawArguments(RHI::DrawIndexed(0, 6, 0));
+
+            m_geometryView.SetIndexBufferView({
+                *m_indexBuffer,
+                0,
+                indexBufSize,
+                RHI::IndexFormat::Uint16
+            });
+
+            m_geometryView.AddStreamBufferView({
                 *m_positionBuffer,
                 0,
                 positionBufSize,
                 sizeof(VertexPosition)
-            };
+            });
 
-            m_streamBufferViews[1] = {
+            m_geometryView.AddStreamBufferView({
                 *m_uvBuffer,
                 0,
                 uvBufSize,
                 sizeof(VertexUV)
-            };
+            });
 
             RHI::InputStreamLayoutBuilder layoutBuilder;
             layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
             layoutBuilder.AddBuffer()->Channel("UV", RHI::Format::R32G32_FLOAT);
             pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
 
-            RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_streamBufferViews);
+            RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_geometryView, m_geometryView.GetFullStreamBufferIndices());
         }
 
         {
@@ -226,29 +235,16 @@ namespace AtomSampleViewer
                 commandList->SetViewports(&m_viewport, 1);
                 commandList->SetScissors(&m_scissor, 1);
 
-                const RHI::IndexBufferView indexBufferView =
-                {
-                    *m_indexBuffer,
-                    0,
-                    indexBufSize,
-                    RHI::IndexFormat::Uint16
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                    m_shaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
                 };
 
-                RHI::DrawIndexed drawIndexed;
-                drawIndexed.m_indexCount = 6;
-                drawIndexed.m_instanceCount = 1;
-
-                const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroup->GetRHIShaderResourceGroup() };
-
-                RHI::DrawItem drawItem;
-                drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_pipelineState.get();
-                drawItem.m_indexBufferView = &indexBufferView;
+                RHI::DeviceDrawItem drawItem;
+                drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+                drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
+                drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                 drawItem.m_shaderResourceGroups = shaderResourceGroups;
-                drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
-                drawItem.m_streamBufferViews = m_streamBufferViews.data();
-
                 commandList->Submit(drawItem);
             };
 

+ 1 - 2
Gem/Code/Source/RHI/CopyQueueComponent.h

@@ -93,8 +93,7 @@ namespace AtomSampleViewer
 
         BufferData m_bufferData;
 
-        AZ::RHI::DrawItem m_drawItem;
-        AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
+        AZ::RHI::GeometryView m_geometryView;
 
         static const int numberOfPaths = 3;
         const char* m_filePaths[numberOfPaths] = {

+ 19 - 24
Gem/Code/Source/RHI/DualSourceBlendingComponent.cpp

@@ -74,12 +74,12 @@ namespace AtomSampleViewer
         using namespace AZ;
         RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
 
-        m_inputAssemblyBufferPool = RHI::Factory::Get().CreateBufferPool();
+        m_inputAssemblyBufferPool = aznew RHI::BufferPool();
 
         RHI::BufferPoolDescriptor bufferPoolDesc;
         bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
         bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-        m_inputAssemblyBufferPool->Init(*device, bufferPoolDesc);
+        m_inputAssemblyBufferPool->Init(bufferPoolDesc);
 
         BufferData bufferData;
         SetVertexPosition(bufferData.m_positions.data(), 0, -1.0f, -1.0f, 0.0f);
@@ -91,7 +91,7 @@ namespace AtomSampleViewer
         SetVertexColor(bufferData.m_colors.data(), 2, 0.0, 0.0, 1.0, 1.0);
 
         SetVertexIndexIncreasing(bufferData.m_indices.data(), bufferData.m_indices.size());
-        m_inputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
+        m_inputAssemblyBuffer = aznew RHI::Buffer();
 
         RHI::BufferInitRequest request;
         request.m_buffer = m_inputAssemblyBuffer.get();
@@ -99,36 +99,35 @@ namespace AtomSampleViewer
         request.m_initialData = &bufferData;
         m_inputAssemblyBufferPool->InitBuffer(request);
 
-        m_streamBufferViews[0] =
-        {
+        m_geometryView.SetDrawArguments(RHI::DrawIndexed(0, 3, 0));
+
+        m_geometryView.AddStreamBufferView({
             *m_inputAssemblyBuffer,
             offsetof(BufferData, m_positions),
             sizeof(BufferData::m_positions),
             sizeof(VertexPosition)
-        };
+        });
 
-        m_streamBufferViews[1] =
-        {
+        m_geometryView.AddStreamBufferView({
             *m_inputAssemblyBuffer,
             offsetof(BufferData, m_colors),
             sizeof(BufferData::m_colors),
             sizeof(VertexColor)
-        };
+        });
 
-        m_indexBufferView =
-        {
+        m_geometryView.SetIndexBufferView({
             *m_inputAssemblyBuffer,
             offsetof(BufferData, m_indices),
             sizeof(BufferData::m_indices),
             RHI::IndexFormat::Uint16
-        };
+        });
 
         RHI::InputStreamLayoutBuilder layoutBuilder;
         layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
         layoutBuilder.AddBuffer()->Channel("COLOR", RHI::Format::R32G32B32A32_FLOAT);
         m_inputStreamLayout = layoutBuilder.End();
 
-        RHI::ValidateStreamBufferViews(m_inputStreamLayout, m_streamBufferViews);
+        RHI::ValidateStreamBufferViews(m_inputStreamLayout, m_geometryView, m_geometryView.GetFullStreamBufferIndices());
     }
 
     void DualSourceBlendingComponent::LoadRasterShader()
@@ -200,18 +199,14 @@ namespace AtomSampleViewer
             commandList->SetViewports(&m_viewport, 1);
             commandList->SetScissors(&m_scissor, 1);
 
-            RHI::DrawIndexed drawIndexed;
-            drawIndexed.m_indexCount = 3;
-            drawIndexed.m_instanceCount = 1;
-
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroup->GetRHIShaderResourceGroup() };
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_shaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+            };
 
-            RHI::DrawItem drawItem;
-            drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_pipelineState.get();
-            drawItem.m_indexBufferView = &m_indexBufferView;
-            drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
-            drawItem.m_streamBufferViews = m_streamBufferViews.data();
+            RHI::DeviceDrawItem drawItem;
+            drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+            drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
+            drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
             drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
             drawItem.m_shaderResourceGroups = shaderResourceGroups;
 

+ 1 - 2
Gem/Code/Source/RHI/DualSourceBlendingComponent.h

@@ -63,8 +63,7 @@ namespace AtomSampleViewer
         AZ::RHI::Ptr<AZ::RHI::BufferPool> m_inputAssemblyBufferPool;
         AZ::RHI::Ptr<AZ::RHI::Buffer> m_inputAssemblyBuffer;
 
-        AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
-        AZ::RHI::IndexBufferView m_indexBufferView;
+        AZ::RHI::GeometryView m_geometryView;
         AZ::RHI::InputStreamLayout m_inputStreamLayout;
 
         // ----------------------------------

+ 74 - 63
Gem/Code/Source/RHI/IndirectRenderingExampleComponent.cpp

@@ -134,12 +134,12 @@ namespace AtomSampleViewer
         layoutBuilder.AddBuffer(RHI::StreamStepFunction::PerInstance)->Channel("BLENDINDICES0", RHI::Format::R32_UINT);
         m_inputStreamLayout = layoutBuilder.End();
 
-        m_inputAssemblyBufferPool = RHI::Factory::Get().CreateBufferPool();
+        m_inputAssemblyBufferPool = aznew RHI::BufferPool();
 
         RHI::BufferPoolDescriptor bufferPoolDesc;
         bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
         bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-        m_inputAssemblyBufferPool->Init(*device, bufferPoolDesc);
+        m_inputAssemblyBufferPool->Init(bufferPoolDesc);
 
         {
             BufferData bufferData;
@@ -158,7 +158,7 @@ namespace AtomSampleViewer
                 bufferData.m_instanceIndices[i] = i;
             }
 
-            m_inputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
+            m_inputAssemblyBuffer = aznew RHI::Buffer();
 
             RHI::BufferInitRequest request;
             request.m_buffer = m_inputAssemblyBuffer.get();
@@ -241,6 +241,14 @@ namespace AtomSampleViewer
 
             RHI::ValidateStreamBufferViews(m_inputStreamLayout, AZStd::span<const RHI::StreamBufferView>(m_streamBufferViews.data(), 2));
         }
+
+        for (uint32_t i = 0; i < m_numObjects; ++i)
+        {
+            RHI::GeometryView& geoView = m_geometryViews[i];
+            geoView.SetIndexBufferView(m_indexBufferViews[0]);
+            geoView.AddStreamBufferView(m_streamBufferViews[0]);
+            geoView.AddStreamBufferView(m_streamBufferViews[1]);
+        }
     }
 
 
@@ -332,12 +340,12 @@ namespace AtomSampleViewer
         RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
         RHI::ResultCode result;
 
-        m_shaderBufferPool = RHI::Factory::Get().CreateBufferPool();
+        m_shaderBufferPool = aznew RHI::BufferPool();
 
         RHI::BufferPoolDescriptor bufferPoolDesc;
         bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::ShaderRead | RHI::BufferBindFlags::Indirect;
         bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-        m_shaderBufferPool->Init(*device, bufferPoolDesc);
+        m_shaderBufferPool->Init(bufferPoolDesc);
 
         // Create the layout depending on which commands are supported by the device.
         m_indirectDrawBufferLayout = RHI::IndirectBufferLayout();
@@ -357,11 +365,11 @@ namespace AtomSampleViewer
 
         // Create the signature and pass the pipeline state since we may have
         // an inline constants command.
-        m_indirectDrawBufferSignature = RHI::Factory::Get().CreateIndirectBufferSignature();
+        m_indirectDrawBufferSignature = aznew RHI::IndirectBufferSignature;
         RHI::IndirectBufferSignatureDescriptor signatureDescriptor;
         signatureDescriptor.m_layout = m_indirectDrawBufferLayout;
         signatureDescriptor.m_pipelineState = m_drawPipelineState.get();
-        result = m_indirectDrawBufferSignature->Init(*device, signatureDescriptor);
+        result = m_indirectDrawBufferSignature->Init(RHI::MultiDevice::AllDevices, signatureDescriptor);
 
         if (result != RHI::ResultCode::Success)
         {
@@ -369,7 +377,7 @@ namespace AtomSampleViewer
             return;
         }
 
-        m_sourceIndirectBuffer = RHI::Factory::Get().CreateBuffer();
+        m_sourceIndirectBuffer = aznew RHI::Buffer();
 
         uint32_t commandsStride = m_indirectDrawBufferSignature->GetByteStride();
         RHI::BufferInitRequest request;
@@ -380,7 +388,7 @@ namespace AtomSampleViewer
         m_shaderBufferPool->InitBuffer(request);
 
         // Create a writer to populate the buffer with the commands.
-        auto indirectBufferWriter = RHI::Factory::Get().CreateIndirectBufferWriter();
+        RHI::Ptr<RHI::IndirectBufferWriter> indirectBufferWriter = aznew RHI::IndirectBufferWriter;
         result = indirectBufferWriter->Init(*m_sourceIndirectBuffer, 0, commandsStride, s_maxNumberOfObjects, *m_indirectDrawBufferSignature);
         if (result != RHI::ResultCode::Success)
         {
@@ -406,22 +414,11 @@ namespace AtomSampleViewer
                         ->SetVertexView(0, triangleStreamBufferView)
                         ->SetIndexView(triangleIndexBufferView);
                 }
-                indirectBufferWriter->DrawIndexed(
-                    RHI::DrawIndexed(
-                        1,
-                        i,
-                        0,
-                        3,
-                        0));
+                indirectBufferWriter->DrawIndexed(RHI::DrawIndexed(0, 3, 0), RHI::DrawInstanceArguments(1, i));
             }
             else
             {
-                RHI::DrawIndexed arguments(
-                    1,
-                    i,
-                    0,
-                    6,
-                    0);
+                RHI::DrawIndexed arguments(0, 6, 0);
 
                 switch (m_mode)
                 {
@@ -441,7 +438,7 @@ namespace AtomSampleViewer
                     return;
                 }
 
-                indirectBufferWriter->DrawIndexed(arguments);
+                indirectBufferWriter->DrawIndexed(arguments, RHI::DrawInstanceArguments(1, i));
             }
 
             indirectBufferWriter->NextSequence();
@@ -450,7 +447,7 @@ namespace AtomSampleViewer
         indirectBufferWriter->Shutdown();
 
         auto viewDescriptor = RHI::BufferViewDescriptor::CreateStructured(0, s_maxNumberOfObjects, commandsStride);
-        m_sourceIndirectBufferView = m_sourceIndirectBuffer->GetBufferView(viewDescriptor);
+        m_sourceIndirectBufferView = m_sourceIndirectBuffer->BuildBufferView(viewDescriptor);
                   
         if(!m_sourceIndirectBufferView.get())
         {
@@ -459,7 +456,7 @@ namespace AtomSampleViewer
         }
 
         // Create the buffer that will contain the compute dispatch arguments.
-        m_indirectDispatchBuffer = RHI::Factory::Get().CreateBuffer();
+        m_indirectDispatchBuffer = aznew RHI::Buffer();
         {
             m_indirectDispatchBufferLayout = RHI::IndirectBufferLayout();
             m_indirectDispatchBufferLayout.AddIndirectCommand(RHI::IndirectCommandDescriptor(RHI::IndirectCommandType::Dispatch));
@@ -469,10 +466,10 @@ namespace AtomSampleViewer
                 return;
             }
 
-            m_indirectDispatchBufferSignature = RHI::Factory::Get().CreateIndirectBufferSignature();
+            m_indirectDispatchBufferSignature = aznew RHI::IndirectBufferSignature;
             signatureDescriptor = {};
             signatureDescriptor.m_layout = m_indirectDispatchBufferLayout;
-            result = m_indirectDispatchBufferSignature->Init(*device, signatureDescriptor);
+            result = m_indirectDispatchBufferSignature->Init(RHI::MultiDevice::AllDevices, signatureDescriptor);
 
             if (result != RHI::ResultCode::Success)
             {
@@ -497,7 +494,7 @@ namespace AtomSampleViewer
                 indirectDispatchStride
             };
 
-            m_indirectDispatchWriter = RHI::Factory::Get().CreateIndirectBufferWriter();
+            m_indirectDispatchWriter = aznew RHI::IndirectBufferWriter;
             result = m_indirectDispatchWriter->Init(*m_indirectDispatchBuffer, 0, indirectDispatchStride, 1, *m_indirectDispatchBufferSignature);
             if (result != RHI::ResultCode::Success)
             {
@@ -525,14 +522,14 @@ namespace AtomSampleViewer
             data.m_velocity.Set(GetRandomFloat(IndirectRendering::VelocityRange.GetX(), IndirectRendering::VelocityRange.GetY()));
         }
 
-        m_instancesBufferPool = RHI::Factory::Get().CreateBufferPool();
+        m_instancesBufferPool = aznew RHI::BufferPool();
 
         RHI::BufferPoolDescriptor bufferPoolDesc;
         bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::ShaderRead;
         bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Host;
-        m_instancesBufferPool->Init(*device, bufferPoolDesc);
+        m_instancesBufferPool->Init(bufferPoolDesc);
 
-        m_instancesDataBuffer = RHI::Factory::Get().CreateBuffer();
+        m_instancesDataBuffer = aznew RHI::Buffer();
 
         RHI::BufferInitRequest request;
         request.m_buffer = m_instancesDataBuffer.get();
@@ -543,7 +540,7 @@ namespace AtomSampleViewer
         m_instancesBufferPool->InitBuffer(request);
 
         auto descriptor = RHI::BufferViewDescriptor::CreateStructured(0, static_cast<uint32_t>(m_instancesData.size()), sizeof(InstanceData));
-        m_instancesDataBufferView = m_instancesDataBuffer->GetBufferView(descriptor);
+        m_instancesDataBufferView = m_instancesDataBuffer->BuildBufferView(descriptor);
                   
         if(!m_instancesDataBufferView.get())
         {
@@ -556,14 +553,14 @@ namespace AtomSampleViewer
         // the compute shader has culled the commands.
         if (m_deviceSupportsCountBuffer)
         {
-            m_copyBufferPool = RHI::Factory::Get().CreateBufferPool();
+            m_copyBufferPool = aznew RHI::BufferPool();
 
             bufferPoolDesc = {};
             bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::CopyRead;
             bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Host;
-            m_copyBufferPool->Init(*device, bufferPoolDesc);
+            m_copyBufferPool->Init(bufferPoolDesc);
 
-            m_resetCounterBuffer = RHI::Factory::Get().CreateBuffer();
+            m_resetCounterBuffer = aznew RHI::Buffer();
 
             AZStd::vector<uint32_t> initData;
             initData.assign(static_cast<size_t>(std::ceil(float(s_maxNumberOfObjects) / maxIndirectDrawCount)), 0);
@@ -619,17 +616,17 @@ namespace AtomSampleViewer
 
         const auto compileFunction = [this](const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
         {
-            const RHI::BufferView* countBufferView = context.GetBufferView(RHI::AttachmentId{ IndirectRendering::CountBufferAttachmentId });
+            const auto* countBufferView = context.GetBufferView(RHI::AttachmentId{ IndirectRendering::CountBufferAttachmentId });
             m_copyDescriptor.m_sourceBuffer = m_resetCounterBuffer.get();
             m_copyDescriptor.m_sourceOffset = 0;
-            m_copyDescriptor.m_destinationBuffer = &countBufferView->GetBuffer();
+            m_copyDescriptor.m_destinationBuffer = countBufferView->GetBuffer();
             m_copyDescriptor.m_destinationOffset = 0;
             m_copyDescriptor.m_size = static_cast<uint32_t>(m_resetCounterBuffer->GetDescriptor().m_byteCount);
         };
 
         const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
         {
-            RHI::CopyItem copyItem(m_copyDescriptor);
+            RHI::DeviceCopyItem copyItem(m_copyDescriptor.GetDeviceCopyBufferDescriptor(context.GetDeviceIndex()));
             context.GetCommandList()->Submit(copyItem);
         };
 
@@ -666,7 +663,8 @@ namespace AtomSampleViewer
             culledBufferAttachment.m_attachmentId = IndirectRendering::CulledIndirectBufferAttachmentId;
             culledBufferAttachment.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::DontCare;
             culledBufferAttachment.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(0, m_numObjects, commandsStride);
-            frameGraph.UseShaderAttachment(culledBufferAttachment, RHI::ScopeAttachmentAccess::ReadWrite);
+            frameGraph.UseShaderAttachment(
+                culledBufferAttachment, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::ComputeShader);
 
             if (m_deviceSupportsCountBuffer)
             {
@@ -678,14 +676,15 @@ namespace AtomSampleViewer
                     0,
                     static_cast<uint32_t>(m_resetCounterBuffer->GetDescriptor().m_byteCount / sizeof(uint32_t)),
                     sizeof(uint32_t));
-                frameGraph.UseShaderAttachment(countBufferAttachment, RHI::ScopeAttachmentAccess::ReadWrite);
+                frameGraph.UseShaderAttachment(
+                    countBufferAttachment, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::ComputeShader);
             }
         };
 
         const auto compileFunction = [this, maxIndirectDrawCount](const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
         {
 
-            const RHI::BufferView* culledBufferView = context.GetBufferView(RHI::AttachmentId{ IndirectRendering::CulledIndirectBufferAttachmentId });
+            const auto* culledBufferView = context.GetBufferView(RHI::AttachmentId{ IndirectRendering::CulledIndirectBufferAttachmentId });
 
             uint32_t sequenceTypeIndex = static_cast<uint32_t>(m_mode);
             auto& indirectCommandsSRG = m_indirectCommandsShaderResourceGroups[sequenceTypeIndex];
@@ -694,10 +693,10 @@ namespace AtomSampleViewer
 
             if (m_deviceSupportsCountBuffer)
             {
-                const RHI::BufferView* countBufferView = context.GetBufferView(RHI::AttachmentId{ IndirectRendering::CountBufferAttachmentId });
+                const auto* countBufferView = context.GetBufferView(RHI::AttachmentId{ IndirectRendering::CountBufferAttachmentId });
 
                 m_cullShaderResourceGroup->SetBufferView(m_cullingCountBufferIndex, countBufferView);
-                m_drawIndirect.m_countBuffer = &countBufferView->GetBuffer();
+                m_drawIndirect.m_countBuffer = countBufferView->GetBuffer();
                 m_drawIndirect.m_countBufferByteOffset = 0;
             }
 
@@ -712,7 +711,7 @@ namespace AtomSampleViewer
             uint32_t stride = m_indirectDrawBufferSignature->GetByteStride();
             m_indirectDrawBufferView =
             {
-                culledBufferView->GetBuffer(),
+                *(culledBufferView->GetBuffer()),
                 *m_indirectDrawBufferSignature,
                 0,
                 stride * m_numObjects,
@@ -724,22 +723,23 @@ namespace AtomSampleViewer
         {
             RHI::CommandList* commandList = context.GetCommandList();
 
-            RHI::DispatchItem dispatchItem;
+            RHI::DeviceDispatchItem dispatchItem;
             uint32_t numSrgs = 0;
-            dispatchItem.m_shaderResourceGroups[numSrgs++] = m_cullShaderResourceGroup->GetRHIShaderResourceGroup();
-            dispatchItem.m_shaderResourceGroups[numSrgs++] = m_sceneShaderResourceGroup->GetRHIShaderResourceGroup();
+            dispatchItem.m_shaderResourceGroups[numSrgs++] = m_cullShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
+            dispatchItem.m_shaderResourceGroups[numSrgs++] = m_sceneShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
 
             for (const auto& srg : m_indirectCommandsShaderResourceGroups)
             {
-                dispatchItem.m_shaderResourceGroups[numSrgs++] = srg->GetRHIShaderResourceGroup();
+                dispatchItem.m_shaderResourceGroups[numSrgs++] = srg->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
             }
 
             // Submit the dispatch in an indirect manner.
             // Not really needed but it tests the indirect dispatch code.
-            RHI::DispatchIndirect dispatchArgs(1, m_indirectDispatchBufferView, 0);
+            RHI::DeviceDispatchIndirect dispatchArgs(
+                1, m_indirectDispatchBufferView.GetDeviceIndirectBufferView(context.GetDeviceIndex()), 0);
 
             dispatchItem.m_arguments = dispatchArgs;
-            dispatchItem.m_pipelineState = m_cullPipelineState.get();
+            dispatchItem.m_pipelineState = m_cullPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
             dispatchItem.m_shaderResourceGroupCount = static_cast<uint8_t>(numSrgs);
 
             commandList->Submit(dispatchItem);
@@ -783,7 +783,9 @@ namespace AtomSampleViewer
                 dsDesc.m_imageViewDescriptor.m_overrideFormat = AZ::RHI::Format::D32_FLOAT;
                 dsDesc.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateDepth(1.f);
                 dsDesc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Clear;
-                frameGraph.UseDepthStencilAttachment(dsDesc, AZ::RHI::ScopeAttachmentAccess::ReadWrite);
+                frameGraph.UseDepthStencilAttachment(
+                    dsDesc, AZ::RHI::ScopeAttachmentAccess::ReadWrite,
+                    RHI::ScopeAttachmentStage::EarlyFragmentTest | RHI::ScopeAttachmentStage::LateFragmentTest);
             }
 
             if (m_deviceSupportsCountBuffer)
@@ -797,7 +799,9 @@ namespace AtomSampleViewer
                     0,
                     static_cast<uint32_t>(m_resetCounterBuffer->GetDescriptor().m_byteCount / sizeof(uint32_t)),
                     sizeof(uint32_t));
-                frameGraph.UseAttachment(descriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentUsage::Indirect);
+                frameGraph.UseAttachment(
+                    descriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentUsage::Indirect,
+                    RHI::ScopeAttachmentStage::DrawIndirect);
             }
 
             {
@@ -810,7 +814,9 @@ namespace AtomSampleViewer
                     0,
                     m_numObjects,
                     m_indirectDrawBufferSignature->GetByteStride());
-                frameGraph.UseAttachment(descriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentUsage::Indirect);
+                frameGraph.UseAttachment(
+                    descriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentUsage::Indirect,
+                    RHI::ScopeAttachmentStage::DrawIndirect);
             }
 
             frameGraph.SetEstimatedItemCount(uint32_t(std::ceil(m_numObjects/ float(maxIndirectDrawCount))));
@@ -826,7 +832,9 @@ namespace AtomSampleViewer
             commandList->SetViewports(&m_viewport, 1);
             commandList->SetScissors(&m_scissor, 1);
 
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_sceneShaderResourceGroup->GetRHIShaderResourceGroup() };
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_sceneShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+            };
 
             // In case multi indirect drawing is not supported
             // we need to emit multiple indirect draw calls.
@@ -839,16 +847,14 @@ namespace AtomSampleViewer
                 m_drawIndirect.m_maxSequenceCount = AZStd::min(m_numObjects - i, maxIndirectDrawCount);
                 m_drawIndirect.m_indirectBufferByteOffset = i * m_indirectDrawBufferView.GetByteStride();
                 m_drawIndirect.m_indirectBufferView = &m_indirectDrawBufferView;
+                m_geometryViews[i].SetDrawArguments(m_drawIndirect);
 
-                RHI::DrawItem drawItem;
-                drawItem.m_arguments = m_drawIndirect;
-                drawItem.m_pipelineState = m_drawPipelineState.get();
-                drawItem.m_indexBufferView = &m_indexBufferViews[0];
+                RHI::DeviceDrawItem drawItem;
+                drawItem.m_geometryView = m_geometryViews[i].GetDeviceGeometryView(context.GetDeviceIndex());
+                drawItem.m_streamIndices = m_geometryViews[i].GetFullStreamBufferIndices();
+                drawItem.m_pipelineState = m_drawPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                 drawItem.m_shaderResourceGroups = shaderResourceGroups;
-                drawItem.m_streamBufferViewCount = 2;
-                drawItem.m_streamBufferViews = m_streamBufferViews.data();
-
                 // Submit the indirect draw item.
                 commandList->Submit(drawItem);
             }
@@ -872,6 +878,7 @@ namespace AtomSampleViewer
         using namespace AZ;
 
         m_numObjects = s_maxNumberOfObjects / 2;
+        m_geometryViews.resize(m_numObjects);
 
         RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
 
@@ -984,6 +991,7 @@ namespace AtomSampleViewer
         m_indirectDispatchBufferSignature = nullptr;
 
         m_instancesData.clear();
+        m_geometryViews.clear();
 
         m_imguiSidebar.Deactivate();
         AzFramework::WindowNotificationBus::Handler::BusDisconnect();
@@ -1025,9 +1033,12 @@ namespace AtomSampleViewer
         RHI::BufferMapResponse response;
 
         m_instancesBufferPool->MapBuffer(request, response);
-        if (response.m_data)
+        if (!response.m_data.empty())
         {
-            ::memcpy(response.m_data, m_instancesData.data(), request.m_byteCount);
+            for(auto& [_, responseData] : response.m_data)
+            {
+                ::memcpy(responseData, m_instancesData.data(), request.m_byteCount);
+            }
             m_instancesBufferPool->UnmapBuffer(*m_instancesDataBuffer);
         }
     }

+ 1 - 0
Gem/Code/Source/RHI/IndirectRenderingExampleComponent.h

@@ -171,6 +171,7 @@ namespace AtomSampleViewer
 
         AZStd::array<AZ::Data::Instance<AZ::RPI::ShaderResourceGroup>, NumSequencesType> m_indirectCommandsShaderResourceGroups;
 
+        AZStd::vector<AZ::RHI::GeometryView> m_geometryViews;
         AZStd::array<AZ::RHI::StreamBufferView, 3> m_streamBufferViews;
         AZStd::array<AZ::RHI::IndexBufferView, 2> m_indexBufferViews;
         AZ::RHI::IndirectBufferView m_indirectDrawBufferView;

+ 29 - 24
Gem/Code/Source/RHI/InputAssemblyExampleComponent.cpp

@@ -145,14 +145,14 @@ namespace AtomSampleViewer
     {
         using namespace AZ;
         RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
-        m_inputAssemblyBufferPool = RHI::Factory::Get().CreateBufferPool();
+        m_inputAssemblyBufferPool = aznew RHI::BufferPool();
 
         RHI::BufferPoolDescriptor bufferPoolDesc;
         bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly | RHI::BufferBindFlags::ShaderReadWrite;
         bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-        m_inputAssemblyBufferPool->Init(*device, bufferPoolDesc);
+        m_inputAssemblyBufferPool->Init(bufferPoolDesc);
 
-        m_inputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
+        m_inputAssemblyBuffer = aznew RHI::Buffer();
 
         RHI::BufferInitRequest request;
         request.m_buffer = m_inputAssemblyBuffer.get();
@@ -248,7 +248,8 @@ namespace AtomSampleViewer
                 attachmentDescriptor.m_attachmentId = InputAssembly::InputAssemblyBufferAttachmentId;
                 attachmentDescriptor.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(0, BufferData::array_size, sizeof(BufferData::value_type));
                 attachmentDescriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::DontCare;
-                frameGraph.UseShaderAttachment(attachmentDescriptor, RHI::ScopeAttachmentAccess::ReadWrite);
+                frameGraph.UseShaderAttachment(
+                    attachmentDescriptor, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::ComputeShader);
             }
 
             {
@@ -256,7 +257,8 @@ namespace AtomSampleViewer
                 attachmentDescriptor.m_attachmentId = InputAssembly::ImportedInputAssemblyBufferAttachmentId;
                 attachmentDescriptor.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateStructured(0, BufferData::array_size, sizeof(BufferData::value_type));
                 attachmentDescriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::DontCare;
-                frameGraph.UseShaderAttachment(attachmentDescriptor, RHI::ScopeAttachmentAccess::ReadWrite);
+                frameGraph.UseShaderAttachment(
+                    attachmentDescriptor, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::ComputeShader);
             }
 
             frameGraph.SetEstimatedItemCount(2);
@@ -266,14 +268,14 @@ namespace AtomSampleViewer
         {
             AZ_UNUSED(scopeData);
             {
-                const RHI::BufferView* inputAssemblyBufferView = context.GetBufferView(RHI::AttachmentId{ InputAssembly::InputAssemblyBufferAttachmentId });
+                const auto* inputAssemblyBufferView = context.GetBufferView(RHI::AttachmentId{ InputAssembly::InputAssemblyBufferAttachmentId });
                 m_dispatchSRG[0]->SetBufferView(m_dispatchIABufferIndex, inputAssemblyBufferView);
                 m_dispatchSRG[0]->SetConstant(m_dispatchTimeConstantIndex, m_time);
                 m_dispatchSRG[0]->Compile();
             }
 
             {
-                const RHI::BufferView* inputAssemblyBufferView = context.GetBufferView(RHI::AttachmentId{ InputAssembly::ImportedInputAssemblyBufferAttachmentId });
+                const auto* inputAssemblyBufferView = context.GetBufferView(RHI::AttachmentId{ InputAssembly::ImportedInputAssemblyBufferAttachmentId });
                 m_dispatchSRG[1]->SetBufferView(m_dispatchIABufferIndex, inputAssemblyBufferView);
                 m_dispatchSRG[1]->SetConstant(m_dispatchTimeConstantIndex, m_time);
                 m_dispatchSRG[1]->Compile();
@@ -284,8 +286,8 @@ namespace AtomSampleViewer
         {
             AZ_UNUSED(scopeData);
             RHI::CommandList* commandList = context.GetCommandList();
-          
-            RHI::DispatchItem dispatchItem;
+
+            RHI::DeviceDispatchItem dispatchItem;
             RHI::DispatchDirect dispatchArgs;
 
             dispatchArgs.m_threadsPerGroupX = aznumeric_cast<uint16_t>(m_numThreadsX);
@@ -296,12 +298,12 @@ namespace AtomSampleViewer
             dispatchArgs.m_totalNumberOfThreadsZ = 1;
 
             dispatchItem.m_arguments = dispatchArgs;
-            dispatchItem.m_pipelineState = m_dispatchPipelineState.get();
+            dispatchItem.m_pipelineState = m_dispatchPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
             dispatchItem.m_shaderResourceGroupCount = 1;
 
             for (uint32_t index = context.GetSubmitRange().m_startIndex; index < context.GetSubmitRange().m_endIndex; ++index)
             {
-                dispatchItem.m_shaderResourceGroups[0] = m_dispatchSRG[index]->GetRHIShaderResourceGroup();
+                dispatchItem.m_shaderResourceGroups[0] = m_dispatchSRG[index]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
                 commandList->Submit(dispatchItem, index);
             }
         };
@@ -366,22 +368,24 @@ namespace AtomSampleViewer
         {
             AZ_UNUSED(scopeData);
             {
-                const RHI::BufferView* inputAssemblyBufferView = context.GetBufferView(RHI::AttachmentId{ InputAssembly::InputAssemblyBufferAttachmentId });
+                const auto* inputAssemblyBufferView = context.GetBufferView(RHI::AttachmentId{ InputAssembly::InputAssemblyBufferAttachmentId });
                 if (inputAssemblyBufferView)
                 {
-                    m_streamBufferView[0] = {inputAssemblyBufferView->GetBuffer(), 0, sizeof(BufferData), sizeof(BufferData::value_type)};
+                    m_geometryView[0].ClearStreamBufferViews();
+                    m_geometryView[0].AddStreamBufferView({*(inputAssemblyBufferView->GetBuffer()), 0, sizeof(BufferData), sizeof(BufferData::value_type)});
 
-                    RHI::ValidateStreamBufferViews(m_inputStreamLayout, AZStd::span<const RHI::StreamBufferView>(&m_streamBufferView[0], 1));
+                    RHI::ValidateStreamBufferViews(m_inputStreamLayout, m_geometryView[0], m_geometryView[0].GetFullStreamBufferIndices());
                 }
             }
 
             {
-                const RHI::BufferView* inputAssemblyBufferView = context.GetBufferView(RHI::AttachmentId{ InputAssembly::ImportedInputAssemblyBufferAttachmentId });
+                const auto* inputAssemblyBufferView = context.GetBufferView(RHI::AttachmentId{ InputAssembly::ImportedInputAssemblyBufferAttachmentId });
                 if (inputAssemblyBufferView)
                 {
-                    m_streamBufferView[1] = {inputAssemblyBufferView->GetBuffer(), 0, sizeof(BufferData), sizeof(BufferData::value_type)};
+                    m_geometryView[1].ClearStreamBufferViews();
+                    m_geometryView[1].AddStreamBufferView({*(inputAssemblyBufferView->GetBuffer()), 0, sizeof(BufferData), sizeof(BufferData::value_type)});
 
-                    RHI::ValidateStreamBufferViews(m_inputStreamLayout, AZStd::span<const RHI::StreamBufferView>(&m_streamBufferView[1], 1));
+                    RHI::ValidateStreamBufferViews(m_inputStreamLayout, m_geometryView[1], m_geometryView[1].GetFullStreamBufferIndices());
                 }
             }
         };
@@ -398,18 +402,19 @@ namespace AtomSampleViewer
             RHI::DrawLinear drawLinear;
             drawLinear.m_vertexCount = BufferData::array_size;
 
-            RHI::DrawItem drawItem;
-            drawItem.m_arguments = drawLinear;
-            drawItem.m_pipelineState = m_drawPipelineState.get();
-            drawItem.m_indexBufferView = nullptr;
-            drawItem.m_streamBufferViewCount = 1;
+            RHI::DeviceDrawItem drawItem;
+            drawItem.m_pipelineState = m_drawPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
             drawItem.m_shaderResourceGroupCount = 1;
 
             for (uint32_t index = context.GetSubmitRange().m_startIndex; index < context.GetSubmitRange().m_endIndex; ++index)
             {
-                drawItem.m_streamBufferViews = &m_streamBufferView[index];
+                m_geometryView[index].SetDrawArguments(drawLinear);
+                drawItem.m_geometryView = m_geometryView[index].GetDeviceGeometryView(context.GetDeviceIndex());
+                drawItem.m_streamIndices = m_geometryView[index].GetFullStreamBufferIndices();
 
-                RHI::ShaderResourceGroup* rhiSRGS[] = { m_drawSRG[index]->GetRHIShaderResourceGroup() };
+                RHI::DeviceShaderResourceGroup* rhiSRGS[] = {
+                    m_drawSRG[index]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+                };
                 drawItem.m_shaderResourceGroups = rhiSRGS;
 
                 commandList->Submit(drawItem, index);

+ 1 - 1
Gem/Code/Source/RHI/InputAssemblyExampleComponent.h

@@ -65,7 +65,7 @@ namespace AtomSampleViewer
         // -------------------------------------------------
         // Input Assembly buffer and its Streams/Index Views
         // -------------------------------------------------
-        AZ::RHI::StreamBufferView m_streamBufferView[2];
+        AZ::RHI::GeometryView m_geometryView[2];
         AZ::RHI::InputStreamLayout m_inputStreamLayout;
 
         AZ::RHI::Ptr<AZ::RHI::BufferPool> m_inputAssemblyBufferPool;

+ 38 - 45
Gem/Code/Source/RHI/MRTExampleComponent.cpp

@@ -9,8 +9,6 @@
 #include <Atom/RHI/CommandList.h>
 #include <Atom/RHI/Factory.h>
 #include <Atom/RHI/FrameScheduler.h>
-#include <Atom/RHI/Image.h>
-#include <Atom/RHI/ImagePool.h>
 #include <Atom/RHI/ScopeProducerFunction.h>
 #include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
 #include <Atom/RHI.Reflect/RenderAttachmentLayoutBuilder.h>
@@ -172,16 +170,16 @@ namespace AtomSampleViewer
         using namespace AZ;
         const RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
 
-        m_bufferPool = RHI::Factory::Get().CreateBufferPool();
+        m_bufferPool = aznew RHI::BufferPool();
         RHI::BufferPoolDescriptor bufferPoolDesc;
         bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
         bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-        m_bufferPool->Init(*device, bufferPoolDesc);
+        m_bufferPool->Init(bufferPoolDesc);
 
         BufferData bufferData;
         SetFullScreenRect(bufferData.m_positions.data(), bufferData.m_uvs.data(), bufferData.m_indices.data());
 
-        m_inputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
+        m_inputAssemblyBuffer = aznew RHI::Buffer();
         RHI::ResultCode result = RHI::ResultCode::Success;
         RHI::BufferInitRequest request;
 
@@ -195,36 +193,35 @@ namespace AtomSampleViewer
             return;
         }
 
-        m_streamBufferViews[0] =
-        {
+        m_geometryView.SetDrawArguments(RHI::DrawIndexed(0, 6, 0));
+
+        m_geometryView.AddStreamBufferView({
             *m_inputAssemblyBuffer,
             offsetof(BufferData, m_positions),
             sizeof(BufferData::m_positions),
             sizeof(VertexPosition)
-        };
+        });
 
-        m_streamBufferViews[1] =
-        {
+        m_geometryView.AddStreamBufferView({
             *m_inputAssemblyBuffer,
             offsetof(BufferData, m_uvs),
             sizeof(BufferData::m_uvs),
             sizeof(VertexUV)
-        };
+        });
 
-        m_indexBufferView =
-        {
+        m_geometryView.SetIndexBufferView({
             *m_inputAssemblyBuffer,
             offsetof(BufferData, m_indices),
             sizeof(BufferData::m_indices),
             RHI::IndexFormat::Uint16
-        };
+        });
 
         RHI::InputStreamLayoutBuilder layoutBuilder;
         layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
         layoutBuilder.AddBuffer()->Channel("UV", RHI::Format::R32G32_FLOAT);
         m_inputStreamLayout = layoutBuilder.End();
 
-        RHI::ValidateStreamBufferViews(m_inputStreamLayout, m_streamBufferViews);
+        RHI::ValidateStreamBufferViews(m_inputStreamLayout, m_geometryView, m_geometryView.GetFullStreamBufferIndices());
     }
 
     void MRTExampleComponent::InitRenderTargets()
@@ -288,21 +285,16 @@ namespace AtomSampleViewer
             commandList->SetViewports(&viewport, 1);
             commandList->SetScissors(&scissor, 1);
 
-            RHI::DrawIndexed drawIndexed;
-            drawIndexed.m_indexCount = 6;
-            drawIndexed.m_instanceCount = 1;
-
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[0]->GetRHIShaderResourceGroup() };
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_shaderResourceGroups[0]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+            };
 
-            RHI::DrawItem drawItem;
-            drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_pipelineStates[0].get();
-            drawItem.m_indexBufferView = &m_indexBufferView;
+            RHI::DeviceDrawItem drawItem;
+            drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+            drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
+            drawItem.m_pipelineState = m_pipelineStates[0]->GetDevicePipelineState(context.GetDeviceIndex()).get();
             drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
             drawItem.m_shaderResourceGroups = shaderResourceGroups;
-            drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
-            drawItem.m_streamBufferViews = m_streamBufferViews.data();
-
             commandList->Submit(drawItem);
         };
 
@@ -333,9 +325,15 @@ namespace AtomSampleViewer
 
             // Bind shader attachments which are rendered by RenderTargetScope.
             {
-                frameGraph.UseShaderAttachment(RHI::ImageScopeAttachmentDescriptor(m_attachmentID[0]), RHI::ScopeAttachmentAccess::Read);
-                frameGraph.UseShaderAttachment(RHI::ImageScopeAttachmentDescriptor(m_attachmentID[1]), RHI::ScopeAttachmentAccess::Read);
-                frameGraph.UseShaderAttachment(RHI::ImageScopeAttachmentDescriptor(m_attachmentID[2]), RHI::ScopeAttachmentAccess::Read);
+                frameGraph.UseShaderAttachment(
+                    RHI::ImageScopeAttachmentDescriptor(m_attachmentID[0]), RHI::ScopeAttachmentAccess::Read,
+                    RHI::ScopeAttachmentStage::FragmentShader);
+                frameGraph.UseShaderAttachment(
+                    RHI::ImageScopeAttachmentDescriptor(m_attachmentID[1]), RHI::ScopeAttachmentAccess::Read,
+                    RHI::ScopeAttachmentStage::FragmentShader);
+                frameGraph.UseShaderAttachment(
+                    RHI::ImageScopeAttachmentDescriptor(m_attachmentID[2]), RHI::ScopeAttachmentAccess::Read,
+                    RHI::ScopeAttachmentStage::FragmentShader);
             }
 
             // We will submit a single draw item.
@@ -344,9 +342,9 @@ namespace AtomSampleViewer
 
         const auto compileFunctionScreen = [this](const AZ::RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
         {
-            const AZ::RHI::ImageView* imageViewR = context.GetImageView(m_attachmentID[0]);
-            const AZ::RHI::ImageView* imageViewG = context.GetImageView(m_attachmentID[1]);
-            const AZ::RHI::ImageView* imageViewB = context.GetImageView(m_attachmentID[2]);
+            const auto* imageViewR = context.GetImageView(m_attachmentID[0]);
+            const auto* imageViewG = context.GetImageView(m_attachmentID[1]);
+            const auto* imageViewB = context.GetImageView(m_attachmentID[2]);
 
             m_shaderResourceGroups[1]->SetImageView(m_shaderInputImageIndices[0], imageViewR);
             m_shaderResourceGroups[1]->SetImageView(m_shaderInputImageIndices[1], imageViewG);
@@ -362,21 +360,16 @@ namespace AtomSampleViewer
             commandList->SetViewports(&m_viewport, 1);
             commandList->SetScissors(&m_scissor, 1);
 
-            RHI::DrawIndexed drawIndexed;
-            drawIndexed.m_indexCount = 6;
-            drawIndexed.m_instanceCount = 1;
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_shaderResourceGroups[1]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+            };
 
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[1]->GetRHIShaderResourceGroup() };
-
-            RHI::DrawItem drawItem;
-            drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_pipelineStates[1].get();
-            drawItem.m_indexBufferView = &m_indexBufferView;
+            RHI::DeviceDrawItem drawItem;
+            drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+            drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
+            drawItem.m_pipelineState = m_pipelineStates[1]->GetDevicePipelineState(context.GetDeviceIndex()).get();
             drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
             drawItem.m_shaderResourceGroups = shaderResourceGroups;
-            drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
-            drawItem.m_streamBufferViews = m_streamBufferViews.data();
-
             commandList->Submit(drawItem);
         };
 

+ 1 - 4
Gem/Code/Source/RHI/MRTExampleComponent.h

@@ -9,7 +9,6 @@
 #pragma once
 
 #include <Atom/RHI/BufferPool.h>
-#include <Atom/RHI/DrawItem.h>
 #include <Atom/RHI/ScopeProducer.h>
 
 #include <Atom/RPI.Public/Image/AttachmentImage.h>
@@ -80,10 +79,8 @@ namespace AtomSampleViewer
             AZStd::array<VertexUV, 4> m_uvs;
             AZStd::array<uint16_t, 6> m_indices;
         };
-        AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
-        AZ::RHI::IndexBufferView m_indexBufferView;
+        AZ::RHI::GeometryView m_geometryView;
 
-        AZ::RHI::DrawItem m_drawItem;
         AZStd::array<AZ::RHI::AttachmentId, 3> m_attachmentID;
         AZ::RHI::ClearValue m_clearValue;
         float m_time;

+ 50 - 59
Gem/Code/Source/RHI/MSAAExampleComponent.cpp

@@ -48,12 +48,12 @@ namespace AtomSampleViewer
     {
         using namespace AZ;
         RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
-        m_inputAssemblyBufferPool = RHI::Factory::Get().CreateBufferPool();
+        m_inputAssemblyBufferPool = aznew RHI::BufferPool();
 
         RHI::BufferPoolDescriptor bufferPoolDesc;
         bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
         bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-        m_inputAssemblyBufferPool->Init(*device, bufferPoolDesc);
+        m_inputAssemblyBufferPool->Init(bufferPoolDesc);
 
         AZStd::vector<RHI::SamplePosition> emptySamplePositions;
         AZStd::vector<RHI::SamplePosition> customSamplePositions = { RHI::SamplePosition(3, 3), RHI::SamplePosition(11, 3), RHI::SamplePosition(3, 11), RHI::SamplePosition(11, 11) };
@@ -136,7 +136,7 @@ namespace AtomSampleViewer
 
         SetVertexIndexIncreasing(bufferData.m_indices.data(), bufferData.m_indices.size());
 
-        m_triangleInputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
+        m_triangleInputAssemblyBuffer = aznew RHI::Buffer();
 
         RHI::BufferInitRequest request;
         request.m_buffer = m_triangleInputAssemblyBuffer.get();
@@ -144,28 +144,35 @@ namespace AtomSampleViewer
         request.m_initialData = &bufferData;
         m_inputAssemblyBufferPool->InitBuffer(request);
 
-        m_triangleStreamBufferViews[0] =
-        {
+        m_triangleGeometryView.SetDrawArguments(RHI::DrawIndexed(0, 3, 0));
+
+        m_triangleGeometryView.SetIndexBufferView({
+            *m_triangleInputAssemblyBuffer,
+            offsetof(TriangleBufferData, m_indices),
+            sizeof(TriangleBufferData::m_indices),
+            RHI::IndexFormat::Uint16
+        });
+
+        m_triangleGeometryView.AddStreamBufferView({
             *m_triangleInputAssemblyBuffer,
             offsetof(TriangleBufferData, m_positions),
             sizeof(TriangleBufferData::m_positions),
             sizeof(VertexPosition)
-        };
+        });
 
-        m_triangleStreamBufferViews[1] =
-        {
+        m_triangleGeometryView.AddStreamBufferView({
             *m_triangleInputAssemblyBuffer,
             offsetof(TriangleBufferData, m_colors),
             sizeof(TriangleBufferData::m_colors),
             sizeof(VertexColor)
-        };
+        });
 
         RHI::InputStreamLayoutBuilder layoutBuilder;
         layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
         layoutBuilder.AddBuffer()->Channel("COLOR", RHI::Format::R32G32B32A32_FLOAT);
         m_triangleInputStreamLayout = layoutBuilder.End();
 
-        RHI::ValidateStreamBufferViews(m_triangleInputStreamLayout, m_triangleStreamBufferViews);
+        RHI::ValidateStreamBufferViews(m_triangleInputStreamLayout, m_triangleGeometryView, m_triangleGeometryView.GetFullStreamBufferIndices());
 
         m_triangleShader = LoadShader(TriangeShaderFilePath, SampleName);
         if (!m_triangleShader)
@@ -189,7 +196,7 @@ namespace AtomSampleViewer
         QuadBufferData bufferData;
         SetFullScreenRect(bufferData.m_positions.data(), bufferData.m_uvs.data(), bufferData.m_indices.data());
 
-        m_quadInputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
+        m_quadInputAssemblyBuffer = aznew RHI::Buffer();
 
         RHI::ResultCode result = RHI::ResultCode::Success;
         RHI::BufferInitRequest request;
@@ -205,26 +212,35 @@ namespace AtomSampleViewer
             return;
         }
 
-        m_quadStreamBufferViews[0] = {
+        m_quadGeometryView.SetDrawArguments(RHI::DrawIndexed(0, 6, 0));
+
+        m_quadGeometryView.SetIndexBufferView({
+            *m_quadInputAssemblyBuffer,
+            offsetof(QuadBufferData, m_indices),
+            sizeof(QuadBufferData::m_indices),
+            RHI::IndexFormat::Uint16
+        });
+
+        m_quadGeometryView.AddStreamBufferView({
             *m_quadInputAssemblyBuffer,
             offsetof(QuadBufferData, m_positions),
             sizeof(QuadBufferData::m_positions),
             sizeof(VertexPosition)
-        };
+        });
 
-        m_quadStreamBufferViews[1] = {
+        m_quadGeometryView.AddStreamBufferView({
             *m_quadInputAssemblyBuffer,
             offsetof(QuadBufferData, m_uvs),
             sizeof(QuadBufferData::m_uvs),
             sizeof(VertexUV)
-        };
+        });
 
         RHI::InputStreamLayoutBuilder layoutBuilder;
         layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
         layoutBuilder.AddBuffer()->Channel("UV", RHI::Format::R32G32_FLOAT);
         m_quadInputStreamLayout = layoutBuilder.End();
 
-        RHI::ValidateStreamBufferViews(m_quadInputStreamLayout, m_quadStreamBufferViews);
+        RHI::ValidateStreamBufferViews(m_quadInputStreamLayout, m_quadGeometryView, m_quadGeometryView.GetFullStreamBufferIndices());
 
         m_customMSAAResolveShader = LoadShader(CustomResolveShaderFilePath, SampleName);
         if (!m_customMSAAResolveShader)
@@ -339,30 +355,17 @@ namespace AtomSampleViewer
             commandList->SetViewports(&m_viewport, 1);
             commandList->SetScissors(&m_scissor, 1);
 
-            const RHI::IndexBufferView indexBufferView =
-            {
-                *m_triangleInputAssemblyBuffer,
-                offsetof(TriangleBufferData, m_indices),
-                sizeof(TriangleBufferData::m_indices),
-                RHI::IndexFormat::Uint16
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_triangleShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
             };
 
-            RHI::DrawIndexed drawIndexed;
-            drawIndexed.m_indexCount = 3;
-            drawIndexed.m_instanceCount = 1;
-
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_triangleShaderResourceGroup->GetRHIShaderResourceGroup() };
-
-            RHI::DrawItem drawItem;
-            drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_pipelineStates[msaaTypeIndex].get();
-            drawItem.m_indexBufferView = &indexBufferView;
+            // Submit the triangle draw item.
+            RHI::DeviceDrawItem drawItem;
+            drawItem.m_geometryView = m_triangleGeometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+            drawItem.m_streamIndices = m_triangleGeometryView.GetFullStreamBufferIndices();
+            drawItem.m_pipelineState = m_pipelineStates[msaaTypeIndex]->GetDevicePipelineState(context.GetDeviceIndex()).get();
             drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
             drawItem.m_shaderResourceGroups = shaderResourceGroups;
-            drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_triangleStreamBufferViews.size());
-            drawItem.m_streamBufferViews = m_triangleStreamBufferViews.data();
-
-            // Submit the triangle draw item.
             commandList->Submit(drawItem);
         };
 
@@ -421,7 +424,7 @@ namespace AtomSampleViewer
                 RHI::ImageScopeAttachmentDescriptor descriptor;
                 descriptor.m_attachmentId = m_sampleProperties[static_cast<uint32_t>(MSAAType::MSAA4X_Custom_Resolve)].m_attachmentId;
                 descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
-                frameGraph.UseShaderAttachment(descriptor, RHI::ScopeAttachmentAccess::Read);
+                frameGraph.UseShaderAttachment(descriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentStage::FragmentShader);
             }
 
             // Binds the swap chain as a color attachment.
@@ -439,7 +442,7 @@ namespace AtomSampleViewer
 
         const auto compileFunction = [this](const AZ::RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
         {
-            const AZ::RHI::ImageView* imageView = context.GetImageView(m_sampleProperties[static_cast<uint32_t>(MSAAType::MSAA4X_Custom_Resolve)].m_attachmentId);
+            const auto* imageView = context.GetImageView(m_sampleProperties[static_cast<uint32_t>(MSAAType::MSAA4X_Custom_Resolve)].m_attachmentId);
             m_customMSAAResolveShaderResourceGroup->SetImageView(m_customMSAAResolveTextureInputIndex, imageView);
             m_customMSAAResolveShaderResourceGroup->Compile();
         };
@@ -452,30 +455,18 @@ namespace AtomSampleViewer
             commandList->SetViewports(&m_viewport, 1);
             commandList->SetScissors(&m_scissor, 1);
 
-            const RHI::IndexBufferView indexBufferView =
-            {
-                *m_quadInputAssemblyBuffer,
-                offsetof(QuadBufferData, m_indices),
-                sizeof(QuadBufferData::m_indices),
-                RHI::IndexFormat::Uint16
-            };
-
-            RHI::DrawIndexed drawIndexed;
-            drawIndexed.m_indexCount = 6;
-            drawIndexed.m_instanceCount = 1;
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = { m_customMSAAResolveShaderResourceGroup
+                                                                                 ->GetRHIShaderResourceGroup()
+                                                                                 ->GetDeviceShaderResourceGroup(context.GetDeviceIndex())
+                                                                                 .get() };
 
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_customMSAAResolveShaderResourceGroup->GetRHIShaderResourceGroup() };
-
-            RHI::DrawItem drawItem;
-            drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_customResolveMSAAPipelineState.get();
-            drawItem.m_indexBufferView = &indexBufferView;
+            // Submit the quad draw item.
+            RHI::DeviceDrawItem drawItem;
+            drawItem.m_geometryView = m_quadGeometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+            drawItem.m_streamIndices = m_quadGeometryView.GetFullStreamBufferIndices();
+            drawItem.m_pipelineState = m_customResolveMSAAPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
             drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
             drawItem.m_shaderResourceGroups = shaderResourceGroups;
-            drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_quadStreamBufferViews.size());
-            drawItem.m_streamBufferViews = m_quadStreamBufferViews.data();
-
-            // Submit the triangle draw item.
             commandList->Submit(drawItem);
         };
 

+ 2 - 2
Gem/Code/Source/RHI/MSAAExampleComponent.h

@@ -133,8 +133,8 @@ namespace AtomSampleViewer
             AZStd::array<uint16_t, 6> m_indices;
         };
 
-        AZStd::array<AZ::RHI::StreamBufferView, 2> m_triangleStreamBufferViews;
-        AZStd::array<AZ::RHI::StreamBufferView, 2> m_quadStreamBufferViews;
+        AZ::RHI::GeometryView m_triangleGeometryView;
+        AZ::RHI::GeometryView m_quadGeometryView;
         AZ::RHI::InputStreamLayout m_triangleInputStreamLayout;
         AZ::RHI::InputStreamLayout m_quadInputStreamLayout;
 

+ 25 - 29
Gem/Code/Source/RHI/MatrixAlignmentTestExampleComponent.cpp

@@ -142,7 +142,7 @@ namespace AtomSampleViewer
         }
         // Always set the float first, this way if there are alignment issues We'll notice the unexpected
         // colors
-        bool success = false;
+        [[maybe_unused]] bool success = false;
 
         if (m_numFloatsAfterMatrix == 1)
         {
@@ -224,12 +224,12 @@ namespace AtomSampleViewer
         AZ::RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
 
         {
-            m_inputAssemblyBufferPool = RHI::Factory::Get().CreateBufferPool();
+            m_inputAssemblyBufferPool = aznew RHI::BufferPool();
 
             RHI::BufferPoolDescriptor bufferPoolDesc;
             bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
             bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-            m_inputAssemblyBufferPool->Init(*device, bufferPoolDesc);
+            m_inputAssemblyBufferPool->Init(bufferPoolDesc);
 
             BufferData bufferData;
 
@@ -241,7 +241,7 @@ namespace AtomSampleViewer
             SetVertexColor(bufferData.m_colors.data(), 2, 0.0, 0.0, 1.0, 1.0);
             SetVertexColor(bufferData.m_colors.data(), 3, 0.0, 0.0, 1.0, 1.0);
 
-            m_inputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
+            m_inputAssemblyBuffer = aznew RHI::Buffer();
 
             RHI::BufferInitRequest request;
             request.m_buffer = m_inputAssemblyBuffer.get();
@@ -249,28 +249,35 @@ namespace AtomSampleViewer
             request.m_initialData = &bufferData;
             m_inputAssemblyBufferPool->InitBuffer(request);
 
-            m_streamBufferViews[0] =
-            {
+            m_geometryView.SetDrawArguments(RHI::DrawIndexed(0, 6, 0));
+
+            m_geometryView.SetIndexBufferView({
+                *m_inputAssemblyBuffer,
+                offsetof(BufferData, m_indices),
+                sizeof(BufferData::m_indices),
+                RHI::IndexFormat::Uint16
+            });
+
+            m_geometryView.AddStreamBufferView({
                 *m_inputAssemblyBuffer,
                 offsetof(BufferData, m_positions),
                 sizeof(BufferData::m_positions),
                 sizeof(VertexPosition)
-            };
+            });
 
-            m_streamBufferViews[1] =
-            {
+            m_geometryView.AddStreamBufferView({
                 *m_inputAssemblyBuffer,
                 offsetof(BufferData, m_colors),
                 sizeof(BufferData::m_colors),
                 sizeof(VertexColor)
-            };
+            });
 
             RHI::InputStreamLayoutBuilder layoutBuilder;
             layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
             layoutBuilder.AddBuffer()->Channel("COLOR", RHI::Format::R32G32B32A32_FLOAT);
             pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
 
-            RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_streamBufferViews);
+            RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_geometryView, m_geometryView.GetFullStreamBufferIndices());
         }
 
         {
@@ -457,28 +464,17 @@ namespace AtomSampleViewer
                 commandList->SetViewports(&m_viewport, 1);
                 commandList->SetScissors(&m_scissor, 1);
 
-                const RHI::IndexBufferView indexBufferView =
-                {
-                    *m_inputAssemblyBuffer,
-                    offsetof(BufferData, m_indices),
-                    sizeof(BufferData::m_indices),
-                    RHI::IndexFormat::Uint16
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                    m_shaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
                 };
 
-                RHI::DrawIndexed drawIndexed;
-                drawIndexed.m_indexCount = 6;
-                drawIndexed.m_instanceCount = 2;
-
-                const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroup->GetRHIShaderResourceGroup() };
-
-                RHI::DrawItem drawItem;
-                drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_pipelineState.get();
-                drawItem.m_indexBufferView = &indexBufferView;
+                RHI::DeviceDrawItem drawItem;
+                drawItem.m_drawInstanceArgs = RHI::DrawInstanceArguments(2, 0);
+                drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+                drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
+                drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                 drawItem.m_shaderResourceGroups = shaderResourceGroups;
-                drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
-                drawItem.m_streamBufferViews = m_streamBufferViews.data();
 
                 // Submit the triangle draw item.
                 commandList->Submit(drawItem);

+ 1 - 3
Gem/Code/Source/RHI/MatrixAlignmentTestExampleComponent.h

@@ -14,7 +14,6 @@
 #include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
 
 #include <Atom/RHI/FrameScheduler.h>
-#include <Atom/RHI/DrawItem.h>
 #include <Atom/RHI/Device.h>
 #include <Atom/RHI/Factory.h>
 #include <Atom/RHI/PipelineState.h>
@@ -176,8 +175,7 @@ namespace AtomSampleViewer
             AZStd::array<uint16_t, 6> m_indices;
         };
 
-        AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
-        AZ::RHI::DrawItem m_drawItem;
+        AZ::RHI::GeometryView m_geometryView;
 
         // ImGui stuff.
         ImGuiSidebar m_imguiSidebar;

+ 710 - 0
Gem/Code/Source/RHI/MultiGPUExampleComponent.cpp

@@ -0,0 +1,710 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <RHI/MultiGPUExampleComponent.h>
+#include <Utils/Utils.h>
+
+#include <SampleComponentManager.h>
+
+#include <Atom/RHI/CommandList.h>
+#include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
+#include <Atom/RHI.Reflect/RenderAttachmentLayoutBuilder.h>
+#include <Atom/RPI.Public/Shader/Shader.h>
+#include <Atom/RHI.Reflect/ImageScopeAttachmentDescriptor.h>
+#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
+#include <AzCore/Serialization/SerializeContext.h>
+#include <Atom/RHI/DrawItem.h>
+#include <Atom/RHI/CopyItem.h>
+#include <Atom/RHI.Reflect/BufferDescriptor.h>
+
+using namespace AZ;
+
+namespace AtomSampleViewer
+{
+    void MultiGPUExampleComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<MultiGPUExampleComponent, AZ::Component>()
+                ->Version(0)
+                ;
+        }
+    }
+
+    void MultiGPUExampleComponent::OnFramePrepare(AZ::RHI::FrameGraphBuilder& frameGraphBuilder)
+    {
+        static float time = 0.0f;
+        time += 0.005f;
+
+        // Move the triangle around.
+        AZ::Vector3 translation(
+            sinf(time) * 0.25f,
+            cosf(time) * 0.25f,
+            0.0f);
+
+        if (m_shaderResourceGroupShared)
+        {
+            [[maybe_unused]] bool success =
+                m_shaderResourceGroupShared->SetConstant(m_objectMatrixConstantIndex, AZ::Matrix4x4::CreateTranslation(translation));
+            AZ_Warning("MultiGPUExampleComponent", success, "Failed to set SRG Constant m_objectMatrix");
+            m_shaderResourceGroupShared->Compile();
+        }
+
+        BasicRHIComponent::OnFramePrepare(frameGraphBuilder);
+    }
+
+    void MultiGPUExampleComponent::FrameBeginInternal(AZ::RHI::FrameGraphBuilder& frameGraphBuilder)
+    {
+        if (m_outputWidth != m_imageWidth || m_outputHeight != m_imageHeight)
+        {
+            // Image used as color attachment only on first device
+            {
+                m_images[0] = aznew RHI::Image;
+                RHI::ImageInitRequest initImageRequest;
+                initImageRequest.m_image = m_images[0].get();
+                initImageRequest.m_descriptor = RHI::ImageDescriptor::Create2D(
+                    RHI::ImageBindFlags::Color | RHI::ImageBindFlags::ShaderReadWrite, m_outputWidth, m_outputHeight, m_outputFormat);
+                initImageRequest.m_deviceMask = m_deviceMask_1;
+                m_imagePool->InitImage(initImageRequest);
+            }
+
+            // Image used as color attachment on both devices (rendered on device 1 and copied to device 0 for compositing)
+            {
+                m_images[1] = aznew RHI::Image;
+                RHI::ImageInitRequest initImageRequest;
+                initImageRequest.m_image = m_images[1].get();
+                initImageRequest.m_descriptor = RHI::ImageDescriptor::Create2D(
+                    RHI::ImageBindFlags::Color | RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead | RHI::ImageBindFlags::CopyWrite, m_outputWidth, m_outputHeight, m_outputFormat);
+                m_imagePool->InitImage(initImageRequest);
+            }
+
+            RHI::BufferBindFlags stagingBufferBindFlags{ RHI::BufferBindFlags::CopyWrite | RHI::BufferBindFlags::CopyRead };
+
+            {
+                m_stagingBufferToGPU = aznew RHI::Buffer;
+                AZStd::vector<unsigned int> initialData(m_outputWidth * m_outputHeight, 0xFFFF00FFu);
+
+                RHI::BufferInitRequest request;
+                request.m_buffer = m_stagingBufferToGPU.get();
+                request.m_descriptor = RHI::BufferDescriptor{stagingBufferBindFlags, initialData.size() * sizeof(unsigned int)};
+                request.m_initialData = initialData.data();
+
+                // This buffer is only necessary on device 0, but we test UpdateBufferDeviceMask below
+                request.m_deviceMask = RHI::MultiDevice::AllDevices;
+                if (m_stagingBufferPoolToGPU->InitBuffer(request) != RHI::ResultCode::Success)
+                {
+                    AZ_Error("MultiGPUExampleComponent", false, "StagingBufferToGPU was not created");
+                }
+
+                auto bufferViewDescriptor{ RHI::BufferViewDescriptor::CreateRaw(0, static_cast<u32>(request.m_descriptor.m_byteCount)) };
+                auto bufferView = m_stagingBufferToGPU->BuildBufferView(bufferViewDescriptor);
+                bufferView->GetDeviceBufferView(0);
+                bufferView->GetDeviceBufferView(1);
+
+                RHI::BufferDeviceMaskRequest updateRequest;
+                updateRequest.m_buffer = m_stagingBufferToGPU.get();
+                updateRequest.m_initialData = initialData.data();
+                updateRequest.m_deviceMask = m_deviceMask_1;
+                if (m_stagingBufferPoolToGPU->UpdateBufferDeviceMask(updateRequest) != RHI::ResultCode::Success)
+                {
+                    AZ_Error("MultiGPUExampleComponent", false, "StagingBufferToGPU was not created");
+                }
+
+                bufferView->GetDeviceBufferView(0);
+            }
+
+            {
+                m_stagingBufferToCPU = aznew RHI::Buffer;
+                RHI::BufferInitRequest request;
+                request.m_buffer = m_stagingBufferToCPU.get();
+                request.m_descriptor =
+                    RHI::BufferDescriptor{ stagingBufferBindFlags, m_outputWidth * m_outputHeight * sizeof(unsigned int) };
+                // This buffer is necessary on device 1, but we test UpdateBufferDeviceMask below
+                request.m_deviceMask = RHI::MultiDevice::NoDevices;
+                if (m_stagingBufferPoolToCPU->InitBuffer(request) != RHI::ResultCode::Success)
+                {
+                    AZ_Error("MultiGPUExampleComponent", false, "StagingBufferToCPU was not created");
+                }
+
+                RHI::BufferDeviceMaskRequest updateRequest;
+                updateRequest.m_buffer = m_stagingBufferToCPU.get();
+                updateRequest.m_deviceMask = m_deviceMask_2;
+                if (m_stagingBufferPoolToCPU->UpdateBufferDeviceMask(updateRequest) != RHI::ResultCode::Success)
+                {
+                    AZ_Error("MultiGPUExampleComponent", false, "StagingBufferToCPU was not created");
+                }
+            }
+
+            m_scissors[0].m_minX = 0;
+            m_scissors[0].m_minY = 0;
+            m_scissors[0].m_maxX = m_outputWidth / 2 + 1;
+            m_scissors[0].m_maxY = m_outputHeight;
+            m_scissors[1].m_minX = m_outputWidth / 2;
+            m_scissors[1].m_minY = 0;
+            m_scissors[1].m_maxX = m_outputWidth;
+            m_scissors[1].m_maxY = m_outputHeight;
+
+            m_imageWidth = m_outputWidth;
+            m_imageHeight = m_outputHeight;
+        }
+
+        frameGraphBuilder.GetAttachmentDatabase().ImportImage(
+            m_imageAttachmentIds[0], m_images[0]);
+
+        frameGraphBuilder.GetAttachmentDatabase().ImportImage(
+            m_imageAttachmentIds[1], m_images[1]);
+
+        frameGraphBuilder.GetAttachmentDatabase().ImportBuffer(
+            m_bufferAttachmentIds[0], m_stagingBufferToCPU);
+
+        frameGraphBuilder.GetAttachmentDatabase().ImportBuffer(
+            m_bufferAttachmentIds[1], m_stagingBufferToGPU);
+
+        RHI::DeviceBufferMapRequest request{};
+        request.m_buffer = m_stagingBufferToCPU->GetDeviceBuffer(1).get();
+        request.m_byteCount = m_imageWidth * m_imageHeight * sizeof(uint32_t);
+
+        RHI::DeviceBufferMapResponse response{};
+
+        m_stagingBufferPoolToCPU->GetDeviceBufferPool(1)->MapBuffer(request, response);
+
+        [[maybe_unused]] uint32_t* source = reinterpret_cast<uint32_t*>(response.m_data);
+
+        request.m_buffer = m_stagingBufferToGPU->GetDeviceBuffer(0).get();
+
+        m_stagingBufferPoolToGPU->GetDeviceBufferPool(0)->MapBuffer(request, response);
+
+        uint32_t* destination = reinterpret_cast<uint32_t*>(response.m_data);
+
+        memcpy(destination, source, request.m_byteCount);
+
+        m_stagingBufferPoolToCPU->GetDeviceBufferPool(1)->UnmapBuffer(*m_stagingBufferToCPU->GetDeviceBuffer(1));
+        m_stagingBufferPoolToGPU->GetDeviceBufferPool(0)->UnmapBuffer(*m_stagingBufferToGPU->GetDeviceBuffer(0));
+    }
+
+    MultiGPUExampleComponent::MultiGPUExampleComponent()
+    {
+        m_supportRHISamplePipeline = true;
+    }
+
+    void MultiGPUExampleComponent::Activate()
+    {
+        AZ_Error("MultiGPUExampleComponent", RHI::RHISystemInterface::Get()->GetDeviceCount() >= 2, "At least 2 devices required to run this sample");
+
+        m_device_1 = RHI::RHISystemInterface::Get()->GetDevice(0);
+        m_device_2 = RHI::RHISystemInterface::Get()->GetDevice(1);
+
+        m_deviceMask_1 = RHI::MultiDevice::DeviceMask{ 1u << 0 };
+        m_deviceMask_2 = RHI::MultiDevice::DeviceMask{ 1u << 1 };
+        m_deviceMask = m_deviceMask_1 | m_deviceMask_2;
+
+        // Create multi-device resources
+
+        RHI::ImageBindFlags imageBindFlags{ RHI::ImageBindFlags::Color | RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead | RHI::ImageBindFlags::CopyWrite };
+
+        // ImagePool for both devices
+        {
+            m_imagePool = aznew RHI::ImagePool;
+            m_imagePool->SetName(Name("MultiDeviceTexturePool"));
+
+            RHI::ImagePoolDescriptor imagePoolDescriptor{};
+            imagePoolDescriptor.m_bindFlags = imageBindFlags;
+            imagePoolDescriptor.m_deviceMask = m_deviceMask;
+
+            if (m_imagePool->Init(imagePoolDescriptor) != RHI::ResultCode::Success)
+            {
+                AZ_Error("MultiGPUExampleComponent", false, "Failed to initialize render texture image pool.");
+                return;
+            }
+        }
+
+        RHI::BufferBindFlags stagingBufferBindFlags{ RHI::BufferBindFlags::CopyWrite | RHI::BufferBindFlags::CopyRead };
+
+        // Create staging buffer pool for buffer copy to the GPU
+        {
+            m_stagingBufferPoolToGPU = aznew RHI::BufferPool;
+
+            RHI::BufferPoolDescriptor bufferPoolDesc;
+            bufferPoolDesc.m_bindFlags = stagingBufferBindFlags;
+            bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Host;
+            bufferPoolDesc.m_hostMemoryAccess = RHI::HostMemoryAccess::Write;
+            bufferPoolDesc.m_deviceMask = m_deviceMask;
+            if (m_stagingBufferPoolToGPU->Init(bufferPoolDesc) != RHI::ResultCode::Success)
+            {
+                AZ_Error("MultiGPUExampleComponent", false, "StagingBufferPool was not initialized");
+            }
+        }
+
+        // Create staging buffer pool for buffer copy to the CPU
+        {
+            m_stagingBufferPoolToCPU = aznew RHI::BufferPool;
+
+            RHI::BufferPoolDescriptor bufferPoolDesc;
+            bufferPoolDesc.m_bindFlags = stagingBufferBindFlags;
+            bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Host;
+            bufferPoolDesc.m_hostMemoryAccess = RHI::HostMemoryAccess::Read;
+            bufferPoolDesc.m_deviceMask = m_deviceMask;
+            if (m_stagingBufferPoolToCPU->Init(bufferPoolDesc) != RHI::ResultCode::Success)
+            {
+                AZ_Error("MultiGPUExampleComponent", false, "StagingBufferPoolToCPU was not initialized");
+            }
+        }
+
+        // Setup main and secondary pipeline
+        CreateRenderScopeProducer();
+        CreateCopyToCPUScopeProducer();
+        CreateCopyToGPUScopeProducer();
+        CreateCompositeScopeProducer();
+
+        RHI::RHISystemNotificationBus::Handler::BusConnect();
+    }
+
+    void MultiGPUExampleComponent::Deactivate()
+    {
+        m_inputAssemblyBuffer = nullptr;
+        m_inputAssemblyBufferPool = nullptr;
+        m_pipelineState = nullptr;
+        m_shaderResourceGroupShared = nullptr;
+
+        m_stagingBufferPoolToGPU = nullptr;
+        m_stagingBufferPoolToCPU = nullptr;
+        m_stagingBufferToGPU = nullptr;
+        m_stagingBufferToCPU = nullptr;
+        m_inputAssemblyBufferComposite = nullptr;
+        m_pipelineStateComposite = nullptr;
+        m_shaderResourceGroupComposite = nullptr;
+        m_shaderResourceGroupDataComposite = RHI::ShaderResourceGroupData{};
+        m_shaderResourceGroupPoolComposite = nullptr;
+
+        RHI::RHISystemNotificationBus::Handler::BusDisconnect();
+        m_windowContext = nullptr;
+        m_scopeProducers.clear();
+        m_secondaryScopeProducers.clear();
+    }
+
+    void MultiGPUExampleComponent::CreateRenderScopeProducer()
+    {
+        RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
+
+        {
+            m_inputAssemblyBufferPool = aznew RHI::BufferPool;
+
+            RHI::BufferPoolDescriptor bufferPoolDesc;
+            bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
+            bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
+            bufferPoolDesc.m_deviceMask = m_deviceMask;
+            m_inputAssemblyBufferPool->Init(bufferPoolDesc);
+
+            BufferDataTrianglePass bufferData;
+
+            SetVertexPosition(bufferData.m_positions.data(), 0,  0.0,  0.5, 0.0);
+            SetVertexPosition(bufferData.m_positions.data(), 1, -0.5, -0.5, 0.0);
+            SetVertexPosition(bufferData.m_positions.data(), 2,  0.5, -0.5, 0.0);
+
+            SetVertexColor(bufferData.m_colors.data(), 0, 1.0, 0.0, 0.0, 1.0);
+            SetVertexColor(bufferData.m_colors.data(), 1, 0.0, 1.0, 0.0, 1.0);
+            SetVertexColor(bufferData.m_colors.data(), 2, 0.0, 0.0, 1.0, 1.0);
+
+            SetVertexIndexIncreasing(bufferData.m_indices.data(), bufferData.m_indices.size());
+
+            m_inputAssemblyBuffer = aznew RHI::Buffer;
+
+            RHI::BufferInitRequest request;
+            request.m_buffer = m_inputAssemblyBuffer.get();
+            request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::InputAssembly, sizeof(bufferData) };
+            request.m_initialData = &bufferData;
+            m_inputAssemblyBufferPool->InitBuffer(request);
+
+            m_geometryView.SetDrawArguments(RHI::DrawIndexed(0, 3, 0));
+
+            m_geometryView.SetIndexBufferView({
+                *m_inputAssemblyBuffer,
+                offsetof(BufferDataTrianglePass, m_indices),
+                sizeof(BufferDataTrianglePass::m_indices),
+                RHI::IndexFormat::Uint16
+            });
+
+            m_geometryView.AddStreamBufferView({
+                *m_inputAssemblyBuffer,
+                offsetof(BufferDataTrianglePass, m_positions),
+                sizeof(BufferDataTrianglePass::m_positions),
+                sizeof(VertexPosition)
+            });
+
+            m_geometryView.AddStreamBufferView({
+                *m_inputAssemblyBuffer,
+                offsetof(BufferDataTrianglePass, m_colors),
+                sizeof(BufferDataTrianglePass::m_colors),
+                sizeof(VertexColor)
+            });
+
+            RHI::InputStreamLayoutBuilder layoutBuilder;
+            layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
+            layoutBuilder.AddBuffer()->Channel("COLOR", RHI::Format::R32G32B32A32_FLOAT);
+            pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
+
+            RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_geometryView, m_geometryView.GetFullStreamBufferIndices());
+        }
+
+        {
+            const char* triangleShaderFilePath = "Shaders/RHI/triangle.azshader";
+            const char* sampleName = "MultiGPUExample";
+
+            auto shader = LoadShader(triangleShaderFilePath, sampleName);
+            if (shader == nullptr)
+                return;
+
+            auto shaderOptionGroup = shader->CreateShaderOptionGroup();
+            shaderOptionGroup.SetUnspecifiedToDefaultValues();
+
+            // This is an example of how to set different shader options when searching for the shader variant you want to display
+            // Searching by id is simple, but suboptimal. Here it's only used to demonstrate the principle,
+            // but in practice the ShaderOptionIndex and the ShaderOptionValue should be cached for better performance
+            // You can also try DrawMode::Green, DrawMode::Blue or DrawMode::White. The specified color will appear on top of the triangle.
+            shaderOptionGroup.SetValue(AZ::Name("o_drawMode"),  AZ::Name("DrawMode::Red"));
+
+            auto shaderVariant = shader->GetVariant(shaderOptionGroup.GetShaderVariantId());
+
+            shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
+
+            RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
+            attachmentsBuilder.AddSubpass()
+                ->RenderTargetAttachment(m_outputFormat);
+            [[maybe_unused]] RHI::ResultCode result = attachmentsBuilder.End(pipelineStateDescriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
+            AZ_Assert(result == RHI::ResultCode::Success, "Failed to create render attachment layout");
+
+            m_pipelineState = shader->AcquirePipelineState(pipelineStateDescriptor);
+            if (!m_pipelineState)
+            {
+                AZ_Error(sampleName, false, "Failed to acquire default pipeline state for shader '%s'", triangleShaderFilePath);
+                return;
+            }
+
+            m_shaderResourceGroupShared = CreateShaderResourceGroup(shader, "TriangleInstanceSrg", sampleName);
+
+            const Name objectMatrixConstantId{ "m_objectMatrix" };
+            FindShaderInputIndex(&m_objectMatrixConstantIndex, m_shaderResourceGroupShared, objectMatrixConstantId, sampleName);
+
+            // In practice m_shaderResourceGroupShared should be one of the cached SRGs owned by the DrawItem
+            if (!shaderVariant.IsFullyBaked() && m_shaderResourceGroupShared->HasShaderVariantKeyFallbackEntry())
+            {
+                // Normally if the requested variant isn't an exact match we have to set it by SetShaderVariantKeyFallbackValue
+                // In most cases this should be the preferred behavior:
+                m_shaderResourceGroupShared->SetShaderVariantKeyFallbackValue(shaderOptionGroup.GetShaderVariantKeyFallbackValue());
+                AZ_Warning(
+                    sampleName, false, "Check the Triangle.shader file - some program variants haven't been baked ('%s')",
+                    triangleShaderFilePath);
+            }
+        }
+
+        // Creates two scopes for rendering the halves of the triangle.
+        {
+            struct ScopeData
+            {
+                bool second{false};
+            };
+
+            const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
+            {
+                // Binds the swap chain as a color attachment. Clears it to black.
+                RHI::ImageScopeAttachmentDescriptor descriptor;
+                descriptor.m_attachmentId = m_imageAttachmentIds[scopeData.second];
+                descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Clear;
+                descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::Store;
+                descriptor.m_loadStoreAction.m_clearValue.m_vector4Uint = {0, 0, 0, 0};
+                frameGraph.UseColorAttachment(descriptor);
+
+                // We will submit a single draw item.
+                frameGraph.SetEstimatedItemCount(1);
+            };
+
+            RHI::EmptyCompileFunction<ScopeData> compileFunction;
+
+            const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
+            {
+                RHI::CommandList* commandList = context.GetCommandList();
+
+                // Set persistent viewport and scissor state.
+                commandList->SetViewports(&m_viewport, 1);
+                commandList->SetScissors(&m_scissors[int(scopeData.second)], 1);
+
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                    m_shaderResourceGroupShared->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+                };
+
+                // Submit the triangle draw item.
+                RHI::DeviceDrawItem drawItem;
+                drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+                drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
+                drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
+                drawItem.m_shaderResourceGroups = shaderResourceGroups;
+                commandList->Submit(drawItem);
+            };
+
+            m_scopeProducers.emplace_back(
+                aznew
+                    RHI::ScopeProducerFunction<ScopeData, decltype(prepareFunction), decltype(compileFunction), decltype(executeFunction)>(
+                        RHI::ScopeId{ "MultiGPUTriangle0" }, ScopeData{}, prepareFunction, compileFunction, executeFunction, 0));
+
+            m_scopeProducers.emplace_back(
+                aznew
+                    RHI::ScopeProducerFunction<ScopeData, decltype(prepareFunction), decltype(compileFunction), decltype(executeFunction)>(
+                        RHI::ScopeId{ "MultiGPUTriangle1" }, ScopeData{true}, prepareFunction, compileFunction, executeFunction, 1));
+        }
+    }
+
+    void MultiGPUExampleComponent::CreateCompositeScopeProducer()
+    {
+        BufferDataCompositePass bufferData;
+        RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
+
+        // Setup input assembly for fullscreen pass
+        {
+            SetFullScreenRect(bufferData.m_positions.data(), bufferData.m_uvs.data(), bufferData.m_indices.data());
+
+            m_inputAssemblyBufferComposite = aznew RHI::Buffer;
+
+            RHI::BufferInitRequest request;
+            request.m_buffer = m_inputAssemblyBufferComposite.get();
+            request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::InputAssembly, sizeof(bufferData) };
+            request.m_initialData = &bufferData;
+            request.m_deviceMask = m_deviceMask_1;
+            m_inputAssemblyBufferPool->InitBuffer(request);
+
+            m_geometryViewComposite.SetDrawArguments(RHI::DrawIndexed(0, 6, 0));
+
+            m_geometryViewComposite.SetIndexBufferView({
+                *m_inputAssemblyBufferComposite,
+                offsetof(BufferDataCompositePass, m_indices),
+                sizeof(BufferDataCompositePass::m_indices),
+                RHI::IndexFormat::Uint16
+            });
+
+            m_geometryViewComposite.AddStreamBufferView({
+                *m_inputAssemblyBufferComposite,
+                offsetof(BufferDataCompositePass, m_positions),
+                sizeof(BufferDataCompositePass::m_positions),
+                sizeof(VertexPosition)
+            });
+
+            m_geometryViewComposite.AddStreamBufferView({
+                *m_inputAssemblyBufferComposite,
+                offsetof(BufferDataCompositePass, m_uvs),
+                sizeof(BufferDataCompositePass::m_uvs),
+                sizeof(VertexUV)
+            });
+
+            RHI::InputStreamLayoutBuilder layoutBuilder;
+            layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
+            layoutBuilder.AddBuffer()->Channel("UV", RHI::Format::R32G32_FLOAT);
+            pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
+
+            RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_geometryViewComposite, m_geometryViewComposite.GetFullStreamBufferIndices());
+        }
+
+        // Load shader and connect inputs
+        {
+            const char* compositeShaderFilePath = "Shaders/RHI/multigpucomposite.azshader";
+            const char* sampleName = "MultiGPUExample";
+
+            auto shader = LoadShader(compositeShaderFilePath, sampleName);
+            if (shader == nullptr)
+            {
+                AZ_Error("MultiGPUExampleComponent", false, "Could not load shader");
+                return;
+            }
+
+            auto shaderVariant = shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
+            shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
+
+            RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
+            attachmentsBuilder.AddSubpass()->RenderTargetAttachment(m_outputFormat);
+            [[maybe_unused]] RHI::ResultCode result =
+                attachmentsBuilder.End(pipelineStateDescriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
+            AZ_Assert(result == RHI::ResultCode::Success, "Failed to create render attachment layout");
+
+            m_pipelineStateComposite = shader->AcquirePipelineState(pipelineStateDescriptor);
+            if (!m_pipelineStateComposite)
+            {
+                AZ_Error(sampleName, false, "Failed to acquire default pipeline state for shader '%s'", compositeShaderFilePath);
+                return;
+            }
+
+            RHI::ShaderResourceGroupPoolDescriptor srgPoolDescriptor{};
+            srgPoolDescriptor.m_layout = shader->GetAsset()->FindShaderResourceGroupLayout(AZ::Name { "CompositeSrg" }, shader->GetSupervariantIndex()).get();
+            srgPoolDescriptor.m_deviceMask = m_deviceMask_1;
+
+            m_shaderResourceGroupPoolComposite = aznew RHI::ShaderResourceGroupPool;
+            m_shaderResourceGroupPoolComposite->Init(srgPoolDescriptor);
+
+            m_shaderResourceGroupComposite = aznew RHI::ShaderResourceGroup;
+            m_shaderResourceGroupPoolComposite->InitGroup(*m_shaderResourceGroupComposite);
+
+            m_shaderResourceGroupDataComposite = RHI::ShaderResourceGroupData{*m_shaderResourceGroupPoolComposite};
+
+            {
+                const AZ::Name inputTextureShaderInput{ "m_inputTextureLeft" };
+                m_textureInputIndices[0] = srgPoolDescriptor.m_layout->FindShaderInputImageIndex(inputTextureShaderInput);
+            }
+            {
+                const AZ::Name inputTextureShaderInput{ "m_inputTextureRight" };
+                m_textureInputIndices[1] = srgPoolDescriptor.m_layout->FindShaderInputImageIndex(inputTextureShaderInput);
+            }
+        }
+
+        // Setup ScopeProducer
+        {
+            struct ScopeData
+            {
+            };
+
+            const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
+            {
+                {
+                    RHI::ImageScopeAttachmentDescriptor descriptor{};
+                    descriptor.m_attachmentId = m_imageAttachmentIds[0];
+                    descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
+                    descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::DontCare;
+                    frameGraph.UseShaderAttachment(descriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentStage::FragmentShader);
+                }
+
+                {
+                    RHI::ImageScopeAttachmentDescriptor descriptor{};
+                    descriptor.m_attachmentId = m_imageAttachmentIds[1];
+                    descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
+                    descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::DontCare;
+                    frameGraph.UseShaderAttachment(descriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentStage::FragmentShader);
+                }
+
+                {
+                    RHI::ImageScopeAttachmentDescriptor desc{};
+                    desc.m_attachmentId = m_outputAttachmentId;
+                    frameGraph.UseColorAttachment(desc);
+                }
+
+                frameGraph.SetEstimatedItemCount(1);
+            };
+
+            const auto compileFunction = [this](const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
+            {
+                m_shaderResourceGroupDataComposite.SetImageView(m_textureInputIndices[0], context.GetImageView(m_imageAttachmentIds[0]));
+                m_shaderResourceGroupDataComposite.SetImageView(m_textureInputIndices[1], context.GetImageView(m_imageAttachmentIds[1]));
+
+                m_shaderResourceGroupComposite->Compile(m_shaderResourceGroupDataComposite);
+            };
+
+            const auto executeFunction = [=](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
+            {
+                RHI::CommandList* commandList = context.GetCommandList();
+
+                commandList->SetViewports(&m_viewport, 1);
+                commandList->SetScissors(&m_scissor, 1);
+
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                    m_shaderResourceGroupComposite->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+                };
+
+                RHI::DeviceDrawItem drawItem;
+                drawItem.m_geometryView = m_geometryViewComposite.GetDeviceGeometryView(context.GetDeviceIndex());
+                drawItem.m_streamIndices = m_geometryViewComposite.GetFullStreamBufferIndices();
+                drawItem.m_pipelineState = m_pipelineStateComposite->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
+                drawItem.m_shaderResourceGroups = shaderResourceGroups;
+                commandList->Submit(drawItem);
+            };
+
+            m_scopeProducers.emplace_back(
+                aznew
+                    RHI::ScopeProducerFunction<ScopeData, decltype(prepareFunction), decltype(compileFunction), decltype(executeFunction)>(
+                        RHI::ScopeId{ "MultiGPUComposite" }, ScopeData{}, prepareFunction, compileFunction, executeFunction));
+        }
+    }
+
+    void MultiGPUExampleComponent::CreateCopyToGPUScopeProducer()
+    {
+        struct ScopeData
+        {
+        };
+
+        const auto prepareFunction = [this]([[maybe_unused]] RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
+        {
+            {
+                RHI::ImageScopeAttachmentDescriptor descriptor{};
+                descriptor.m_attachmentId = m_imageAttachmentIds[1];
+                descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::DontCare;
+                descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::Store;
+                frameGraph.UseCopyAttachment(descriptor, RHI::ScopeAttachmentAccess::Write);
+            }
+        };
+
+        const auto compileFunction = []([[maybe_unused]] const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
+        {
+        };
+
+        const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
+        {
+            RHI::DeviceCopyBufferToImageDescriptor copyDescriptor{};
+            copyDescriptor.m_sourceBuffer = m_stagingBufferToGPU->GetDeviceBuffer(context.GetDeviceIndex()).get();
+            copyDescriptor.m_sourceOffset = 0;
+            copyDescriptor.m_sourceBytesPerRow = m_imageWidth * sizeof(uint32_t);
+            copyDescriptor.m_sourceBytesPerImage = static_cast<uint32_t>(m_stagingBufferToGPU->GetDescriptor().m_byteCount);
+            copyDescriptor.m_sourceSize = RHI::Size{ m_imageWidth, m_imageHeight, 1 };
+            copyDescriptor.m_sourceFormat = m_images[1]->GetDeviceImage(context.GetDeviceIndex())->GetDescriptor().m_format;
+            copyDescriptor.m_destinationImage = m_images[1]->GetDeviceImage(context.GetDeviceIndex()).get();
+
+            RHI::DeviceCopyItem copyItem(copyDescriptor);
+            context.GetCommandList()->Submit(copyItem);
+        };
+
+        m_scopeProducers.emplace_back(
+            aznew RHI::ScopeProducerFunction<ScopeData, decltype(prepareFunction), decltype(compileFunction), decltype(executeFunction)>(
+                RHI::ScopeId{ "MultiGPUCopyToGPU" }, ScopeData{}, prepareFunction, compileFunction, executeFunction));
+    }
+
+    void MultiGPUExampleComponent::CreateCopyToCPUScopeProducer()
+    {
+        struct ScopeData
+        {
+        };
+
+        const auto prepareFunction = [this]([[maybe_unused]] RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
+        {
+            {
+                RHI::ImageScopeAttachmentDescriptor descriptor{};
+                descriptor.m_attachmentId = m_imageAttachmentIds[1];
+                descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
+                descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::DontCare;
+                frameGraph.UseCopyAttachment(descriptor, RHI::ScopeAttachmentAccess::Read);
+            }
+        };
+
+        const auto compileFunction = []([[maybe_unused]] const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
+        {
+        };
+
+        const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
+        {
+            RHI::DeviceCopyImageToBufferDescriptor copyDescriptor{};
+            copyDescriptor.m_sourceImage = m_images[1]->GetDeviceImage(context.GetDeviceIndex()).get();
+            copyDescriptor.m_sourceSize = RHI::Size{ m_imageWidth, m_imageHeight, 1 };
+            copyDescriptor.m_destinationBuffer = m_stagingBufferToCPU->GetDeviceBuffer(context.GetDeviceIndex()).get();
+            copyDescriptor.m_destinationOffset = 0;
+            copyDescriptor.m_destinationBytesPerRow = m_imageWidth * sizeof(uint32_t);
+            copyDescriptor.m_destinationBytesPerImage = static_cast<uint32_t>(m_stagingBufferToCPU->GetDescriptor().m_byteCount);
+            copyDescriptor.m_destinationFormat = m_outputFormat;
+
+            RHI::DeviceCopyItem copyItem(copyDescriptor);
+            context.GetCommandList()->Submit(copyItem);
+        };
+
+        m_scopeProducers.emplace_back(
+            aznew RHI::ScopeProducerFunction<ScopeData, decltype(prepareFunction), decltype(compileFunction), decltype(executeFunction)>(
+                RHI::ScopeId{ "MultiGPUCopyToCPU" }, ScopeData{}, prepareFunction, compileFunction, executeFunction, 1));
+    }
+} // namespace AtomSampleViewer

+ 126 - 0
Gem/Code/Source/RHI/MultiGPUExampleComponent.h

@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/Component/Component.h>
+
+#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
+
+#include <Atom/RHI/FrameScheduler.h>
+#include <Atom/RHI/DrawItem.h>
+#include <Atom/RHI/Device.h>
+#include <Atom/RHI/Factory.h>
+#include <Atom/RHI/PipelineState.h>
+#include <Atom/RHI/BufferPool.h>
+#include <Atom/RHI/ImagePool.h>
+#include <Atom/RHI/Image.h>
+#include <Atom/RHI/ShaderResourceGroupPool.h>
+#include <Atom/RHI/CopyItem.h>
+
+#include <AzCore/Math/Matrix4x4.h>
+
+#include <RHI/BasicRHIComponent.h>
+
+namespace AtomSampleViewer
+{
+    // MultiGPU RHI example.
+    // Renders a rotating triangle to the screen similar to the TriangleExampleComponent, except, the left half of the screen is rendered by
+    // GPU 0 and the right half by GPU 1, which is then copied to GPU 0 to composite and show the final output on GPU 0.
+    // At least two devices need to be initialized (by passing "--device-count 2") to run this example.
+    class MultiGPUExampleComponent final
+        : public BasicRHIComponent
+    {
+    public:
+        AZ_COMPONENT(MultiGPUExampleComponent, "{BBA75A38-F111-4F52-AD5E-334B6DD58827}", AZ::Component);
+        AZ_DISABLE_COPY(MultiGPUExampleComponent);
+
+        static void Reflect(AZ::ReflectContext* context);
+
+        MultiGPUExampleComponent();
+        ~MultiGPUExampleComponent() override = default;
+
+    protected:
+
+        // AZ::Component
+        void Activate() override;
+        void Deactivate() override;
+        void FrameBeginInternal(AZ::RHI::FrameGraphBuilder& frameGraphBuilder) override;
+
+        // RHISystemNotificationBus::Handler
+        void OnFramePrepare(AZ::RHI::FrameGraphBuilder& frameGraphBuilder) override;
+
+    private:
+        /////////////////////////////////////////////////////////////////////////
+        //! Shared Resources
+
+        void CreateRenderScopeProducer();
+
+        AZ::RHI::MultiDevice::DeviceMask m_deviceMask;
+        AZ::RHI::Ptr<AZ::RHI::BufferPool> m_inputAssemblyBufferPool;
+        AZ::RHI::Ptr<AZ::RHI::Buffer> m_inputAssemblyBuffer;
+
+        AZ::RHI::ConstPtr<AZ::RHI::PipelineState> m_pipelineState;
+        AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> m_shaderResourceGroupShared;
+        AZ::RHI::ShaderInputConstantIndex m_objectMatrixConstantIndex;
+
+        struct BufferDataTrianglePass
+        {
+            AZStd::array<VertexPosition, 3> m_positions;
+            AZStd::array<VertexColor, 3> m_colors;
+            AZStd::array<uint16_t, 3> m_indices;
+        };
+
+        AZ::RHI::GeometryView m_geometryView;
+        AZ::RHI::Ptr<AZ::RHI::ImagePool> m_imagePool{};
+        AZStd::array<AZ::RHI::Ptr<AZ::RHI::Image>, 2> m_images;
+        AZStd::array<AZ::RHI::AttachmentId, 2> m_imageAttachmentIds = { { AZ::RHI::AttachmentId("MultiGPURenderTexture1"),
+                                                                          AZ::RHI::AttachmentId("MultiGPURenderTexture2") } };
+        AZStd::array<AZ::RHI::AttachmentId, 2> m_bufferAttachmentIds = { { AZ::RHI::AttachmentId("MultiGPUBufferToCPU"),
+                                                                           AZ::RHI::AttachmentId("MultiGPUBufferToGPU") } };
+        uint32_t m_imageWidth{0};
+        uint32_t m_imageHeight{0};
+
+        /////////////////////////////////////////////////////////////////////////
+        //! First device methods and members
+
+        void CreateCopyToGPUScopeProducer();
+        void CreateCopyToCPUScopeProducer();
+        void CreateCompositeScopeProducer();
+
+        struct BufferDataCompositePass
+        {
+            AZStd::array<VertexPosition, 4> m_positions;
+            AZStd::array<VertexUV, 4> m_uvs;
+            AZStd::array<uint16_t, 6> m_indices;
+        };
+
+        AZStd::array<AZ::RHI::ShaderInputImageIndex, 2> m_textureInputIndices;
+
+        AZ::RHI::Ptr<AZ::RHI::Device> m_device_1{};
+        AZ::RHI::MultiDevice::DeviceMask m_deviceMask_1{};
+        AZ::RHI::Ptr<AZ::RHI::BufferPool> m_stagingBufferPoolToGPU{};
+        AZ::RHI::Ptr<AZ::RHI::Buffer> m_stagingBufferToGPU{};
+        AZ::RHI::Ptr<AZ::RHI::Buffer> m_inputAssemblyBufferComposite{};
+        AZ::RHI::GeometryView m_geometryViewComposite;
+        AZ::RHI::ConstPtr<AZ::RHI::PipelineState> m_pipelineStateComposite;
+        AZ::RHI::Ptr<AZ::RHI::ShaderResourceGroupPool> m_shaderResourceGroupPoolComposite;
+        AZ::RHI::Ptr<AZ::RHI::ShaderResourceGroup> m_shaderResourceGroupComposite;
+        AZ::RHI::ShaderResourceGroupData m_shaderResourceGroupDataComposite;
+        AZStd::array<AZ::RHI::Scissor, 2> m_scissors{};
+
+        /////////////////////////////////////////////////////////////////////////
+        //! Second device methods and members
+
+        AZ::RHI::Ptr<AZ::RHI::Device> m_device_2{};
+        AZ::RHI::MultiDevice::DeviceMask m_deviceMask_2{};
+        AZ::RHI::Ptr<AZ::RHI::BufferPool> m_stagingBufferPoolToCPU{};
+        AZ::RHI::Ptr<AZ::RHI::Buffer> m_stagingBufferToCPU{};
+        AZStd::vector<AZStd::shared_ptr<AZ::RHI::ScopeProducer>> m_secondaryScopeProducers;
+    };
+} // namespace AtomSampleViewer

+ 23 - 27
Gem/Code/Source/RHI/MultiThreadComponent.cpp

@@ -149,11 +149,11 @@ namespace AtomSampleViewer
         const AZ::RHI::Ptr<AZ::RHI::Device> device = Utils::GetRHIDevice();
         AZ::RHI::ResultCode result = AZ::RHI::ResultCode::Success;
 
-        m_bufferPool = AZ::RHI::Factory::Get().CreateBufferPool();
+        m_bufferPool = aznew AZ::RHI::BufferPool();
         AZ::RHI::BufferPoolDescriptor bufferPoolDesc;
         bufferPoolDesc.m_bindFlags = AZ::RHI::BufferBindFlags::InputAssembly;
         bufferPoolDesc.m_heapMemoryLevel = AZ::RHI::HeapMemoryLevel::Device;
-        result = m_bufferPool->Init(*device, bufferPoolDesc);
+        result = m_bufferPool->Init(bufferPoolDesc);
         if (result != AZ::RHI::ResultCode::Success)
         {
             AZ_Error("MultiThreadComponent", false, "Failed to initialize buffer pool with error code %d", result);
@@ -162,7 +162,7 @@ namespace AtomSampleViewer
 
         SingleCubeBufferData bufferData = CreateSingleCubeBufferData(AZ::Vector4(1.0f, 0.0f, 0.0f, 0.0f));
 
-        m_inputAssemblyBuffer = AZ::RHI::Factory::Get().CreateBuffer();
+        m_inputAssemblyBuffer = aznew AZ::RHI::Buffer();
         AZ::RHI::BufferInitRequest request;
 
         request.m_buffer = m_inputAssemblyBuffer.get();
@@ -175,29 +175,28 @@ namespace AtomSampleViewer
             return;
         }
 
-        m_streamBufferViews[0] =
-        {
+        m_geometryView.SetDrawArguments(AZ::RHI::DrawIndexed(0, s_geometryIndexCount, 0));
+
+        m_geometryView.AddStreamBufferView({
             *m_inputAssemblyBuffer,
             offsetof(SingleCubeBufferData, m_positions),
             sizeof(SingleCubeBufferData::m_positions),
             sizeof(VertexPosition)
-        };
+        });
 
-        m_streamBufferViews[1] =
-        {
+        m_geometryView.AddStreamBufferView({
             *m_inputAssemblyBuffer,
             offsetof(SingleCubeBufferData, m_colors),
             sizeof(SingleCubeBufferData::m_colors),
             sizeof(VertexColor)
-        };
+        });
 
-        m_indexBufferView =
-        {
+        m_geometryView.SetIndexBufferView({
             *m_inputAssemblyBuffer,
             offsetof(SingleCubeBufferData, m_indices),
             sizeof(SingleCubeBufferData::m_indices),
             AZ::RHI::IndexFormat::Uint16
-        };
+        });
 
         AZ::RHI::InputStreamLayoutBuilder layoutBuilder;
         layoutBuilder.SetTopology(AZ::RHI::PrimitiveTopology::TriangleList);
@@ -206,7 +205,7 @@ namespace AtomSampleViewer
         m_streamLayoutDescriptor.Clear();
         m_streamLayoutDescriptor = layoutBuilder.End();
 
-        AZ::RHI::ValidateStreamBufferViews(m_streamLayoutDescriptor, m_streamBufferViews);
+        AZ::RHI::ValidateStreamBufferViews(m_streamLayoutDescriptor, m_geometryView, m_geometryView.GetFullStreamBufferIndices());
     }
 
     void MultiThreadComponent::CreatePipeline()
@@ -284,7 +283,9 @@ namespace AtomSampleViewer
                 dsDesc.m_imageViewDescriptor.m_overrideFormat = device->GetNearestSupportedFormat(AZ::RHI::Format::D24_UNORM_S8_UINT, AZ::RHI::FormatCapabilities::DepthStencil);
                 dsDesc.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateDepthStencil(1.0f, 0);
                 dsDesc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Clear;
-                frameGraph.UseDepthStencilAttachment(dsDesc, AZ::RHI::ScopeAttachmentAccess::Write);
+                frameGraph.UseDepthStencilAttachment(
+                    dsDesc, AZ::RHI::ScopeAttachmentAccess::Write,
+                    AZ::RHI::ScopeAttachmentStage::EarlyFragmentTest | AZ::RHI::ScopeAttachmentStage::LateFragmentTest);
             }
 
             // We will submit s_numberOfCubes draw items.
@@ -312,10 +313,6 @@ namespace AtomSampleViewer
             commandList->SetViewports(&m_viewport, 1);
             commandList->SetScissors(&m_scissor, 1);
 
-            AZ::RHI::DrawIndexed drawIndexed;
-            drawIndexed.m_indexCount = s_geometryIndexCount;
-            drawIndexed.m_instanceCount = 1;
-
             if (context.GetCommandListIndex() == context.GetCommandListCount() - 1)
             {
 #if defined(AZ_DEBUG_BUILD)
@@ -326,17 +323,16 @@ namespace AtomSampleViewer
             
             for (uint32_t i = context.GetSubmitRange().m_startIndex; i < context.GetSubmitRange().m_endIndex; ++i)
             {
-                const AZ::RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[i]->GetRHIShaderResourceGroup() };
-
-                AZ::RHI::DrawItem drawItem;
-                drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_pipelineState.get();
-                drawItem.m_indexBufferView = &m_indexBufferView;
+                const AZ::RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                    m_shaderResourceGroups[i]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+                };
+
+                AZ::RHI::DeviceDrawItem drawItem;
+                drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+                drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
+                drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(AZ::RHI::ArraySize(shaderResourceGroups));
                 drawItem.m_shaderResourceGroups = shaderResourceGroups;
-                drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
-                drawItem.m_streamBufferViews = m_streamBufferViews.data();
-
                 commandList->Submit(drawItem, i);
             }
         };

+ 1 - 2
Gem/Code/Source/RHI/MultiThreadComponent.h

@@ -86,7 +86,6 @@ namespace AtomSampleViewer
         AZ::RHI::ShaderInputConstantIndex m_shaderIndexViewProj;
 
         AZ::RHI::AttachmentId m_depthStencilID;
-        AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
-        AZ::RHI::IndexBufferView m_indexBufferView;
+        AZ::RHI::GeometryView m_geometryView;
     };
 } // namespace AtomSampleViewer

+ 38 - 44
Gem/Code/Source/RHI/MultipleViewsComponent.cpp

@@ -235,15 +235,15 @@ namespace AtomSampleViewer
 
         const AZ::RHI::Ptr<AZ::RHI::Device> device = Utils::GetRHIDevice();
 
-        m_bufferPool = AZ::RHI::Factory::Get().CreateBufferPool();
+        m_bufferPool = aznew AZ::RHI::BufferPool();
         AZ::RHI::BufferPoolDescriptor bufferPoolDesc;
         bufferPoolDesc.m_bindFlags = AZ::RHI::BufferBindFlags::InputAssembly;
         bufferPoolDesc.m_heapMemoryLevel = AZ::RHI::HeapMemoryLevel::Device;
-        m_bufferPool->Init(*device, bufferPoolDesc);
+        m_bufferPool->Init(bufferPoolDesc);
 
         BufferData bufferData = CreateBufferData();
 
-        m_inputAssemblyBuffer = AZ::RHI::Factory::Get().CreateBuffer();
+        m_inputAssemblyBuffer = aznew AZ::RHI::Buffer();
         AZ::RHI::ResultCode result = AZ::RHI::ResultCode::Success;
         AZ::RHI::BufferInitRequest request;
 
@@ -257,37 +257,35 @@ namespace AtomSampleViewer
             return;
         }
 
-        m_streamBufferViews[0] =
-        {
+        m_geometryView.SetDrawArguments(RHI::DrawIndexed(0, geometryIndexCount, 0));
+
+        m_geometryView.AddStreamBufferView({
             *m_inputAssemblyBuffer,
             offsetof(BufferData, m_positions),
             sizeof(BufferData::m_positions),
             sizeof(VertexPosition)
-        };
+        });
 
-        m_streamBufferViews[1] =
-        {
+        m_geometryView.AddStreamBufferView({
             *m_inputAssemblyBuffer,
             offsetof(BufferData, m_colors),
             sizeof(BufferData::m_colors),
             sizeof(VertexColor)
-        };
+        });
 
-        m_streamBufferViews[2] =
-        {
+        m_geometryView.AddStreamBufferView({
             *m_inputAssemblyBuffer,
             offsetof(BufferData, m_normals),
             sizeof(BufferData::m_normals),
             sizeof(VertexNormal)
-        };
+        });
 
-        m_indexBufferView =
-        {
+        m_geometryView.SetIndexBufferView({
             *m_inputAssemblyBuffer,
             offsetof(BufferData, m_indices),
             sizeof(BufferData::m_indices),
             AZ::RHI::IndexFormat::Uint16
-        };
+        });
 
         RHI::InputStreamLayoutBuilder layoutBuilder;
         layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
@@ -295,7 +293,7 @@ namespace AtomSampleViewer
         layoutBuilder.AddBuffer()->Channel("NORMAL", RHI::Format::R32G32B32_FLOAT);
         m_inputStreamLayout = layoutBuilder.End();
 
-        AZ::RHI::ValidateStreamBufferViews(m_inputStreamLayout, m_streamBufferViews);
+        AZ::RHI::ValidateStreamBufferViews(m_inputStreamLayout, m_geometryView, m_geometryView.GetFullStreamBufferIndices());
     }
 
     void MultipleViewsComponent::InitRenderTarget()
@@ -399,7 +397,9 @@ namespace AtomSampleViewer
                 dsDesc.m_imageViewDescriptor.m_overrideFormat = AZ::RHI::Format::D32_FLOAT;
                 dsDesc.m_loadStoreAction.m_clearValue = m_depthClearValue;
                 dsDesc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Clear;
-                frameGraph.UseDepthStencilAttachment(dsDesc, AZ::RHI::ScopeAttachmentAccess::ReadWrite);
+                frameGraph.UseDepthStencilAttachment(
+                    dsDesc, AZ::RHI::ScopeAttachmentAccess::ReadWrite,
+                    AZ::RHI::ScopeAttachmentStage::EarlyFragmentTest | AZ::RHI::ScopeAttachmentStage::LateFragmentTest);
             }
 
             frameGraph.SetEstimatedItemCount(1);
@@ -416,21 +416,16 @@ namespace AtomSampleViewer
             commandList->SetViewports(&viewport, 1);
             commandList->SetScissors(&scissor, 1);
 
-            AZ::RHI::DrawIndexed drawIndexed;
-            drawIndexed.m_indexCount = geometryIndexCount;
-            drawIndexed.m_instanceCount = 1;
-
-            const AZ::RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[0]->GetRHIShaderResourceGroup() };
+            const AZ::RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_shaderResourceGroups[0]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+            };
 
-            AZ::RHI::DrawItem drawItem;
-            drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_pipelineStates[0].get();
-            drawItem.m_indexBufferView = &m_indexBufferView;
+            AZ::RHI::DeviceDrawItem drawItem;
+            drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+            drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
+            drawItem.m_pipelineState = m_pipelineStates[0]->GetDevicePipelineState(context.GetDeviceIndex()).get();
             drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(AZ::RHI::ArraySize(shaderResourceGroups));
             drawItem.m_shaderResourceGroups = shaderResourceGroups;
-            drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
-            drawItem.m_streamBufferViews = m_streamBufferViews.data();
-
             commandList->Submit(drawItem);
         };
 
@@ -460,7 +455,9 @@ namespace AtomSampleViewer
 
             // Binds depth buffer from depth pass
             {
-                frameGraph.UseShaderAttachment(AZ::RHI::ImageScopeAttachmentDescriptor(m_transientImageDescriptor.m_attachmentId), AZ::RHI::ScopeAttachmentAccess::Read);
+                frameGraph.UseShaderAttachment(
+                    AZ::RHI::ImageScopeAttachmentDescriptor(m_transientImageDescriptor.m_attachmentId),
+                    AZ::RHI::ScopeAttachmentAccess::Read, AZ::RHI::ScopeAttachmentStage::FragmentShader);
             }
 
             // Create & Binds DepthStencil image
@@ -482,7 +479,9 @@ namespace AtomSampleViewer
                 dsDesc.m_imageViewDescriptor.m_overrideFormat = depthStencilFormat;
                 dsDesc.m_loadStoreAction.m_clearValue = m_depthClearValue;
                 dsDesc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Clear;
-                frameGraph.UseDepthStencilAttachment(dsDesc, AZ::RHI::ScopeAttachmentAccess::Write);
+                frameGraph.UseDepthStencilAttachment(
+                    dsDesc, AZ::RHI::ScopeAttachmentAccess::Write,
+                    AZ::RHI::ScopeAttachmentStage::EarlyFragmentTest | AZ::RHI::ScopeAttachmentStage::LateFragmentTest);
             }
 
             // We will submit a single draw item.
@@ -491,7 +490,7 @@ namespace AtomSampleViewer
 
         const auto compileFunctionMainView = [this](const AZ::RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
         {
-            const AZ::RHI::ImageView* imageView = context.GetImageView(m_transientImageDescriptor.m_attachmentId);
+            const auto* imageView = context.GetImageView(m_transientImageDescriptor.m_attachmentId);
 
             m_shaderResourceGroups[1]->SetImageView(m_shaderInputImageIndex, imageView);
             m_shaderResourceGroups[1]->Compile();
@@ -505,21 +504,16 @@ namespace AtomSampleViewer
             commandList->SetViewports(&m_viewport, 1);
             commandList->SetScissors(&m_scissor, 1);
 
-            AZ::RHI::DrawIndexed drawIndexed;
-            drawIndexed.m_indexCount = geometryIndexCount;
-            drawIndexed.m_instanceCount = 1;
-
-            const AZ::RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[1]->GetRHIShaderResourceGroup() };
+            const AZ::RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_shaderResourceGroups[1]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+            };
 
-            AZ::RHI::DrawItem drawItem;
-            drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_pipelineStates[1].get();
-            drawItem.m_indexBufferView = &m_indexBufferView;
+            AZ::RHI::DeviceDrawItem drawItem;
+            drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+            drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
+            drawItem.m_pipelineState = m_pipelineStates[1]->GetDevicePipelineState(context.GetDeviceIndex()).get();
             drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(AZ::RHI::ArraySize(shaderResourceGroups));
             drawItem.m_shaderResourceGroups = shaderResourceGroups;
-            drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
-            drawItem.m_streamBufferViews = m_streamBufferViews.data();
-
             commandList->Submit(drawItem);
         };
 

+ 1 - 4
Gem/Code/Source/RHI/MultipleViewsComponent.h

@@ -12,7 +12,6 @@
 
 #include <Atom/RHI/Buffer.h>
 #include <Atom/RHI/BufferPool.h>
-#include <Atom/RHI/DrawItem.h>
 #include <Atom/RHI/PipelineState.h>
 #include <Atom/RPI.Public/Image/AttachmentImage.h>
 
@@ -100,12 +99,10 @@ namespace AtomSampleViewer
         AZStd::array<AZ::RHI::ShaderInputConstantIndex, 6> m_shaderInputConstantIndices;
         AZ::RHI::ShaderInputImageIndex m_shaderInputImageIndex;
 
-        AZStd::array<AZ::RHI::StreamBufferView, 3> m_streamBufferViews;
-        AZ::RHI::IndexBufferView m_indexBufferView;
+        AZ::RHI::GeometryView m_geometryView;
         AZ::RHI::AttachmentId m_depthMapID;
         AZ::RHI::AttachmentId m_depthStencilID;
         AZ::RHI::ClearValue m_depthClearValue;
-        AZ::RHI::DrawItem m_drawItem;
 
         AZ::RHI::TransientImageDescriptor m_transientImageDescriptor;
     };

+ 51 - 50
Gem/Code/Source/RHI/QueryExampleComponent.cpp

@@ -113,7 +113,7 @@ namespace AtomSampleViewer
 
         {
             auto scaleMatrix = AZ::Matrix4x4::CreateScale(AZ::Vector3(0.2, 0.2, 0.2));
-            bool success = m_shaderResourceGroups[0]->SetConstant(m_objectMatrixConstantIndex, scaleMatrix * AZ::Matrix4x4::CreateTranslation(AZ::Vector3(0, 0, 0.5)));
+            [[maybe_unused]] bool success = m_shaderResourceGroups[0]->SetConstant(m_objectMatrixConstantIndex, scaleMatrix * AZ::Matrix4x4::CreateTranslation(AZ::Vector3(0, 0, 0.5)));
             success &= m_shaderResourceGroups[0]->SetConstant(m_colorConstantIndex, AZ::Vector4(1.0, 0, 0, 1.0));
             AZ_Warning(QueryExample::SampleName, success, "Failed to set SRG Constant data");
         }
@@ -123,7 +123,7 @@ namespace AtomSampleViewer
                 sinf(m_elapsedTime),
                 0.0f,
                 0.0f);
-            bool success = m_shaderResourceGroups[1]->SetConstant(m_objectMatrixConstantIndex, scaleMatrix * AZ::Matrix4x4::CreateTranslation(translation));
+            [[maybe_unused]] bool success = m_shaderResourceGroups[1]->SetConstant(m_objectMatrixConstantIndex, scaleMatrix * AZ::Matrix4x4::CreateTranslation(translation));
             success &= m_shaderResourceGroups[1]->SetConstant(m_colorConstantIndex, AZ::Color(1.f, 1.f, 1.f, 0.5f).GetAsVector4());
             AZ_Warning(QueryExample::SampleName, success, "Failed to set SRG Constant data");
         }
@@ -154,19 +154,19 @@ namespace AtomSampleViewer
     {
         using namespace AZ;
         RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
-        m_inputAssemblyBufferPool = RHI::Factory::Get().CreateBufferPool();
+        m_inputAssemblyBufferPool = aznew RHI::BufferPool();
 
         RHI::BufferPoolDescriptor bufferPoolDesc;
         bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
         bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-        m_inputAssemblyBufferPool->Init(*device, bufferPoolDesc);
+        m_inputAssemblyBufferPool->Init(bufferPoolDesc);
 
         {
             // Create quad buffer and views
             BufferData bufferData;
             SetFullScreenRect(bufferData.m_positions.data(), nullptr, bufferData.m_indices.data());
 
-            m_quadInputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
+            m_quadInputAssemblyBuffer = aznew RHI::Buffer();
 
             RHI::ResultCode result = RHI::ResultCode::Success;
             RHI::BufferInitRequest request;
@@ -181,18 +181,27 @@ namespace AtomSampleViewer
                 return;
             }
 
-            m_quadStreamBufferView = {
+            m_geometryView.SetDrawArguments(RHI::DrawIndexed(0, 6, 0));
+
+            m_geometryView.SetIndexBufferView({
+                *m_quadInputAssemblyBuffer,
+                offsetof(BufferData, m_indices),
+                sizeof(BufferData::m_indices),
+                RHI::IndexFormat::Uint16
+            });
+
+            m_geometryView.AddStreamBufferView({
                 *m_quadInputAssemblyBuffer,
                 offsetof(BufferData, m_positions),
                 sizeof(BufferData::m_positions),
                 sizeof(VertexPosition)
-            };
+            });
 
             RHI::InputStreamLayoutBuilder layoutBuilder;
             layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
             m_quadInputStreamLayout = layoutBuilder.End();
 
-            RHI::ValidateStreamBufferViews(m_quadInputStreamLayout, AZStd::span<const RHI::StreamBufferView>(&m_quadStreamBufferView, 1));
+            RHI::ValidateStreamBufferViews(m_quadInputStreamLayout, m_geometryView, m_geometryView.GetFullStreamBufferIndices());
         }
     }
 
@@ -304,16 +313,16 @@ namespace AtomSampleViewer
             {
                 return;
             }
-            
-            RHI::CopyQueryToBufferDescriptor descriptor;
-            descriptor.m_sourceQueryPool = m_occlusionQueryPool.get();
-            descriptor.m_firstQuery = m_occlusionQueries[m_currentOcclusionQueryIndex].m_query->GetHandle();
+
+            RHI::DeviceCopyQueryToBufferDescriptor descriptor;
+            descriptor.m_sourceQueryPool = m_occlusionQueryPool->GetDeviceQueryPool(context.GetDeviceIndex()).get();
+            descriptor.m_firstQuery = m_occlusionQueries[m_currentOcclusionQueryIndex].m_query->GetHandle(context.GetDeviceIndex());
             descriptor.m_queryCount = 1;
-            descriptor.m_destinationBuffer = m_predicationBuffer.get();
+            descriptor.m_destinationBuffer = m_predicationBuffer->GetDeviceBuffer(context.GetDeviceIndex()).get();
             descriptor.m_destinationOffset = 0;
             descriptor.m_destinationStride = sizeof(uint64_t);
 
-            RHI::CopyItem copyItem(descriptor);
+            RHI::DeviceCopyItem copyItem(descriptor);
 
             context.GetCommandList()->Submit(copyItem);
         };
@@ -370,7 +379,9 @@ namespace AtomSampleViewer
                 dsDesc.m_imageViewDescriptor.m_overrideFormat = AZ::RHI::Format::D32_FLOAT;
                 dsDesc.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateDepth(1.f);
                 dsDesc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Clear;
-                frameGraph.UseDepthStencilAttachment(dsDesc, AZ::RHI::ScopeAttachmentAccess::Write);
+                frameGraph.UseDepthStencilAttachment(
+                    dsDesc, AZ::RHI::ScopeAttachmentAccess::Write,
+                    AZ::RHI::ScopeAttachmentStage::EarlyFragmentTest | AZ::RHI::ScopeAttachmentStage::LateFragmentTest);
             }
 
             // Query pools
@@ -403,7 +414,9 @@ namespace AtomSampleViewer
             // Add the predication buffer if necessary
             if (m_currentType == QueryType::Predication)
             {
-                frameGraph.UseAttachment(m_predicationBufferAttachmentDescriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentUsage::Predication);
+                frameGraph.UseAttachment(
+                    m_predicationBufferAttachmentDescriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentUsage::Predication,
+                    RHI::ScopeAttachmentStage::Predication);
             }
 
             // We will submit a single draw item.
@@ -428,26 +441,14 @@ namespace AtomSampleViewer
             commandList->SetScissors(&m_scissor, 1);
 
             {
-                const RHI::IndexBufferView quadIndexBufferView =
-                {
-                    *m_quadInputAssemblyBuffer,
-                    offsetof(BufferData, m_indices),
-                    sizeof(BufferData::m_indices),
-                    RHI::IndexFormat::Uint16
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                    m_shaderResourceGroups[0]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
                 };
 
-                RHI::DrawIndexed drawIndexed;
-                drawIndexed.m_indexCount = 6;
-                drawIndexed.m_instanceCount = 1;
-
-                const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[0]->GetRHIShaderResourceGroup() };
-
-                RHI::DrawItem drawItem;
-                drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_quadPipelineState.get();
-                drawItem.m_indexBufferView = &quadIndexBufferView;
-                drawItem.m_streamBufferViewCount = 1;
-                drawItem.m_streamBufferViews = &m_quadStreamBufferView;
+                RHI::DeviceDrawItem drawItem;
+                drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+                drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
+                drawItem.m_pipelineState = m_quadPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                 drawItem.m_shaderResourceGroups = shaderResourceGroups;
 
@@ -455,13 +456,13 @@ namespace AtomSampleViewer
                 {
                     if (m_timestampEnabled)
                     {
-                        m_timestampQueries[m_currentTimestampQueryIndex].m_query->WriteTimestamp(*commandList);
+                        m_timestampQueries[m_currentTimestampQueryIndex].m_query->GetDeviceQuery(context.GetDeviceIndex())->WriteTimestamp(*commandList);
                         m_timestampQueries[m_currentTimestampQueryIndex].m_isValid = true;
                     }
 
                     if (m_pipelineStatisticsEnabled)
                     {
-                        m_statisticsQueries[m_currentStatisticsQueryIndex].m_query->Begin(*commandList);
+                        m_statisticsQueries[m_currentStatisticsQueryIndex].m_query->GetDeviceQuery(context.GetDeviceIndex())->Begin(*commandList);
                     }
 
                     switch (m_currentType)
@@ -484,7 +485,7 @@ namespace AtomSampleViewer
                     }
                     case QueryType::Predication:
                     {
-                        commandList->BeginPredication(*m_predicationBuffer, 0, RHI::PredicationOp::EqualZero);
+                        commandList->BeginPredication(*m_predicationBuffer->GetDeviceBuffer(context.GetDeviceIndex()), 0, RHI::PredicationOp::EqualZero);
                         commandList->Submit(drawItem, 0);
                         commandList->EndPredication();
                         break;
@@ -493,28 +494,28 @@ namespace AtomSampleViewer
 
                     if (m_pipelineStatisticsEnabled)
                     {
-                        m_statisticsQueries[m_currentStatisticsQueryIndex].m_query->End(*commandList);
+                        m_statisticsQueries[m_currentStatisticsQueryIndex].m_query->GetDeviceQuery(context.GetDeviceIndex())->End(*commandList);
                         m_statisticsQueries[m_currentStatisticsQueryIndex].m_isValid = true;
                     }
 
                     if (m_timestampEnabled)
                     {
-                        m_timestampQueries[m_currentTimestampQueryIndex + 1].m_query->WriteTimestamp(*commandList);
+                        m_timestampQueries[m_currentTimestampQueryIndex + 1].m_query->GetDeviceQuery(context.GetDeviceIndex())->WriteTimestamp(*commandList);
                         m_timestampQueries[m_currentTimestampQueryIndex + 1].m_isValid = true;
                     }
                 }
 
                 // Draw occluding quad
-                shaderResourceGroups[0] = m_shaderResourceGroups[1]->GetRHIShaderResourceGroup();
+                shaderResourceGroups[0] = m_shaderResourceGroups[1]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
                 commandList->Submit(drawItem, 1);
 
                 // Draw quad to use for the oclussion query
-                drawItem.m_pipelineState = m_boudingBoxPipelineState.get();
-                shaderResourceGroups[0] = m_shaderResourceGroups[2]->GetRHIShaderResourceGroup();
+                drawItem.m_pipelineState = m_boudingBoxPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                shaderResourceGroups[0] = m_shaderResourceGroups[2]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
                 auto& queryEntry = m_occlusionQueries[m_currentOcclusionQueryIndex];
-                queryEntry.m_query->Begin(*commandList, m_precisionOcclusionEnabled ? RHI::QueryControlFlags::PreciseOcclusion : RHI::QueryControlFlags::None);
+                queryEntry.m_query->GetDeviceQuery(context.GetDeviceIndex())->Begin(*commandList, m_precisionOcclusionEnabled ? RHI::QueryControlFlags::PreciseOcclusion : RHI::QueryControlFlags::None);
                 commandList->Submit(drawItem, 2);
-                queryEntry.m_query->End(*commandList);
+                queryEntry.m_query->GetDeviceQuery(context.GetDeviceIndex())->End(*commandList);
                 queryEntry.m_isValid = true;
             }
         };
@@ -552,8 +553,8 @@ namespace AtomSampleViewer
         queryPoolDesc.m_type = type;
         queryPoolDesc.m_pipelineStatisticsMask = statisticsMask;
 
-        queryPool = RHI::Factory::Get().CreateQueryPool();
-        auto result = queryPool->Init(*device, queryPoolDesc);
+        queryPool = aznew RHI::QueryPool;
+        auto result = queryPool->Init(queryPoolDesc);
         if (result != RHI::ResultCode::Success)
         {
             AZ_Assert(false, "Failed to createa query pool");
@@ -562,7 +563,7 @@ namespace AtomSampleViewer
 
         for (auto& queryEntry : queries)
         {
-            queryEntry.m_query = RHI::Factory::Get().CreateQuery();
+            queryEntry.m_query = aznew RHI::Query;
             queryPool->InitQuery(queryEntry.m_query.get());
         }
     }
@@ -588,14 +589,14 @@ namespace AtomSampleViewer
             return;
         }
 
-        m_predicationBufferPool = RHI::Factory::Get().CreateBufferPool();
+        m_predicationBufferPool = aznew RHI::BufferPool();
 
         RHI::BufferPoolDescriptor bufferPoolDesc;
         bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::Predication | RHI::BufferBindFlags::CopyWrite;
         bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-        m_predicationBufferPool->Init(*device, bufferPoolDesc);
+        m_predicationBufferPool->Init(bufferPoolDesc);
 
-        m_predicationBuffer = RHI::Factory::Get().CreateBuffer();
+        m_predicationBuffer = aznew RHI::Buffer();
 
         RHI::BufferInitRequest request;
         request.m_buffer = m_predicationBuffer.get();

+ 1 - 1
Gem/Code/Source/RHI/QueryExampleComponent.h

@@ -100,8 +100,8 @@ namespace AtomSampleViewer
             AZStd::array<VertexPosition, 4> m_positions;
             AZStd::array<uint16_t, 6> m_indices;
         };
-        AZ::RHI::StreamBufferView m_quadStreamBufferView;
         AZ::RHI::InputStreamLayout m_quadInputStreamLayout;
+        AZ::RHI::GeometryView m_geometryView;
 
         AZStd::array<AZ::Data::Instance<AZ::RPI::ShaderResourceGroup>, 3> m_shaderResourceGroups;
         AZ::RHI::ShaderInputConstantIndex m_objectMatrixConstantIndex;

+ 57 - 70
Gem/Code/Source/RHI/RayTracingExampleComponent.cpp

@@ -64,16 +64,14 @@ namespace AtomSampleViewer
 
     void RayTracingExampleComponent::CreateResourcePools()
     {
-        RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
-
         // create input assembly buffer pool
         {
-            m_inputAssemblyBufferPool = RHI::Factory::Get().CreateBufferPool();
+            m_inputAssemblyBufferPool = aznew RHI::BufferPool();
 
             RHI::BufferPoolDescriptor bufferPoolDesc;
             bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
             bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Host;
-            [[maybe_unused]] RHI::ResultCode resultCode = m_inputAssemblyBufferPool->Init(*device, bufferPoolDesc);
+            [[maybe_unused]] RHI::ResultCode resultCode = m_inputAssemblyBufferPool->Init(bufferPoolDesc);
             AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to initialize input assembly buffer pool");
         }
 
@@ -82,14 +80,14 @@ namespace AtomSampleViewer
             RHI::ImagePoolDescriptor imagePoolDesc;
             imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::ShaderReadWrite;
 
-            m_imagePool = RHI::Factory::Get().CreateImagePool();
-            [[maybe_unused]] RHI::ResultCode result = m_imagePool->Init(*device, imagePoolDesc);
+            m_imagePool = aznew RHI::ImagePool();
+            [[maybe_unused]] RHI::ResultCode result = m_imagePool->Init(imagePoolDesc);
             AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize output image pool");
         }
 
         // initialize ray tracing buffer pools
-        m_rayTracingBufferPools = RHI::RayTracingBufferPools::CreateRHIRayTracingBufferPools();
-        m_rayTracingBufferPools->Init(device);
+        m_rayTracingBufferPools = aznew RHI::RayTracingBufferPools;
+        m_rayTracingBufferPools->Init(RHI::MultiDevice::AllDevices);
     }
 
     void RayTracingExampleComponent::CreateGeometry()
@@ -101,7 +99,7 @@ namespace AtomSampleViewer
             SetVertexPosition(m_triangleVertices.data(), 1, 0.5f, -0.5f, 1.0);
             SetVertexPosition(m_triangleVertices.data(), 2, -0.5f, -0.5f, 1.0);
 
-            m_triangleVB = RHI::Factory::Get().CreateBuffer();
+            m_triangleVB = aznew RHI::Buffer();
             m_triangleVB->SetName(AZ::Name("Triangle VB"));
 
             RHI::BufferInitRequest request;
@@ -113,7 +111,7 @@ namespace AtomSampleViewer
             // index buffer
             SetVertexIndexIncreasing(m_triangleIndices.data(), m_triangleIndices.size());
 
-            m_triangleIB = RHI::Factory::Get().CreateBuffer();
+            m_triangleIB = aznew RHI::Buffer();
             m_triangleIB->SetName(AZ::Name("Triangle IB"));
 
             request.m_buffer = m_triangleIB.get();
@@ -130,7 +128,7 @@ namespace AtomSampleViewer
             SetVertexPosition(m_rectangleVertices.data(), 2,  0.5f, -0.5f, 1.0);
             SetVertexPosition(m_rectangleVertices.data(), 3, -0.5f, -0.5f, 1.0);
 
-            m_rectangleVB = RHI::Factory::Get().CreateBuffer();
+            m_rectangleVB = aznew RHI::Buffer();
             m_rectangleVB->SetName(AZ::Name("Rectangle VB"));
 
             RHI::BufferInitRequest request;
@@ -147,7 +145,7 @@ namespace AtomSampleViewer
             m_rectangleIndices[4] = 2;
             m_rectangleIndices[5] = 3;
 
-            m_rectangleIB = RHI::Factory::Get().CreateBuffer();
+            m_rectangleIB = aznew RHI::Buffer();
             m_rectangleIB->SetName(AZ::Name("Rectangle IB"));
 
             request.m_buffer = m_rectangleIB.get();
@@ -162,7 +160,7 @@ namespace AtomSampleViewer
         FullScreenBufferData bufferData;
         SetFullScreenRect(bufferData.m_positions.data(), bufferData.m_uvs.data(), bufferData.m_indices.data());
 
-        m_fullScreenInputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
+        m_fullScreenInputAssemblyBuffer = aznew RHI::Buffer();
 
         RHI::BufferInitRequest request;
         request.m_buffer = m_fullScreenInputAssemblyBuffer.get();
@@ -170,29 +168,28 @@ namespace AtomSampleViewer
         request.m_initialData = &bufferData;
         m_inputAssemblyBufferPool->InitBuffer(request);
 
-        m_fullScreenStreamBufferViews[0] =
-        {
+        m_geometryView.SetDrawArguments(RHI::DrawIndexed(0, 6, 0));
+
+        m_geometryView.AddStreamBufferView({
             *m_fullScreenInputAssemblyBuffer,
             offsetof(FullScreenBufferData, m_positions),
             sizeof(FullScreenBufferData::m_positions),
             sizeof(VertexPosition)
-        };
+        });
 
-        m_fullScreenStreamBufferViews[1] =
-        {
+        m_geometryView.AddStreamBufferView({
             *m_fullScreenInputAssemblyBuffer,
             offsetof(FullScreenBufferData, m_uvs),
             sizeof(FullScreenBufferData::m_uvs),
             sizeof(VertexUV)
-        };
+        });
 
-        m_fullScreenIndexBufferView =
-        {
+        m_geometryView.SetIndexBufferView({
             *m_fullScreenInputAssemblyBuffer,
             offsetof(FullScreenBufferData, m_indices),
             sizeof(FullScreenBufferData::m_indices),
             RHI::IndexFormat::Uint16
-        };
+        });
 
         RHI::InputStreamLayoutBuilder layoutBuilder;
         layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
@@ -202,10 +199,8 @@ namespace AtomSampleViewer
 
     void RayTracingExampleComponent::CreateOutputTexture()
     {
-        RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
-
         // create output image
-        m_outputImage = RHI::Factory::Get().CreateImage();
+        m_outputImage = aznew RHI::Image();
 
         RHI::ImageInitRequest request;
         request.m_image = m_outputImage.get();
@@ -214,16 +209,16 @@ namespace AtomSampleViewer
         AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize output image");
 
         m_outputImageViewDescriptor = RHI::ImageViewDescriptor::Create(RHI::Format::R8G8B8A8_UNORM, 0, 0);
-        m_outputImageView = m_outputImage->GetImageView(m_outputImageViewDescriptor);
+        m_outputImageView = m_outputImage->BuildImageView(m_outputImageViewDescriptor);
         AZ_Assert(m_outputImageView.get(), "Failed to create output image view");
-        AZ_Assert(m_outputImageView->IsFullView(), "Image View initialization IsFullView() failed");
+        AZ_Assert(m_outputImageView->GetDeviceImageView(RHI::MultiDevice::DefaultDeviceIndex)->IsFullView(), "Image View initialization IsFullView() failed");
     }
 
     void RayTracingExampleComponent::CreateRayTracingAccelerationStructureObjects()
     {
-        m_triangleRayTracingBlas = AZ::RHI::RayTracingBlas::CreateRHIRayTracingBlas();
-        m_rectangleRayTracingBlas = AZ::RHI::RayTracingBlas::CreateRHIRayTracingBlas();
-        m_rayTracingTlas = AZ::RHI::RayTracingTlas::CreateRHIRayTracingTlas();
+        m_triangleRayTracingBlas = aznew AZ::RHI::RayTracingBlas;
+        m_rectangleRayTracingBlas = aznew AZ::RHI::RayTracingBlas;
+        m_rayTracingTlas = aznew AZ::RHI::RayTracingTlas;
     }
 
     void RayTracingExampleComponent::CreateRasterShader()
@@ -250,8 +245,6 @@ namespace AtomSampleViewer
 
     void RayTracingExampleComponent::CreateRayTracingPipelineState()
     {
-        RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
-
         // load ray generation shader
         const char* rayGenerationShaderFilePath = "Shaders/RHI/RayTracingDispatch.azshader";
         m_rayGenerationShader = LoadShader(rayGenerationShaderFilePath, RayTracingExampleName);
@@ -311,15 +304,14 @@ namespace AtomSampleViewer
                 ->ClosestHitShaderName(AZ::Name("ClosestHitSolidShader"));
     
         // create the ray tracing pipeline state object
-        m_rayTracingPipelineState = RHI::Factory::Get().CreateRayTracingPipelineState();
-        m_rayTracingPipelineState->Init(*device.get(), &descriptor);
+        m_rayTracingPipelineState = aznew RHI::RayTracingPipelineState;
+        m_rayTracingPipelineState->Init(RHI::MultiDevice::AllDevices, descriptor);
     }
 
     void RayTracingExampleComponent::CreateRayTracingShaderTable()
     {
-        RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
-        m_rayTracingShaderTable = RHI::Factory::Get().CreateRayTracingShaderTable();
-        m_rayTracingShaderTable->Init(*device.get(), *m_rayTracingBufferPools);
+        m_rayTracingShaderTable = aznew RHI::RayTracingShaderTable;
+        m_rayTracingShaderTable->Init(RHI::MultiDevice::AllDevices, *m_rayTracingBufferPools);
     }
 
     void RayTracingExampleComponent::CreateRayTracingAccelerationTableScope()
@@ -330,8 +322,6 @@ namespace AtomSampleViewer
 
         const auto prepareFunction = [this]([[maybe_unused]] RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
         {
-            RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
-
             // create triangle BLAS buffer if necessary
             if (!m_triangleRayTracingBlas->IsValid())
             {
@@ -359,7 +349,7 @@ namespace AtomSampleViewer
                         ->IndexBuffer(triangleIndexBufferView)
                 ;
 
-                m_triangleRayTracingBlas->CreateBuffers(*device, &triangleBlasDescriptor, *m_rayTracingBufferPools);
+                m_triangleRayTracingBlas->CreateBuffers(RHI::MultiDevice::AllDevices, &triangleBlasDescriptor, *m_rayTracingBufferPools);
             }
 
             // create rectangle BLAS if necessary
@@ -389,7 +379,7 @@ namespace AtomSampleViewer
                         ->IndexBuffer(rectangleIndexBufferView)
                 ;
 
-                m_rectangleRayTracingBlas->CreateBuffers(*device, &rectangleBlasDescriptor, *m_rayTracingBufferPools);
+                m_rectangleRayTracingBlas->CreateBuffers(RHI::MultiDevice::AllDevices, &rectangleBlasDescriptor, *m_rayTracingBufferPools);
             }
 
             m_time += 0.005f;
@@ -436,7 +426,7 @@ namespace AtomSampleViewer
                     ->Transform(rectangleTransform)
                 ;
 
-            m_rayTracingTlas->CreateBuffers(*device, &tlasDescriptor, *m_rayTracingBufferPools);
+            m_rayTracingTlas->CreateBuffers(RHI::MultiDevice::AllDevices, &tlasDescriptor, *m_rayTracingBufferPools);
 
             m_tlasBufferViewDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, (uint32_t)m_rayTracingTlas->GetTlasBuffer()->GetDescriptor().m_byteCount);
 
@@ -448,7 +438,7 @@ namespace AtomSampleViewer
             desc.m_bufferViewDescriptor = m_tlasBufferViewDescriptor;
             desc.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
 
-            frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite);
+            frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::AnyGraphics);
         };
 
         RHI::EmptyCompileFunction<ScopeData> compileFunction;
@@ -457,9 +447,10 @@ namespace AtomSampleViewer
         {
             RHI::CommandList* commandList = context.GetCommandList();
 
-            commandList->BuildBottomLevelAccelerationStructure(*m_triangleRayTracingBlas);
-            commandList->BuildBottomLevelAccelerationStructure(*m_rectangleRayTracingBlas);
-            commandList->BuildTopLevelAccelerationStructure(*m_rayTracingTlas);
+            commandList->BuildBottomLevelAccelerationStructure(*m_triangleRayTracingBlas->GetDeviceRayTracingBlas(context.GetDeviceIndex()));
+            commandList->BuildBottomLevelAccelerationStructure(*m_rectangleRayTracingBlas->GetDeviceRayTracingBlas(context.GetDeviceIndex()));
+            commandList->BuildTopLevelAccelerationStructure(
+                *m_rayTracingTlas->GetDeviceRayTracingTlas(context.GetDeviceIndex()), { m_triangleRayTracingBlas->GetDeviceRayTracingBlas(context.GetDeviceIndex()).get(), m_rectangleRayTracingBlas->GetDeviceRayTracingBlas(context.GetDeviceIndex()).get() });
         };
 
         m_scopeProducers.emplace_back(
@@ -493,7 +484,7 @@ namespace AtomSampleViewer
                 desc.m_imageViewDescriptor = m_outputImageViewDescriptor;
                 desc.m_loadStoreAction.m_clearValue = RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f);
 
-                frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite);
+                frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::RayTracingShader);
             }
 
             // attach TLAS buffer
@@ -504,7 +495,7 @@ namespace AtomSampleViewer
                 desc.m_bufferViewDescriptor = m_tlasBufferViewDescriptor;
                 desc.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
 
-                frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite);
+                frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::RayTracingShader);
             }
 
             frameGraph.SetEstimatedItemCount(1);
@@ -520,7 +511,7 @@ namespace AtomSampleViewer
 
                 uint32_t tlasBufferByteCount = aznumeric_cast<uint32_t>(m_rayTracingTlas->GetTlasBuffer()->GetDescriptor().m_byteCount);
                 RHI::BufferViewDescriptor bufferViewDescriptor = RHI::BufferViewDescriptor::CreateRayTracingTLAS(tlasBufferByteCount);
-                m_globalSrg->SetBufferView(tlasConstantIndex, m_rayTracingTlas->GetTlasBuffer()->GetBufferView(bufferViewDescriptor).get());
+                m_globalSrg->SetBufferView(tlasConstantIndex, m_rayTracingTlas->GetTlasBuffer()->BuildBufferView(bufferViewDescriptor).get());
 
                 RHI::ShaderInputImageIndex outputConstantIndex;
                 FindShaderInputIndex(&outputConstantIndex, m_globalSrg, AZ::Name{ "m_output" }, RayTracingExampleName);
@@ -593,19 +584,19 @@ namespace AtomSampleViewer
 
             RHI::CommandList* commandList = context.GetCommandList();
 
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] = {
-                m_globalSrg->GetRHIShaderResourceGroup()
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_globalSrg->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
             };
 
-            RHI::DispatchRaysItem dispatchRaysItem;
-            dispatchRaysItem.m_width = m_imageWidth;
-            dispatchRaysItem.m_height = m_imageHeight;
-            dispatchRaysItem.m_depth = 1;
-            dispatchRaysItem.m_rayTracingPipelineState = m_rayTracingPipelineState.get();
-            dispatchRaysItem.m_rayTracingShaderTable = m_rayTracingShaderTable.get();
+            RHI::DeviceDispatchRaysItem dispatchRaysItem;
+            dispatchRaysItem.m_arguments.m_direct.m_width = m_imageWidth;
+            dispatchRaysItem.m_arguments.m_direct.m_height = m_imageHeight;
+            dispatchRaysItem.m_arguments.m_direct.m_depth = 1;
+            dispatchRaysItem.m_rayTracingPipelineState = m_rayTracingPipelineState->GetDeviceRayTracingPipelineState(context.GetDeviceIndex()).get();
+            dispatchRaysItem.m_rayTracingShaderTable = m_rayTracingShaderTable->GetDeviceRayTracingShaderTable(context.GetDeviceIndex()).get();
             dispatchRaysItem.m_shaderResourceGroupCount = RHI::ArraySize(shaderResourceGroups);
             dispatchRaysItem.m_shaderResourceGroups = shaderResourceGroups;
-            dispatchRaysItem.m_globalPipelineState = m_globalPipelineState.get();
+            dispatchRaysItem.m_globalPipelineState = m_globalPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
 
             // submit the DispatchRays item
             commandList->Submit(dispatchRaysItem);
@@ -647,7 +638,7 @@ namespace AtomSampleViewer
                 desc.m_imageViewDescriptor = m_outputImageViewDescriptor;
                 desc.m_loadStoreAction.m_clearValue = RHI::ClearValue::CreateVector4Float(0.0f, 0.0f, 0.0f, 0.0f);
 
-                frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite);
+                frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite, RHI::ScopeAttachmentStage::FragmentShader);
 
                 const Name outputImageId{ "m_output" };
                 RHI::ShaderInputImageIndex outputImageIndex = m_drawSRG->FindShaderInputImageIndex(outputImageId);
@@ -668,18 +659,14 @@ namespace AtomSampleViewer
             commandList->SetViewports(&m_viewport, 1);
             commandList->SetScissors(&m_scissor, 1);
 
-            RHI::DrawIndexed drawIndexed;
-            drawIndexed.m_indexCount = 6;
-            drawIndexed.m_instanceCount = 1;
-
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_drawSRG->GetRHIShaderResourceGroup() };
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_drawSRG->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+            };
 
-            RHI::DrawItem drawItem;
-            drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_drawPipelineState.get();
-            drawItem.m_indexBufferView = &m_fullScreenIndexBufferView;
-            drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_fullScreenStreamBufferViews.size());
-            drawItem.m_streamBufferViews = m_fullScreenStreamBufferViews.data();
+            RHI::DeviceDrawItem drawItem;
+            drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+            drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
+            drawItem.m_pipelineState = m_drawPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
             drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
             drawItem.m_shaderResourceGroups = shaderResourceGroups;
 

+ 11 - 13
Gem/Code/Source/RHI/RayTracingExampleComponent.h

@@ -8,20 +8,20 @@
 
 #pragma once
 
-#include <AzCore/Component/Component.h>
-#include <AzCore/Math/Matrix4x4.h>
-#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
-#include <Atom/RHI/RayTracingPipelineState.h>
-#include <Atom/RHI/RayTracingShaderTable.h>
-#include <Atom/RHI/FrameScheduler.h>
-#include <Atom/RHI/DispatchRaysItem.h>
+#include <Atom/RHI/BufferPool.h>
 #include <Atom/RHI/Device.h>
+#include <Atom/RHI/DeviceDispatchRaysItem.h>
 #include <Atom/RHI/Factory.h>
+#include <Atom/RHI/FrameScheduler.h>
 #include <Atom/RHI/PipelineState.h>
-#include <Atom/RHI/BufferPool.h>
-#include <RHI/BasicRHIComponent.h>
-#include <Atom/RHI/RayTracingBufferPools.h>
 #include <Atom/RHI/RayTracingAccelerationStructure.h>
+#include <Atom/RHI/RayTracingBufferPools.h>
+#include <Atom/RHI/RayTracingPipelineState.h>
+#include <Atom/RHI/RayTracingShaderTable.h>
+#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
+#include <AzCore/Component/Component.h>
+#include <AzCore/Math/Matrix4x4.h>
+#include <RHI/BasicRHIComponent.h>
 
 namespace AtomSampleViewer
 {
@@ -134,14 +134,12 @@ namespace AtomSampleViewer
         };
 
         RHI::Ptr<RHI::Buffer> m_fullScreenInputAssemblyBuffer;
-        AZStd::array<RHI::StreamBufferView, 2> m_fullScreenStreamBufferViews;
-        RHI::IndexBufferView m_fullScreenIndexBufferView;
+        RHI::GeometryView m_geometryView;
         RHI::InputStreamLayout m_fullScreenInputStreamLayout;
 
         RHI::ConstPtr<RHI::PipelineState> m_drawPipelineState;
         Data::Instance<RPI::ShaderResourceGroup> m_drawSRG;
         RHI::ShaderInputConstantIndex m_drawDimensionConstantIndex;
-        RHI::DrawItem m_drawItem;
 
         // time variable for moving the triangles and rectangle each frame
         float m_time = 0.0f;

+ 33 - 35
Gem/Code/Source/RHI/SphericalHarmonicsExampleComponent.cpp

@@ -93,18 +93,18 @@ namespace AtomSampleViewer
         }
 
         {
-            m_bufferPool = RHI::Factory::Get().CreateBufferPool();
+            m_bufferPool = aznew RHI::BufferPool();
 
             RHI::BufferPoolDescriptor bufferPoolDesc;
             bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
             bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-            m_bufferPool->Init(*device, bufferPoolDesc);
+            m_bufferPool->Init(bufferPoolDesc);
 
             SetFullScreenRect(bufferData.m_positions.data(), bufferData.m_uvs.data(), bufferData.m_indices.data());
 
-            m_positionBuffer = RHI::Factory::Get().CreateBuffer();
-            m_indexBuffer = RHI::Factory::Get().CreateBuffer();
-            m_uvBuffer = RHI::Factory::Get().CreateBuffer();
+            m_positionBuffer = aznew RHI::Buffer();
+            m_indexBuffer = aznew RHI::Buffer();
+            m_uvBuffer = aznew RHI::Buffer();
 
             RHI::ResultCode result = RHI::ResultCode::Success;
             RHI::BufferInitRequest request;
@@ -142,26 +142,35 @@ namespace AtomSampleViewer
                 return;
             }
 
-            m_streamBufferViews[0] = {
+            m_geometryView.SetDrawArguments(RHI::DrawIndexed(0, 6, 0));
+
+            m_geometryView.SetIndexBufferView({
+                *m_indexBuffer,
+                0,
+                indexBufSize,
+                RHI::IndexFormat::Uint16
+            });
+
+            m_geometryView.AddStreamBufferView({
                 *m_positionBuffer,
                 0,
                 positionBufSize,
                 sizeof(VertexPosition)
-            };
+            });
 
-            m_streamBufferViews[1] = {
+            m_geometryView.AddStreamBufferView({
                 *m_uvBuffer,
                 0,
                 uvBufSize,
                 sizeof(VertexUV)
-            };
+            });
 
             RHI::InputStreamLayoutBuilder layoutBuilder;
             layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
             layoutBuilder.AddBuffer()->Channel("UV", RHI::Format::R32G32_FLOAT);
             pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
 
-            RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_streamBufferViews);
+            RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_geometryView, m_geometryView.GetFullStreamBufferIndices());
         }
 
         constexpr float objectMatrixScale = 0.8f;
@@ -307,35 +316,24 @@ namespace AtomSampleViewer
                 commandList->SetScissors(&m_scissor, 1);
 
                 // Bind ViewSrg
-                commandList->SetShaderResourceGroupForDraw(*m_viewShaderResourceGroup->GetRHIShaderResourceGroup());
-
-                const RHI::IndexBufferView indexBufferView =
-                {
-                    *m_indexBuffer,
-                    0,
-                    indexBufSize,
-                    RHI::IndexFormat::Uint16
-                };
-
-                RHI::DrawIndexed drawIndexed;
-                drawIndexed.m_indexCount = 6;
-                drawIndexed.m_instanceCount = 1;
-
-                const RHI::ShaderResourceGroup* shaderResourceGroups[] = {
-                    (m_mode ? m_demoShaderResourceGroup->GetRHIShaderResourceGroup() :
-                              m_renderShaderResourceGroup->GetRHIShaderResourceGroup()
-                    ),
-                    m_viewShaderResourceGroup->GetRHIShaderResourceGroup()
+                commandList->SetShaderResourceGroupForDraw(*m_viewShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get());
+
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                    (m_mode ? m_demoShaderResourceGroup->GetRHIShaderResourceGroup()
+                                  ->GetDeviceShaderResourceGroup(context.GetDeviceIndex())
+                                  .get()
+                            : m_renderShaderResourceGroup->GetRHIShaderResourceGroup()
+                                  ->GetDeviceShaderResourceGroup(context.GetDeviceIndex())
+                                  .get()),
+                    m_viewShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
                 };
 
-                RHI::DrawItem drawItem;
-                drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_mode ? m_demoPipelineState.get() : m_renderPipelineState.get();
-                drawItem.m_indexBufferView = &indexBufferView;
+                RHI::DeviceDrawItem drawItem;
+                drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+                drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
+                drawItem.m_pipelineState = m_mode ? m_demoPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get() : m_renderPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                 drawItem.m_shaderResourceGroups = shaderResourceGroups;
-                drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
-                drawItem.m_streamBufferViews = m_streamBufferViews.data();
 
                 commandList->Submit(drawItem);
             };

+ 1 - 3
Gem/Code/Source/RHI/SphericalHarmonicsExampleComponent.h

@@ -13,7 +13,6 @@
 #include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
 
 #include <Atom/RHI/BufferPool.h>
-#include <Atom/RHI/DrawItem.h>
 #include <Atom/RHI.Reflect/SamplerState.h>
 
 #include <AzCore/Component/TickBus.h>
@@ -127,6 +126,7 @@ namespace AtomSampleViewer
         AZ::RHI::Ptr<AZ::RHI::Buffer> m_indexBuffer;
         AZ::RHI::Ptr<AZ::RHI::Buffer> m_positionBuffer;
         AZ::RHI::Ptr<AZ::RHI::Buffer> m_uvBuffer;
+        AZ::RHI::GeometryView m_geometryView;
 
         struct BufferData
         {
@@ -135,8 +135,6 @@ namespace AtomSampleViewer
             AZStd::array<uint16_t, 6> m_indices;
         };
 
-        AZ::RHI::DrawItem m_drawItem;
-        AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
         // ------------------------------------------------------------
 
         AZ::EntityId m_cameraEntityId;

+ 30 - 37
Gem/Code/Source/RHI/StencilExampleComponent.cpp

@@ -52,13 +52,12 @@ namespace AtomSampleViewer
         RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
 
         {
-            m_inputAssemblyBufferPool = RHI::Factory::Get().CreateBufferPool();
+            m_inputAssemblyBufferPool = aznew RHI::BufferPool();
 
             RHI::BufferPoolDescriptor bufferPoolDesc;
             bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
             bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-            m_inputAssemblyBufferPool->Init(*device, bufferPoolDesc);
-
+            m_inputAssemblyBufferPool->Init(bufferPoolDesc);
 
             BufferData bufferData;
 
@@ -96,7 +95,7 @@ namespace AtomSampleViewer
             // Triangles index
             SetVertexIndexIncreasing(bufferData.m_indices.data(), s_numberOfVertices);
 
-            m_inputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
+            m_inputAssemblyBuffer = aznew RHI::Buffer();
 
             RHI::BufferInitRequest request;
             request.m_buffer = m_inputAssemblyBuffer.get();
@@ -104,28 +103,38 @@ namespace AtomSampleViewer
             request.m_initialData = &bufferData;
             m_inputAssemblyBufferPool->InitBuffer(request);
 
-            m_streamBufferViews[0] =
-            {
+            m_geometryView.SetIndexBufferView({
+                *m_inputAssemblyBuffer,
+                offsetof(BufferData, m_indices),
+                sizeof(BufferData::m_indices),
+                RHI::IndexFormat::Uint16
+            });
+
+            m_geometryView.AddStreamBufferView({
                 *m_inputAssemblyBuffer,
                 offsetof(BufferData, m_positions),
                 sizeof(BufferData::m_positions),
                 sizeof(VertexPosition)
-            };
+            });
 
-            m_streamBufferViews[1] =
-            {
+            m_geometryView.AddStreamBufferView({
                 *m_inputAssemblyBuffer,
                 offsetof(BufferData, m_colors),
                 sizeof(BufferData::m_colors),
                 sizeof(VertexColor)
-            };
+            });
+
+            m_geometryView.SetDrawArguments(RHI::DrawIndexed(0, s_numberOfVertices / 2, 0));
+
+            m_geometryView2 = m_geometryView;
+            m_geometryView2.SetDrawArguments(RHI::DrawIndexed(0, 3, s_numberOfVertices / 2));
 
             RHI::InputStreamLayoutBuilder layoutBuilder;
             layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
             layoutBuilder.AddBuffer()->Channel("COLOR", RHI::Format::R32G32B32A32_FLOAT);
             pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
 
-            RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_streamBufferViews);
+            RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_geometryView, m_geometryView.GetFullStreamBufferIndices());
         }
 
         {
@@ -186,7 +195,9 @@ namespace AtomSampleViewer
                     dsDesc.m_loadStoreAction.m_clearValue = m_depthClearValue;
                     dsDesc.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Clear;
                     dsDesc.m_loadStoreAction.m_loadActionStencil = RHI::AttachmentLoadAction::Clear;
-                    frameGraph.UseDepthStencilAttachment(dsDesc, RHI::ScopeAttachmentAccess::ReadWrite);
+                    frameGraph.UseDepthStencilAttachment(
+                        dsDesc, RHI::ScopeAttachmentAccess::ReadWrite,
+                        RHI::ScopeAttachmentStage::EarlyFragmentTest | RHI::ScopeAttachmentStage::LateFragmentTest);
                 }
                 // 1 color triangles draw item + 8 white triangles draw items
                 uint32_t itemCount = static_cast<uint32_t>(1 + m_pipelineStateStencil.size());
@@ -203,45 +214,27 @@ namespace AtomSampleViewer
                 commandList->SetViewports(&m_viewport, 1);
                 commandList->SetScissors(&m_scissor, 1);
 
-                const RHI::IndexBufferView indexBufferView =
-                {
-                    *m_inputAssemblyBuffer,
-                    offsetof(BufferData, m_indices),
-                    sizeof(BufferData::m_indices),
-                    RHI::IndexFormat::Uint16
-                };
-
-                RHI::DrawIndexed drawIndexed;
-                drawIndexed.m_instanceCount = 1;
 
-                RHI::DrawItem drawItem;
-                drawItem.m_indexBufferView = &indexBufferView;
-                drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
-                drawItem.m_streamBufferViews = m_streamBufferViews.data();
+                RHI::DeviceDrawItem drawItem;
+                drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+                drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
 
                 for (uint32_t i = context.GetSubmitRange().m_startIndex; i < context.GetSubmitRange().m_endIndex; ++i)
                 {
                     if (i == 0)
                     {
-                        drawIndexed.m_indexCount = s_numberOfVertices / 2;
-
                         // Draw color triangles
-                        drawItem.m_arguments = drawIndexed;
-                        drawItem.m_pipelineState = m_pipelineStateBasePass.get();
+                        drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+                        drawItem.m_pipelineState = m_pipelineStateBasePass->GetDevicePipelineState(context.GetDeviceIndex()).get();
                         commandList->Submit(drawItem, i);
                     }
                     else
                     {
                         // Draw white triangles
-                        drawIndexed.m_indexOffset = s_numberOfVertices / 2;
-                        drawIndexed.m_indexCount = 3;
                         drawItem.m_stencilRef = 1;
-
-                        drawItem.m_arguments = drawIndexed;
-                        drawItem.m_pipelineState = m_pipelineStateStencil[i-1].get();
+                        drawItem.m_geometryView = m_geometryView2.GetDeviceGeometryView(context.GetDeviceIndex());
+                        drawItem.m_pipelineState = m_pipelineStateStencil[i-1]->GetDevicePipelineState(context.GetDeviceIndex()).get();
                         commandList->Submit(drawItem, i);
-
-                        drawIndexed.m_indexOffset += 3;
                     }
                 }
             };

+ 2 - 4
Gem/Code/Source/RHI/StencilExampleComponent.h

@@ -13,8 +13,6 @@
 
 #include <Atom/RHI/BufferPool.h>
 #include <Atom/RHI/Device.h>
-#include <Atom/RHI/DrawItem.h>
-#include <Atom/RHI/ImagePool.h>
 #include <Atom/RHI/PipelineState.h>
 
 namespace AtomSampleViewer
@@ -62,8 +60,8 @@ namespace AtomSampleViewer
             AZStd::array<uint16_t, s_numberOfVertices> m_indices;
         };
 
-        AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
-        AZ::RHI::DrawItem m_drawItem;
+        AZ::RHI::GeometryView m_geometryView;
+        AZ::RHI::GeometryView m_geometryView2;
 
         AZ::RHI::AttachmentId m_depthStencilID;
         AZ::RHI::ClearValue m_depthClearValue;

+ 54 - 42
Gem/Code/Source/RHI/SubpassExampleComponent.cpp

@@ -148,10 +148,10 @@ namespace AtomSampleViewer
     {
         const char* modelsPath[ModelType_Count] =
         {
-            "objects/plane.azmodel",
-            "objects/shaderball_simple.azmodel",
-            "objects/bunny.azmodel",
-            "objects/suzanne.azmodel",
+            "objects/plane.fbx.azmodel",
+            "objects/shaderball_simple.fbx.azmodel",
+            "objects/bunny.fbx.azmodel",
+            "objects/suzanne.fbx.azmodel",
         };
 
         for (uint32_t i = 0; i < AZ_ARRAY_SIZE(modelsPath); ++i)
@@ -222,20 +222,25 @@ namespace AtomSampleViewer
         uint32_t subpassIndex = 0;
         // Build the render attachment layout with the 2 subpasses.
         RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
+
         // GBuffer Subpass
         attachmentsBuilder.AddSubpass()
             ->RenderTargetAttachment(RHI::Format::R16G16B16A16_FLOAT, m_positionAttachmentId)
             ->RenderTargetAttachment(RHI::Format::R16G16B16A16_FLOAT, m_normalAttachmentId)
             ->RenderTargetAttachment(RHI::Format::R8G8B8A8_UNORM, m_albedoAttachmentId)
             ->RenderTargetAttachment(m_outputFormat, m_outputAttachmentId)
-            ->DepthStencilAttachment(AZ::RHI::Format::D32_FLOAT, m_depthStencilAttachmentId);
+            ->DepthStencilAttachment(AZ::RHI::Format::D32_FLOAT, m_depthStencilAttachmentId, AZ::RHI::AttachmentLoadStoreAction(),
+                AZ::RHI::ScopeAttachmentAccess::Write,
+                AZ::RHI::ScopeAttachmentStage::EarlyFragmentTest | AZ::RHI::ScopeAttachmentStage::LateFragmentTest);
         // Composition Subpass
         attachmentsBuilder.AddSubpass()
             ->SubpassInputAttachment(m_positionAttachmentId, RHI::ImageAspectFlags::Color)
             ->SubpassInputAttachment(m_normalAttachmentId, RHI::ImageAspectFlags::Color)
             ->SubpassInputAttachment(m_albedoAttachmentId, RHI::ImageAspectFlags::Color)
             ->RenderTargetAttachment(m_outputAttachmentId)
-            ->DepthStencilAttachment(m_depthStencilAttachmentId);
+            ->DepthStencilAttachment(m_depthStencilAttachmentId, AZ::RHI::AttachmentLoadStoreAction(),
+                AZ::RHI::ScopeAttachmentAccess::Read,
+                AZ::RHI::ScopeAttachmentStage::EarlyFragmentTest | AZ::RHI::ScopeAttachmentStage::LateFragmentTest);
 
         RHI::RenderAttachmentLayout renderAttachmentLayout;
         [[maybe_unused]] RHI::ResultCode result = attachmentsBuilder.End(renderAttachmentLayout);
@@ -258,7 +263,7 @@ namespace AtomSampleViewer
 
                 modelLod->GetStreamsForMesh(
                     pipelineDesc.m_inputStreamLayout,
-                    modelData.m_streamBufferList,
+                    modelData.m_streamIndices,
                     nullptr,
                     shader->GetInputContract(),
                     0);
@@ -341,6 +346,7 @@ namespace AtomSampleViewer
                 descriptor.m_attachmentId = m_positionAttachmentId;
                 descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Clear;
                 descriptor.m_loadStoreAction.m_clearValue = RHI::ClearValue::CreateVector4Float(0.f, 0.f, 0.f, 0.f);
+                descriptor.m_imageViewDescriptor.m_aspectFlags = RHI::ImageAspectFlags::Color;
                 frameGraph.UseColorAttachment(descriptor);
             }
 
@@ -350,6 +356,7 @@ namespace AtomSampleViewer
                 descriptor.m_attachmentId = m_normalAttachmentId;
                 descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Clear;
                 descriptor.m_loadStoreAction.m_clearValue = RHI::ClearValue::CreateVector4Float(0.f, 0.f, 0.f, 0.f);
+                descriptor.m_imageViewDescriptor.m_aspectFlags = RHI::ImageAspectFlags::Color;
                 frameGraph.UseColorAttachment(descriptor);
             }
 
@@ -359,6 +366,7 @@ namespace AtomSampleViewer
                 descriptor.m_attachmentId = m_albedoAttachmentId;
                 descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Clear;
                 descriptor.m_loadStoreAction.m_clearValue = RHI::ClearValue::CreateVector4Float(0.f, 0.f, 0.f, 0.f);
+                descriptor.m_imageViewDescriptor.m_aspectFlags = RHI::ImageAspectFlags::Color;
                 frameGraph.UseColorAttachment(descriptor);
             }
 
@@ -367,6 +375,7 @@ namespace AtomSampleViewer
                 RHI::ImageScopeAttachmentDescriptor descriptor;
                 descriptor.m_attachmentId = m_outputAttachmentId;
                 descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
+                descriptor.m_imageViewDescriptor.m_aspectFlags = RHI::ImageAspectFlags::Color;
                 frameGraph.UseColorAttachment(descriptor);
             }
 
@@ -376,9 +385,13 @@ namespace AtomSampleViewer
                 dsDesc.m_attachmentId = m_depthStencilAttachmentId;
                 dsDesc.m_loadStoreAction.m_clearValue = RHI::ClearValue::CreateDepthStencil(0, 0);
                 dsDesc.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Clear;
-                frameGraph.UseDepthStencilAttachment(dsDesc, RHI::ScopeAttachmentAccess::Write);
+                dsDesc.m_imageViewDescriptor.m_aspectFlags = RHI::ImageAspectFlags::Depth;
+                frameGraph.UseDepthStencilAttachment(
+                    dsDesc, RHI::ScopeAttachmentAccess::Write,
+                    AZ::RHI::ScopeAttachmentStage::EarlyFragmentTest | AZ::RHI::ScopeAttachmentStage::LateFragmentTest);
             }
 
+            frameGraph.SetGroupId(AZ::Name(SubpassInputExample::SampleName));
             frameGraph.SetEstimatedItemCount(m_meshCount);
         };
 
@@ -389,7 +402,7 @@ namespace AtomSampleViewer
             RHI::CommandList* commandList = context.GetCommandList();
 
             // Bind ViewSrg
-            commandList->SetShaderResourceGroupForDraw(*m_viewShaderResourceGroup->GetRHIShaderResourceGroup());
+            commandList->SetShaderResourceGroupForDraw(*m_viewShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get());
 
             // Set persistent viewport and scissor state.
             commandList->SetViewports(&m_viewport, 1);
@@ -399,21 +412,20 @@ namespace AtomSampleViewer
             {
                 // Model
                 const auto& modelData = m_opaqueModelsData[i];
-                const RHI::ShaderResourceGroup* shaderResourceGroups[] =
-                {
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
                     modelData.m_shaderResourceGroup->GetRHIShaderResourceGroup()
+                        ->GetDeviceShaderResourceGroup(context.GetDeviceIndex())
+                        .get()
                 };
 
-                for (const auto& mesh : m_models[modelData.m_modelType]->GetLods()[0]->GetMeshes())
+                for (auto& mesh : m_models[modelData.m_modelType]->GetLods()[0]->GetMeshes())
                 {
-                    RHI::DrawItem drawItem;
-                    drawItem.m_arguments = mesh.m_drawArguments;
-                    drawItem.m_pipelineState = modelData.m_pipelineState.get();
-                    drawItem.m_indexBufferView = &mesh.m_indexBufferView;
+                    RHI::DeviceDrawItem drawItem;
+                    drawItem.m_geometryView = mesh.GetDeviceGeometryView(context.GetDeviceIndex());
+                    drawItem.m_streamIndices = modelData.m_streamIndices;
+                    drawItem.m_pipelineState = modelData.m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                     drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                     drawItem.m_shaderResourceGroups = shaderResourceGroups;
-                    drawItem.m_streamBufferViewCount = static_cast<uint8_t>(modelData.m_streamBufferList.size());
-                    drawItem.m_streamBufferViews = modelData.m_streamBufferList.data();
 
                     commandList->Submit(drawItem);
                 }
@@ -447,7 +459,8 @@ namespace AtomSampleViewer
                 RHI::ImageScopeAttachmentDescriptor descriptor;
                 descriptor.m_attachmentId = m_positionAttachmentId;
                 descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
-                frameGraph.UseSubpassInputAttachment(descriptor);
+                descriptor.m_imageViewDescriptor.m_aspectFlags = RHI::ImageAspectFlags::Color;
+                frameGraph.UseSubpassInputAttachment(descriptor, RHI::ScopeAttachmentStage::FragmentShader);
             }
 
             // Bind the normal GBuffer
@@ -455,7 +468,8 @@ namespace AtomSampleViewer
                 RHI::ImageScopeAttachmentDescriptor descriptor;
                 descriptor.m_attachmentId = m_normalAttachmentId;
                 descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
-                frameGraph.UseSubpassInputAttachment(descriptor);
+                descriptor.m_imageViewDescriptor.m_aspectFlags = RHI::ImageAspectFlags::Color;
+                frameGraph.UseSubpassInputAttachment(descriptor, RHI::ScopeAttachmentStage::FragmentShader);
             }
 
             // Bind the albedo GBuffer
@@ -463,7 +477,8 @@ namespace AtomSampleViewer
                 RHI::ImageScopeAttachmentDescriptor descriptor;
                 descriptor.m_attachmentId = m_albedoAttachmentId;
                 descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
-                frameGraph.UseSubpassInputAttachment(descriptor);
+                descriptor.m_imageViewDescriptor.m_aspectFlags = RHI::ImageAspectFlags::Color;
+                frameGraph.UseSubpassInputAttachment(descriptor, RHI::ScopeAttachmentStage::FragmentShader);
             }
 
             // Bind SwapChain image
@@ -471,6 +486,7 @@ namespace AtomSampleViewer
                 RHI::ImageScopeAttachmentDescriptor descriptor;
                 descriptor.m_attachmentId = m_outputAttachmentId;
                 descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
+                descriptor.m_imageViewDescriptor.m_aspectFlags = RHI::ImageAspectFlags::Color;
                 frameGraph.UseColorAttachment(descriptor);
             }
 
@@ -479,17 +495,20 @@ namespace AtomSampleViewer
                 RHI::ImageScopeAttachmentDescriptor dsDesc;
                 dsDesc.m_attachmentId = m_depthStencilAttachmentId;
                 dsDesc.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
-                frameGraph.UseDepthStencilAttachment(dsDesc, RHI::ScopeAttachmentAccess::Read);
+                dsDesc.m_imageViewDescriptor.m_aspectFlags = RHI::ImageAspectFlags::Depth;
+                frameGraph.UseDepthStencilAttachment(
+                    dsDesc, RHI::ScopeAttachmentAccess::Read,
+                    RHI::ScopeAttachmentStage::EarlyFragmentTest | RHI::ScopeAttachmentStage::LateFragmentTest);
             }
-
+            frameGraph.SetGroupId(AZ::Name(SubpassInputExample::SampleName));
             frameGraph.SetEstimatedItemCount(1);
         };
 
         const auto compileFunction = [this](const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
         {
-            const AZ::RHI::ImageView* positionImageView = context.GetImageView(m_positionAttachmentId);
-            const AZ::RHI::ImageView* normalImageView = context.GetImageView(m_normalAttachmentId);
-            const AZ::RHI::ImageView* albedoImageView = context.GetImageView(m_albedoAttachmentId);
+            const auto* positionImageView = context.GetImageView(m_positionAttachmentId);
+            const auto* normalImageView = context.GetImageView(m_normalAttachmentId);
+            const auto* albedoImageView = context.GetImageView(m_albedoAttachmentId);
 
             m_compositionSubpassInputsSRG->SetImageView(m_subpassInputPosition, positionImageView);
             m_compositionSubpassInputsSRG->SetImageView(m_subpassInputNormal, normalImageView);
@@ -502,32 +521,25 @@ namespace AtomSampleViewer
             RHI::CommandList* commandList = context.GetCommandList();
 
             // Bind ViewSrg
-            commandList->SetShaderResourceGroupForDraw(*m_viewShaderResourceGroup->GetRHIShaderResourceGroup());
+            commandList->SetShaderResourceGroupForDraw(*m_viewShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get());
 
             // Set persistent viewport and scissor state.
             commandList->SetViewports(&m_viewport, 1);
             commandList->SetScissors(&m_scissor, 1);
 
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] =
-            {
-                m_compositionSubpassInputsSRG->GetRHIShaderResourceGroup(),
-                m_sceneShaderResourceGroup->GetRHIShaderResourceGroup(),
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_compositionSubpassInputsSRG->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get(),
+                m_sceneShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get(),
             };
 
-            RHI::DrawLinear drawArguments;
-            drawArguments.m_instanceCount = 1;
-            drawArguments.m_instanceOffset = 0;
-            drawArguments.m_vertexCount = 4;
-            drawArguments.m_vertexOffset = 0;
+            m_compositeGeometryView.SetDrawArguments(RHI::DrawLinear(4, 0));
 
-            RHI::DrawItem drawItem;
-            drawItem.m_arguments = RHI::DrawArguments(drawArguments);
-            drawItem.m_pipelineState = m_compositionPipeline.get();
-            drawItem.m_indexBufferView = nullptr;
+            RHI::DeviceDrawItem drawItem;
+            drawItem.m_geometryView = m_compositeGeometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+            drawItem.m_streamIndices = m_compositeGeometryView.GetFullStreamBufferIndices();
+            drawItem.m_pipelineState = m_compositionPipeline->GetDevicePipelineState(context.GetDeviceIndex()).get();
             drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
             drawItem.m_shaderResourceGroups = shaderResourceGroups;
-            drawItem.m_streamBufferViewCount = 0;
-            drawItem.m_streamBufferViews = nullptr;
             commandList->Submit(drawItem);
         };
 

+ 3 - 1
Gem/Code/Source/RHI/SubpassExampleComponent.h

@@ -72,7 +72,7 @@ namespace AtomSampleViewer
 
         struct ModelData
         {
-            AZ::RPI::ModelLod::StreamBufferViewList m_streamBufferList;
+            AZ::RHI::StreamBufferIndices m_streamIndices;
             AZ::RHI::ConstPtr<AZ::RHI::PipelineState> m_pipelineState;
             AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> m_shaderResourceGroup;
             ModelType m_modelType = ModelType_ShaderBall;
@@ -110,6 +110,8 @@ namespace AtomSampleViewer
         AZ::RHI::AttachmentId m_positionAttachmentId;
         AZ::RHI::AttachmentId m_depthStencilAttachmentId;
 
+        AZ::RHI::GeometryView m_compositeGeometryView;
+
         AZ::EntityId m_cameraEntityId;
         AzFramework::EntityContextId m_entityContextId;
 

+ 19 - 24
Gem/Code/Source/RHI/Texture3dExampleComponent.cpp

@@ -48,8 +48,6 @@ namespace AtomSampleViewer
     {
         using namespace AZ;
 
-        const RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
-
         // Get the window size
         AzFramework::NativeWindowHandle windowHandle = nullptr;
         AzFramework::WindowSystemRequestBus::BroadcastResult(
@@ -57,13 +55,12 @@ namespace AtomSampleViewer
             &AzFramework::WindowSystemRequestBus::Events::GetDefaultWindowHandle);
         AzFramework::WindowRequestBus::EventResult(
             m_windowSize,
-            windowHandle,
-            &AzFramework::WindowRequestBus::Events::GetClientAreaSize);
+            windowHandle, 
+            &AzFramework::WindowRequestBus::Events::GetRenderResolution);
 
         // Create image pool
         {
-            AZ::RHI::Factory& factory = RHI::Factory::Get();
-            m_imagePool = factory.CreateImagePool();
+            m_imagePool = aznew RHI::ImagePool;
             m_imagePool->SetName(Name("Texture3DPool"));
 
             RHI::ImagePoolDescriptor imagePoolDesc = {};
@@ -71,7 +68,7 @@ namespace AtomSampleViewer
             const uint64_t imagePoolBudget = 1 << 24; // 16 Megabyte
             imagePoolDesc.m_budgetInBytes = imagePoolBudget;
 
-            const RHI::ResultCode resultCode = m_imagePool->Init(*device, imagePoolDesc);
+            const RHI::ResultCode resultCode = m_imagePool->Init(imagePoolDesc);
             if (resultCode != RHI::ResultCode::Success)
             {
                 AZ_Error("Texture3dExampleComponent", false, "Failed to initialize image pool.");
@@ -84,7 +81,8 @@ namespace AtomSampleViewer
             // Create the 3d image data
             AZStd::vector<uint8_t> imageData;
             RHI::Format format = {};
-            BasicRHIComponent::CreateImage3dData(imageData, m_imageLayout, format, {
+            auto& deviceImageLayout{m_imageLayout.GetDeviceImageSubresource(RHI::MultiDevice::DefaultDeviceIndex)};
+            BasicRHIComponent::CreateImage3dData(imageData, deviceImageLayout, format, {
                                                             "textures/streaming/streaming13.dds.streamingimage",
                                                             "textures/streaming/streaming14.dds.streamingimage",
                                                             "textures/streaming/streaming15.dds.streamingimage",
@@ -93,12 +91,12 @@ namespace AtomSampleViewer
                                                             "textures/streaming/streaming19.dds.streamingimage" });
 
             // Create the image resource
-            m_image = RHI::Factory::Get().CreateImage();
+            m_image = aznew RHI::Image();
             m_image->SetName(Name("Texture3D"));
 
             RHI::ImageInitRequest imageRequest;
             imageRequest.m_image = m_image.get();
-            imageRequest.m_descriptor = RHI::ImageDescriptor::Create3D(RHI::ImageBindFlags::ShaderRead, m_imageLayout.m_size.m_width, m_imageLayout.m_size.m_height, m_imageLayout.m_size.m_depth, format);
+            imageRequest.m_descriptor = RHI::ImageDescriptor::Create3D(RHI::ImageBindFlags::ShaderRead, deviceImageLayout.m_size.m_width, deviceImageLayout.m_size.m_height, deviceImageLayout.m_size.m_depth, format);
             RHI::ResultCode resultCode = m_imagePool->InitImage(imageRequest);
             if (resultCode != RHI::ResultCode::Success)
             {
@@ -111,7 +109,7 @@ namespace AtomSampleViewer
             imageViewDescriptor = RHI::ImageViewDescriptor::Create(format, 0, 0);
             imageViewDescriptor.m_overrideBindFlags = RHI::ImageBindFlags::ShaderRead;
             
-            m_imageView = m_image->GetImageView(imageViewDescriptor);
+            m_imageView = m_image->BuildImageView(imageViewDescriptor);
             m_imageView->SetName(Name("Texture3DView"));
             if(!m_imageView.get())
             {
@@ -121,8 +119,7 @@ namespace AtomSampleViewer
 
             // Update/stage the image with data
             RHI::ImageSubresourceLayout imageSubresourceLayout;
-            RHI::ImageSubresourceRange range(0, 0, 0, 0);
-            m_image->GetSubresourceLayouts(range, &imageSubresourceLayout, nullptr);
+            m_image->GetSubresourceLayout(imageSubresourceLayout);
 
             RHI::ImageUpdateRequest updateRequest;
             updateRequest.m_image = m_image.get();
@@ -216,21 +213,19 @@ namespace AtomSampleViewer
                 commandList->SetViewports(&m_viewport, 1);
                 commandList->SetScissors(&m_scissor, 1);
 
-                RHI::DrawLinear drawIndexed;
-                drawIndexed.m_vertexCount = 4;
-                drawIndexed.m_instanceCount = 1;
+                m_geometryView.SetDrawArguments(RHI::DrawLinear(4, 0));
 
-                const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroup->GetRHIShaderResourceGroup() };
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                    m_shaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+                };
 
                 // Create the draw item
-                RHI::DrawItem drawItem;
-                drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_pipelineState.get();
-                drawItem.m_indexBufferView = nullptr;
+                RHI::DeviceDrawItem drawItem;
+                drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
+                drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
+                drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                 drawItem.m_shaderResourceGroups = shaderResourceGroups;
-                drawItem.m_streamBufferViewCount = 0;
-                drawItem.m_streamBufferViews = nullptr;
 
                 // Add the draw item to the commandlist
                 commandList->Submit(drawItem);
@@ -277,7 +272,7 @@ namespace AtomSampleViewer
         {
             ImGui::Text("3D image slice index");
 
-            for (int32_t i = 0; i < static_cast<int32_t>(m_imageLayout.m_size.m_depth); i++)
+            for (int32_t i = 0; i < static_cast<int32_t>(m_imageLayout.GetDeviceImageSubresource(AZ::RHI::MultiDevice::DefaultDeviceIndex).m_size.m_depth); i++)
             {
                 const AZStd::string label = AZStd::string::format("Image Slice %d", i);
                 ScriptableImGui::RadioButton(label.c_str(), &m_sliceIndex, i);

+ 1 - 0
Gem/Code/Source/RHI/Texture3dExampleComponent.h

@@ -54,6 +54,7 @@ namespace AtomSampleViewer
         AZ::RHI::Ptr<AZ::RHI::ImageView> m_imageView = nullptr;
         AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> m_shaderResourceGroup = nullptr;
         AZ::RHI::ConstPtr<AZ::RHI::PipelineState> m_pipelineState = nullptr;
+        AZ::RHI::GeometryView m_geometryView;
 
         // Shader Input indices
         AZ::RHI::ShaderInputImageIndex m_texture3dInputIndex;

Някои файлове не бяха показани, защото твърде много файлове са промени