Browse Source

Merge pull request #679 from o3de/multi-device-resources

Multi-device resources
jhmueller-huawei 1 year ago
parent
commit
9cbcde4cee
69 changed files with 2987 additions and 664 deletions
  1. 2 2
      Gem/Code/Source/BloomExampleComponent.cpp
  2. 186 0
      Gem/Code/Source/MultiGPURPIExampleComponent.cpp
  3. 82 0
      Gem/Code/Source/MultiGPURPIExampleComponent.h
  4. 18 22
      Gem/Code/Source/Passes/RayTracingAmbientOcclusionPass.cpp
  5. 1 0
      Gem/Code/Source/ProceduralSkinnedMeshUtils.cpp
  6. 17 13
      Gem/Code/Source/RHI/AlphaToCoverageExampleComponent.cpp
  7. 115 63
      Gem/Code/Source/RHI/AsyncComputeExampleComponent.cpp
  8. 7 3
      Gem/Code/Source/RHI/BasicRHIComponent.cpp
  9. 5 1
      Gem/Code/Source/RHI/BasicRHIComponent.h
  10. 64 51
      Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.cpp
  11. 27 21
      Gem/Code/Source/RHI/ComputeExampleComponent.cpp
  12. 17 16
      Gem/Code/Source/RHI/CopyQueueComponent.cpp
  13. 0 1
      Gem/Code/Source/RHI/CopyQueueComponent.h
  14. 15 8
      Gem/Code/Source/RHI/DualSourceBlendingComponent.cpp
  15. 53 42
      Gem/Code/Source/RHI/IndirectRenderingExampleComponent.cpp
  16. 20 17
      Gem/Code/Source/RHI/InputAssemblyExampleComponent.cpp
  17. 30 18
      Gem/Code/Source/RHI/MRTExampleComponent.cpp
  18. 0 2
      Gem/Code/Source/RHI/MRTExampleComponent.h
  19. 32 27
      Gem/Code/Source/RHI/MSAAExampleComponent.cpp
  20. 16 14
      Gem/Code/Source/RHI/MatrixAlignmentTestExampleComponent.cpp
  21. 0 2
      Gem/Code/Source/RHI/MatrixAlignmentTestExampleComponent.h
  22. 714 0
      Gem/Code/Source/RHI/MultiGPUExampleComponent.cpp
  23. 130 0
      Gem/Code/Source/RHI/MultiGPUExampleComponent.h
  24. 15 8
      Gem/Code/Source/RHI/MultiThreadComponent.cpp
  25. 30 14
      Gem/Code/Source/RHI/MultipleViewsComponent.cpp
  26. 0 2
      Gem/Code/Source/RHI/MultipleViewsComponent.h
  27. 35 35
      Gem/Code/Source/RHI/QueryExampleComponent.cpp
  28. 46 48
      Gem/Code/Source/RHI/RayTracingExampleComponent.cpp
  29. 10 11
      Gem/Code/Source/RHI/RayTracingExampleComponent.h
  30. 23 21
      Gem/Code/Source/RHI/SphericalHarmonicsExampleComponent.cpp
  31. 0 2
      Gem/Code/Source/RHI/SphericalHarmonicsExampleComponent.h
  32. 14 15
      Gem/Code/Source/RHI/StencilExampleComponent.cpp
  33. 0 3
      Gem/Code/Source/RHI/StencilExampleComponent.h
  34. 25 19
      Gem/Code/Source/RHI/SubpassExampleComponent.cpp
  35. 14 15
      Gem/Code/Source/RHI/Texture3dExampleComponent.cpp
  36. 17 14
      Gem/Code/Source/RHI/TextureArrayExampleComponent.cpp
  37. 17 18
      Gem/Code/Source/RHI/TextureExampleComponent.cpp
  38. 1 2
      Gem/Code/Source/RHI/TextureExampleComponent.h
  39. 31 18
      Gem/Code/Source/RHI/TextureMapExampleComponent.cpp
  40. 16 16
      Gem/Code/Source/RHI/TriangleExampleComponent.cpp
  41. 0 2
      Gem/Code/Source/RHI/TriangleExampleComponent.h
  42. 23 13
      Gem/Code/Source/RHI/TrianglesConstantBufferExampleComponent.cpp
  43. 0 3
      Gem/Code/Source/RHI/TrianglesConstantBufferExampleComponent.h
  44. 38 31
      Gem/Code/Source/RHI/VariableRateShadingExampleComponent.cpp
  45. 2 2
      Gem/Code/Source/RHI/VariableRateShadingExampleComponent.h
  46. 15 8
      Gem/Code/Source/RHI/XRExampleComponent.cpp
  47. 0 2
      Gem/Code/Source/RHI/XRExampleComponent.h
  48. 2 4
      Gem/Code/Source/ReadbackExampleComponent.cpp
  49. 2 2
      Gem/Code/Source/RootConstantsExampleComponent.cpp
  50. 7 3
      Gem/Code/Source/SampleComponentManager.cpp
  51. 7 7
      Gem/Code/Source/StreamingImageExampleComponent.cpp
  52. 2 2
      Gem/Code/Source/TonemappingExampleComponent.cpp
  53. 1 1
      Gem/Code/Source/Utils/Utils.cpp
  54. 4 0
      Gem/Code/atomsampleviewergem_private_files.cmake
  55. 28 0
      Passes/ASV/PassTemplates.azasset
  56. 54 0
      Passes/MultiGPUCompositePass.pass
  57. 52 0
      Passes/MultiGPUCopyBufferToBuffer.pass
  58. 50 0
      Passes/MultiGPUCopyBufferToImage.pass
  59. 47 0
      Passes/MultiGPUCopyImageToBuffer.pass
  60. 299 0
      Passes/MultiGPUCopyTestPipeline.pass
  61. 230 0
      Passes/MultiGPUPipeline.pass
  62. 29 0
      Passes/MultiGPUTrianglePass.pass
  63. 44 0
      Shaders/MultiGPURPIExample/Composite.azsl
  64. 22 0
      Shaders/MultiGPURPIExample/Composite.shader
  65. 66 0
      Shaders/MultiGPURPIExample/Triangle.azsl
  66. 22 0
      Shaders/MultiGPURPIExample/Triangle.shader
  67. 64 0
      Shaders/RHI/MultiGPUComposite.azsl
  68. 22 0
      Shaders/RHI/MultiGPUComposite.shader
  69. 10 0
      atomsampleviewer_asset_files.cmake

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

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

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

@@ -0,0 +1,186 @@
+/*
+ * 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/ViewProviderBus.h>
+#include <Atom/RPI.Public/RenderPipeline.h>
+#include <Atom/RPI.Public/Scene.h>
+#include <Atom/RPI.Public/RPISystemInterface.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();
+
+        // preload assets
+        AZStd::vector<AssetCollectionAsyncLoader::AssetToLoadInfo> assetList = {
+            {CubeModelFilePath, azrtti_typeid<RPI::ModelAsset>()}
+        };
+
+        PreloadAssets(assetList);
+
+        // 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;
+
+        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_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");
+            }
+            m_imguiSidebar.End();
+        }
+    }
+
+    void MultiGPURPIExampleComponent::OnAllAssetsReadyActivate()
+    {
+        auto meshFeatureProcessor = GetMeshFeatureProcessor();
+
+        auto asset = RPI::AssetUtils::LoadAssetByProductPath<RPI::ModelAsset>(CubeModelFilePath,
+                                                                              RPI::AssetUtils::TraceLevel::Assert);
+        m_meshHandle = meshFeatureProcessor->AcquireMesh(Render::MeshHandleDescriptor(asset));
+
+        const Vector3 translation{ 0.f, 0.f, -1.0f };
+        Transform transform = Transform::CreateTranslation(translation);
+        meshFeatureProcessor->SetTransform(m_meshHandle, transform);
+    }
+
+    void MultiGPURPIExampleComponent::DefaultWindowCreated()
+    {
+        AZ::Render::Bootstrap::DefaultWindowBus::BroadcastResult(m_windowContext,
+                                                                 &AZ::Render::Bootstrap::DefaultWindowBus::Events::GetDefaultWindowContext);
+    }
+} // namespace AtomSampleViewer

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

@@ -0,0 +1,82 @@
+/*
+ * 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;
+
+        // CommonSampleComponentBase overrides...
+        void OnAllAssetsReadyActivate() 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;
+    };
+
+} // namespace AtomSampleViewer

+ 18 - 22
Gem/Code/Source/Passes/RayTracingAmbientOcclusionPass.cpp

@@ -6,21 +6,21 @@
  *
  */
 
-#include <Passes/RayTracingAmbientOcclusionPass.h>
+#include <Atom/Feature/TransformService/TransformServiceFeatureProcessor.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)
@@ -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
@@ -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;
+            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.get();
-            dispatchRaysItem.m_rayTracingShaderTable = m_rayTracingShaderTable.get();
+            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);

+ 1 - 0
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>

+ 17 - 13
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(RHI::MultiDevice::AllDevices, 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;
@@ -390,24 +390,28 @@ namespace AtomSampleViewer
 
             for (uint32_t rectIndex = context.GetSubmitRange().m_startIndex; rectIndex < context.GetSubmitRange().m_endIndex; ++rectIndex)
             {
-                const RHI::ShaderResourceGroup* shaderResourceGroups[] = {m_shaderResourceGroups[typeIndex][rectIndex]->GetRHIShaderResourceGroup()};
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[typeIndex][rectIndex]
+                                                                                     ->GetRHIShaderResourceGroup()
+                                                                                     ->GetDeviceShaderResourceGroup(
+                                                                                         context.GetDeviceIndex())
+                                                                                     .get() };
 
-                RHI::DrawItem drawItem;
+                RHI::DeviceDrawItem drawItem;
                 drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_pipelineStates[typeIndex].get();
+                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
+                const RHI::DeviceIndexBufferView indexBufferView = {
+                    *m_rectangleInputAssemblyBuffer->GetDeviceBuffer(context.GetDeviceIndex()), offsetof(RectangleBufferData, m_indices),
+                    sizeof(RectangleBufferData::m_indices), RHI::IndexFormat::Uint16
                 };
                 drawItem.m_indexBufferView = &indexBufferView;
 
-                AZStd::array<AZ::RHI::StreamBufferView, 2>& streamBufferViews = m_rectangleStreamBufferViews;
+                AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> streamBufferViews{
+                    m_rectangleStreamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                    m_rectangleStreamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+                };
                 drawItem.m_streamBufferViewCount = static_cast<uint8_t>(streamBufferViews.size());
                 drawItem.m_streamBufferViews = streamBufferViews.data();
 

+ 115 - 63
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(RHI::MultiDevice::AllDevices, 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(RHI::MultiDevice::AllDevices, 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;
 
@@ -795,7 +791,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();
         };
@@ -812,16 +808,27 @@ namespace AtomSampleViewer
                 drawIndexed.m_indexCount = 6;
                 drawIndexed.m_instanceCount = 1;
 
-                const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[CopyTextureScope].front()->GetRHIShaderResourceGroup() };
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[CopyTextureScope]
+                                                                                     .front()
+                                                                                     ->GetRHIShaderResourceGroup()
+                                                                                     ->GetDeviceShaderResourceGroup(
+                                                                                         context.GetDeviceIndex())
+                                                                                     .get() };
 
-                RHI::DrawItem drawItem;
+                RHI::DeviceDrawItem drawItem;
                 drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_copyTexturePipelineState.get();
-                drawItem.m_indexBufferView = &m_quadIndexBufferView;
+                drawItem.m_pipelineState = m_copyTexturePipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                auto deviceIndexBufferView{m_quadIndexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+                drawItem.m_indexBufferView = &deviceIndexBufferView;
                 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();
+                AZStd::vector<RHI::DeviceStreamBufferView> deviceQuadStreamBufferViews;
+                for(const auto& streamBufferView : m_quadStreamBufferViews[CopyTextureScope])
+                {
+                    deviceQuadStreamBufferViews.emplace_back(streamBufferView.GetDeviceStreamBufferView(context.GetDeviceIndex()));
+                }
+                drawItem.m_streamBufferViews = deviceQuadStreamBufferViews.data();
                 commandList->Submit(drawItem);
             }
        };
@@ -882,33 +889,52 @@ namespace AtomSampleViewer
                     drawIndexed.m_indexCount = 6;
                     drawIndexed.m_instanceCount = 1;
 
-                    const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[ShadowScope][0]->GetRHIShaderResourceGroup() };
+                    const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[ShadowScope][0]
+                                                                                         ->GetRHIShaderResourceGroup()
+                                                                                         ->GetDeviceShaderResourceGroup(
+                                                                                             context.GetDeviceIndex())
+                                                                                         .get() };
 
-                    RHI::DrawItem drawItem;
+                    RHI::DeviceDrawItem drawItem;
                     drawItem.m_arguments = drawIndexed;
-                    drawItem.m_pipelineState = m_terrainPipelineStates[ShadowScope].get();
-                    drawItem.m_indexBufferView = &m_quadIndexBufferView;
+                    drawItem.m_pipelineState = m_terrainPipelineStates[ShadowScope]->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                    auto deviceIndexBufferView{m_quadIndexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+                    drawItem.m_indexBufferView = &deviceIndexBufferView;
                     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();
+                    AZStd::vector<RHI::DeviceStreamBufferView> deviceQuadStreamBufferViews;
+                    for(const auto& streamBufferView : m_quadStreamBufferViews[ShadowScope])
+                    {
+                        deviceQuadStreamBufferViews.emplace_back(streamBufferView.GetDeviceStreamBufferView(context.GetDeviceIndex()));
+                    }
+                    drawItem.m_streamBufferViews = deviceQuadStreamBufferViews.data();
                     commandList->Submit(drawItem, i);
                 }
                 else
                 {
                     // Models
-                    const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[ShadowScope][i]->GetRHIShaderResourceGroup() };
+                    const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[ShadowScope][i]
+                                                                                         ->GetRHIShaderResourceGroup()
+                                                                                         ->GetDeviceShaderResourceGroup(
+                                                                                             context.GetDeviceIndex())
+                                                                                         .get() };
                     for (const 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_arguments = mesh.m_drawArguments.GetDeviceDrawArguments(context.GetDeviceIndex());
+                        drawItem.m_pipelineState = m_modelPipelineStates[ShadowScope]->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                        auto deviceIndexBufferView{mesh.m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+                        drawItem.m_indexBufferView = &deviceIndexBufferView;
                         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();
-
+                        AZStd::vector<RHI::DeviceStreamBufferView> deviceQuadStreamBufferViews;
+                        for(const auto& streamBufferView : m_modelStreamBufferViews[ShadowScope])
+                        {
+                            deviceQuadStreamBufferViews.emplace_back(streamBufferView.GetDeviceStreamBufferView(context.GetDeviceIndex()));
+                        }
+                        drawItem.m_streamBufferViews = deviceQuadStreamBufferViews.data();
                         commandList->Submit(drawItem, i);
                     }
                 }
@@ -967,7 +993,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])
             {
@@ -981,7 +1007,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;
@@ -995,46 +1021,62 @@ 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;
+                    RHI::DeviceDrawItem drawItem;
                     drawItem.m_arguments = drawIndexed;
-                    drawItem.m_pipelineState = m_terrainPipelineStates[ForwardScope].get();
-                    drawItem.m_indexBufferView = &m_quadIndexBufferView;
+                    drawItem.m_pipelineState = m_terrainPipelineStates[ForwardScope]->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                    auto deviceIndexBufferView{m_quadIndexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+                    drawItem.m_indexBufferView = &deviceIndexBufferView;
                     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();
+                    AZStd::vector<RHI::DeviceStreamBufferView> deviceQuadStreamBufferViews;
+                    for(const auto& streamBufferView : m_quadStreamBufferViews[ForwardScope])
+                    {
+                        deviceQuadStreamBufferViews.emplace_back(streamBufferView.GetDeviceStreamBufferView(context.GetDeviceIndex()));
+                    }
+                    drawItem.m_streamBufferViews = deviceQuadStreamBufferViews.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())
                     {
-                        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_arguments = mesh.m_drawArguments.GetDeviceDrawArguments(context.GetDeviceIndex());
+                        drawItem.m_pipelineState = m_modelPipelineStates[ForwardScope]->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                        auto deviceIndexBufferView{mesh.m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+                        drawItem.m_indexBufferView = &deviceIndexBufferView;
                         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();
+                        AZStd::vector<RHI::DeviceStreamBufferView> deviceQuadStreamBufferViews;
+                        for(const auto& streamBufferView : m_modelStreamBufferViews[ForwardScope])
+                        {
+                            deviceQuadStreamBufferViews.emplace_back(streamBufferView.GetDeviceStreamBufferView(context.GetDeviceIndex()));
+                        }
+                        drawItem.m_streamBufferViews = deviceQuadStreamBufferViews.data();
 
                         commandList->Submit(drawItem, i);
                     }
@@ -1080,8 +1122,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])
             {
@@ -1095,8 +1137,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;
 
@@ -1111,7 +1153,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;
 
@@ -1154,7 +1196,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])
             {
@@ -1178,16 +1220,26 @@ namespace AtomSampleViewer
                 drawIndexed.m_indexCount = 6;
                 drawIndexed.m_instanceCount = 1;
 
-                const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[LuminanceMapScope][0]->GetRHIShaderResourceGroup() };
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[LuminanceMapScope][0]
+                                                                                     ->GetRHIShaderResourceGroup()
+                                                                                     ->GetDeviceShaderResourceGroup(
+                                                                                         context.GetDeviceIndex())
+                                                                                     .get() };
 
-                RHI::DrawItem drawItem;
+                RHI::DeviceDrawItem drawItem;
                 drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_luminancePipelineState.get();
-                drawItem.m_indexBufferView = &m_quadIndexBufferView;
+                drawItem.m_pipelineState = m_luminancePipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                auto deviceIndexBufferView{m_quadIndexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+                drawItem.m_indexBufferView = &deviceIndexBufferView;
                 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();
+                AZStd::vector<AZ::RHI::DeviceStreamBufferView> singleDeviceQuadStreamBufferViews;
+                for(const auto& streamBufferView : m_quadStreamBufferViews[LuminanceMapScope])
+                {
+                    singleDeviceQuadStreamBufferViews.emplace_back(streamBufferView.GetDeviceStreamBufferView(context.GetDeviceIndex()));
+                }
+                drawItem.m_streamBufferViews = singleDeviceQuadStreamBufferViews.data();
                 commandList->Submit(drawItem);
             }
         };
@@ -1252,8 +1304,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);
@@ -1265,8 +1317,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;
 
@@ -1280,7 +1332,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;
 

+ 7 - 3
Gem/Code/Source/RHI/BasicRHIComponent.cpp

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

+ 64 - 51
Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.cpp

@@ -217,28 +217,27 @@ namespace AtomSampleViewer
 
     void BindlessPrototypeExampleComponent::CreatePools()
     {
-        const RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
         //Create Buffer pool for read only buffers
         {
-            m_bufferPool = RHI::Factory::Get().CreateBufferPool();
+            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(*device, bufferPoolDesc);
+            [[maybe_unused]] RHI::ResultCode resultCode = m_bufferPool->Init(RHI::MultiDevice::AllDevices, bufferPoolDesc);
             AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to create Material Buffer Pool");
         }
 
         // Create Buffer pool for read write buffers
         {
-            m_computeBufferPool = RHI::Factory::Get().CreateBufferPool();
+            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(*device, bufferPoolDesc);
+            [[maybe_unused]] RHI::ResultCode result = m_computeBufferPool->Init(RHI::MultiDevice::AllDevices, bufferPoolDesc);
             AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialized compute buffer pool");
         }
 
@@ -246,15 +245,15 @@ namespace AtomSampleViewer
         {
             RHI::ImagePoolDescriptor imagePoolDesc;
             imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::ShaderReadWrite;
-            m_rwImagePool = RHI::Factory::Get().CreateImagePool();
-            [[maybe_unused]] RHI::ResultCode result = m_rwImagePool->Init(*device, imagePoolDesc);
+            m_rwImagePool = aznew RHI::ImagePool();
+            [[maybe_unused]] RHI::ResultCode result = m_rwImagePool->Init(RHI::MultiDevice::AllDevices, 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;
@@ -271,7 +270,7 @@ namespace AtomSampleViewer
         AZ::RHI::Ptr<AZ::RHI::BufferView>& bufferView, 
         size_t byteSize)
     {
-        indirectionBuffer = RHI::Factory::Get().CreateBuffer();
+        indirectionBuffer = aznew RHI::Buffer();
         indirectionBuffer->SetName(bufferName);
         RHI::BufferInitRequest bufferRequest;
         bufferRequest.m_descriptor.m_bindFlags = RHI::BufferBindFlags::ShaderRead;
@@ -282,7 +281,7 @@ namespace AtomSampleViewer
 
         RHI::BufferViewDescriptor viewDesc =
             RHI::BufferViewDescriptor::CreateRaw(0, aznumeric_cast<uint32_t>(bufferRequest.m_descriptor.m_byteCount));
-        bufferView = indirectionBuffer->GetBufferView(viewDesc);
+        bufferView = indirectionBuffer->BuildBufferView(viewDesc);
     }
 
     void BindlessPrototypeExampleComponent::CreateColorBuffer(
@@ -297,7 +296,7 @@ namespace AtomSampleViewer
         randColors[2] = colorVal.GetZ();
         randColors[3] = colorVal.GetW();
 
-        buffer = RHI::Factory::Get().CreateBuffer();
+        buffer = aznew RHI::Buffer();
         buffer->SetName(bufferName);
         RHI::BufferInitRequest bufferRequest;
         bufferRequest.m_descriptor.m_bindFlags = RHI::BufferBindFlags::ShaderRead;
@@ -309,7 +308,7 @@ namespace AtomSampleViewer
 
         RHI::BufferViewDescriptor viewDesc =
             RHI::BufferViewDescriptor::CreateRaw(0, aznumeric_cast<uint32_t>(bufferRequest.m_descriptor.m_byteCount));
-        bufferView = buffer->GetBufferView(viewDesc);
+        bufferView = buffer->BuildBufferView(viewDesc);
     }
 
     void BindlessPrototypeExampleComponent::ClearObjects()
@@ -347,7 +346,7 @@ namespace AtomSampleViewer
             auto uvAssetBufferView{ m_model->GetModelAsset()->GetLodAssets()[lodModel]->GetMeshes()[subMeshIdx].GetSemanticBufferAssetView(
                 AZ::Name{ "UV" }) };
             auto rpiUVBuffer{ AZ::RPI::Buffer::FindOrCreate(uvAssetBufferView->GetBufferAsset()) };
-            const RHI::BufferView* uvBufferView = rpiUVBuffer->GetBufferView();
+            const auto* uvBufferView = rpiUVBuffer->GetBufferView();
             uint32_t uvBufferByteOffset =
                 uvAssetBufferView->GetBufferViewDescriptor().m_elementSize * uvAssetBufferView->GetBufferViewDescriptor().m_elementOffset;
 
@@ -356,7 +355,7 @@ namespace AtomSampleViewer
 
             subMeshInstance.m_perSubMeshSrg = CreateShaderResourceGroup(m_shader, "HandleSrg", InternalBP::SampleName);
             subMeshInstance.m_mesh = &mesh;
-            subMeshInstance.m_uvBufferIndex = uvBufferView->GetBindlessReadIndex();
+            subMeshInstance.m_uvBufferIndex = uvBufferView->GetDeviceBufferView(RHI::MultiDevice::DefaultDeviceIndex)->GetBindlessReadIndex();
             subMeshInstance.m_uvBufferByteOffset = uvBufferByteOffset;
 
             // Set the buffer stream
@@ -507,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
@@ -559,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);
@@ -568,7 +562,6 @@ namespace AtomSampleViewer
         }
 
         // Load appropriate cubemap textures used by the unbounded texture array
-        AZStd::vector<const RHI::ImageView*> cubemapImageViews;
         for (uint32_t textureIdx = 0u; textureIdx < InternalBP::CubeMapImageCount; textureIdx++)
         {
             AZ::Data::Instance<AZ::RPI::StreamingImage> image = LoadStreamingImage(InternalBP::CubeMapImages[textureIdx], InternalBP::SampleName);
@@ -587,7 +580,7 @@ namespace AtomSampleViewer
 
         // Set the color multiplier buffer
         {
-            m_computeBuffer = RHI::Factory::Get().CreateBuffer();
+            m_computeBuffer = aznew RHI::Buffer();
             m_computeBuffer->SetName(Name("m_colorBufferMultiplier"));
             uint32_t bufferSize = sizeof(uint32_t);//RHI ::GetFormatSize(RHI::Format::R32G32B32A32_FLOAT);
 
@@ -598,12 +591,12 @@ namespace AtomSampleViewer
             AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialized compute buffer");
 
             m_rwBufferViewDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, bufferSize);
-            m_computeBufferView = m_computeBuffer->GetBufferView(m_rwBufferViewDescriptor);
+            m_computeBufferView = m_computeBuffer->BuildBufferView(m_rwBufferViewDescriptor);
         }
 
         // Set the image version of color multiplier buffer
         {
-            m_computeImage = RHI::Factory::Get().CreateImage();
+            m_computeImage = aznew RHI::Image();
 
             RHI::ImageInitRequest request;
             request.m_image = m_computeImage.get();
@@ -613,7 +606,7 @@ namespace AtomSampleViewer
             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->GetImageView(m_rwImageViewDescriptor);
+            m_computeImageView = m_computeImage->BuildImageView(m_rwImageViewDescriptor);
         }
 
 #if ATOMSAMPLEVIEWER_TRAIT_BINDLESS_PROTOTYPE_SUPPORTS_DIRECT_BOUND_UNBOUNDED_ARRAY
@@ -778,7 +771,7 @@ namespace AtomSampleViewer
             uint32_t arrayIndex = 0;
             auto indirectionBufferIndex = indirectionBufferSrg->FindShaderInputBufferIndex(AZ::Name{ "m_imageIndirectionBuffer" });
             indirectionBufferSrg->SetBindlessViews(
-                indirectionBufferIndex, m_imageIndirectionBufferView.get(), views, static_cast<uint32_t*>(mapResponse.m_data),
+                indirectionBufferIndex, m_imageIndirectionBufferView.get(), views, static_cast<uint32_t*>(mapResponse.m_data[RHI::MultiDevice::DefaultDeviceIndex]),
                 isViewReadOnly, arrayIndex);
 
             m_bufferPool->UnmapBuffer(*m_imageIndirectionBuffer);
@@ -807,7 +800,7 @@ namespace AtomSampleViewer
             uint32_t arrayIndex = 0;
             auto indirectionBufferIndex = indirectionBufferSrg->FindShaderInputBufferIndex(AZ::Name{ "m_bufferIndirectionBuffer" });
             indirectionBufferSrg->SetBindlessViews(
-                indirectionBufferIndex, m_bufferIndirectionBufferView.get(), views, static_cast<uint32_t*>(mapResponse.m_data),
+                indirectionBufferIndex, m_bufferIndirectionBufferView.get(), views, static_cast<uint32_t*>(mapResponse.m_data[RHI::MultiDevice::DefaultDeviceIndex]),
                 isViewReadOnly, arrayIndex);
 
             m_bufferPool->UnmapBuffer(*m_bufferIndirectionBuffer);
@@ -921,9 +914,12 @@ 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;
         }
@@ -1052,10 +1048,10 @@ namespace AtomSampleViewer
             commandList->SetViewports(&m_viewport, 1);
             commandList->SetScissors(&m_scissor, 1);
 
-            AZStd::array<const RHI::ShaderResourceGroup*, 8> shaderResourceGroups;
-            shaderResourceGroups[0] = m_bufferDispatchSRG->GetRHIShaderResourceGroup();
+            AZStd::array<const RHI::DeviceShaderResourceGroup*, 8> shaderResourceGroups;
+            shaderResourceGroups[0] = m_bufferDispatchSRG->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
 
-            RHI::DispatchItem dispatchItem;
+            RHI::DeviceDispatchItem dispatchItem;
             RHI::DispatchDirect dispatchArgs;
 
             dispatchArgs.m_threadsPerGroupX = aznumeric_cast<uint16_t>(m_bufferNumThreadsX);
@@ -1066,7 +1062,7 @@ namespace AtomSampleViewer
             dispatchArgs.m_totalNumberOfThreadsZ = 1;
 
             dispatchItem.m_arguments = dispatchArgs;
-            dispatchItem.m_pipelineState = m_bufferDispatchPipelineState.get();
+            dispatchItem.m_pipelineState = m_bufferDispatchPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
             dispatchItem.m_shaderResourceGroupCount = 1;
             dispatchItem.m_shaderResourceGroups = shaderResourceGroups;
 
@@ -1125,10 +1121,10 @@ namespace AtomSampleViewer
             commandList->SetViewports(&m_viewport, 1);
             commandList->SetScissors(&m_scissor, 1);
 
-            AZStd::array<const RHI::ShaderResourceGroup*, 8> shaderResourceGroups;
-            shaderResourceGroups[0] = m_imageDispatchSRG->GetRHIShaderResourceGroup();
+            AZStd::array<const RHI::DeviceShaderResourceGroup*, 8> shaderResourceGroups;
+            shaderResourceGroups[0] = m_imageDispatchSRG->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get();
 
-            RHI::DispatchItem dispatchItem;
+            RHI::DeviceDispatchItem dispatchItem;
             RHI::DispatchDirect dispatchArgs;
 
             dispatchArgs.m_threadsPerGroupX = aznumeric_cast<uint16_t>(m_imageNumThreadsX);
@@ -1139,7 +1135,7 @@ namespace AtomSampleViewer
             dispatchArgs.m_totalNumberOfThreadsZ = 1;
 
             dispatchItem.m_arguments = dispatchArgs;
-            dispatchItem.m_pipelineState = m_imageDispatchPipelineState.get();
+            dispatchItem.m_pipelineState = m_imageDispatchPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
             dispatchItem.m_shaderResourceGroupCount = 1;
             dispatchItem.m_shaderResourceGroups = shaderResourceGroups;
 
@@ -1264,24 +1260,41 @@ namespace AtomSampleViewer
                 {
                     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(),
+                    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(),
+                        m_bindlessSrg->GetSrg(m_imageSrgName)
+                            ->GetRHIShaderResourceGroup()
+                            ->GetDeviceShaderResourceGroup(context.GetDeviceIndex())
+                            .get(),
 #endif
-                        m_bindlessSrg->GetSrg(m_indirectionBufferSrgName)->GetRHIShaderResourceGroup(),
+                        m_bindlessSrg->GetSrg(m_indirectionBufferSrgName)
+                            ->GetRHIShaderResourceGroup()
+                            ->GetDeviceShaderResourceGroup(context.GetDeviceIndex())
+                            .get(),
                     };
-                    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;
+                    RHI::DeviceDrawItem drawItem;
+                    drawItem.m_arguments = subMesh.m_mesh->m_drawArguments.GetDeviceDrawArguments(context.GetDeviceIndex());
+                    drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                    auto deviceIndexBufferView{subMesh.m_mesh->m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+                    drawItem.m_indexBufferView = &deviceIndexBufferView;
                     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();
+                    AZStd::vector<RHI::DeviceStreamBufferView> deviceQuadStreamBufferViews;
+                    for(const auto& streamBufferView : subMesh.bufferStreamViewArray)
+                    {
+                        deviceQuadStreamBufferViews.emplace_back(streamBufferView.GetDeviceStreamBufferView(context.GetDeviceIndex()));
+                    }
+                    drawItem.m_streamBufferViews = deviceQuadStreamBufferViews.data();
 
                     // Submit the triangle draw item.
                     commandList->Submit(drawItem, instanceIdx);

+ 27 - 21
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(RHI::MultiDevice::AllDevices, 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();
@@ -235,17 +233,17 @@ namespace AtomSampleViewer
         RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
 
         [[maybe_unused]] RHI::ResultCode result = RHI::ResultCode::Success;
-        m_computeBufferPool = RHI::Factory::Get().CreateBufferPool();
+        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(RHI::MultiDevice::AllDevices, 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 +254,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");
 
     }
 
@@ -309,11 +307,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 +322,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;
 
@@ -398,14 +396,22 @@ namespace AtomSampleViewer
             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;
+            RHI::DeviceDrawItem drawItem;
             drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_drawPipelineState.get();
-            drawItem.m_indexBufferView = &m_indexBufferView;
+            drawItem.m_pipelineState = m_drawPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+            auto deviceIndexBufferView{m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+            drawItem.m_indexBufferView = &deviceIndexBufferView;
             drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
-            drawItem.m_streamBufferViews = m_streamBufferViews.data();
+            AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+            };
+            drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
             drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
             drawItem.m_shaderResourceGroups = shaderResourceGroups;
 

+ 17 - 16
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(RHI::MultiDevice::AllDevices, 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;
@@ -226,28 +226,29 @@ 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::DeviceIndexBufferView indexBufferView = { *m_indexBuffer->GetDeviceBuffer(context.GetDeviceIndex()), 0,
+                                                                     indexBufSize, RHI::IndexFormat::Uint16 };
 
                 RHI::DrawIndexed drawIndexed;
                 drawIndexed.m_indexCount = 6;
                 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;
+                RHI::DeviceDrawItem drawItem;
                 drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_pipelineState.get();
+                drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_indexBufferView = &indexBufferView;
                 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();
+                AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                    m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                    m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+                };
+                drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
                 commandList->Submit(drawItem);
             };

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

@@ -93,7 +93,6 @@ namespace AtomSampleViewer
 
         BufferData m_bufferData;
 
-        AZ::RHI::DrawItem m_drawItem;
         AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
 
         static const int numberOfPaths = 3;

+ 15 - 8
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(RHI::MultiDevice::AllDevices, 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();
@@ -204,14 +204,21 @@ namespace AtomSampleViewer
             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;
+            RHI::DeviceDrawItem drawItem;
             drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_pipelineState.get();
-            drawItem.m_indexBufferView = &m_indexBufferView;
+            drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+            auto deviceIndexBufferView{m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+            drawItem.m_indexBufferView = &deviceIndexBufferView;
             drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
-            drawItem.m_streamBufferViews = m_streamBufferViews.data();
+            AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+            };
+            drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
             drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
             drawItem.m_shaderResourceGroups = shaderResourceGroups;
 

+ 53 - 42
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(RHI::MultiDevice::AllDevices, 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();
@@ -332,12 +332,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(RHI::MultiDevice::AllDevices, bufferPoolDesc);
 
         // Create the layout depending on which commands are supported by the device.
         m_indirectDrawBufferLayout = RHI::IndirectBufferLayout();
@@ -357,11 +357,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 +369,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 +380,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)
         {
@@ -450,7 +450,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 +459,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 +469,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 +497,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 +525,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(RHI::MultiDevice::AllDevices, bufferPoolDesc);
 
-        m_instancesDataBuffer = RHI::Factory::Get().CreateBuffer();
+        m_instancesDataBuffer = aznew RHI::Buffer();
 
         RHI::BufferInitRequest request;
         request.m_buffer = m_instancesDataBuffer.get();
@@ -543,7 +543,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 +556,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(RHI::MultiDevice::AllDevices, 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 +619,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);
         };
 
@@ -687,7 +687,7 @@ namespace AtomSampleViewer
         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];
@@ -696,10 +696,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;
             }
 
@@ -714,7 +714,7 @@ namespace AtomSampleViewer
             uint32_t stride = m_indirectDrawBufferSignature->GetByteStride();
             m_indirectDrawBufferView =
             {
-                culledBufferView->GetBuffer(),
+                *(culledBufferView->GetBuffer()),
                 *m_indirectDrawBufferSignature,
                 0,
                 stride * m_numObjects,
@@ -726,22 +726,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);
@@ -834,7 +835,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.
@@ -848,14 +851,19 @@ namespace AtomSampleViewer
                 m_drawIndirect.m_indirectBufferByteOffset = i * m_indirectDrawBufferView.GetByteStride();
                 m_drawIndirect.m_indirectBufferView = &m_indirectDrawBufferView;
 
-                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_arguments = AZ::RHI::DrawArguments(m_drawIndirect).GetDeviceDrawArguments(context.GetDeviceIndex());
+                drawItem.m_pipelineState = m_drawPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                auto deviceIndexBufferView{m_indexBufferViews[0].GetDeviceIndexBufferView(context.GetDeviceIndex())};
+                drawItem.m_indexBufferView = &deviceIndexBufferView;
                 drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                 drawItem.m_shaderResourceGroups = shaderResourceGroups;
                 drawItem.m_streamBufferViewCount = 2;
-                drawItem.m_streamBufferViews = m_streamBufferViews.data();
+                AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                    m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                    m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+                };
+                drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
                 // Submit the indirect draw item.
                 commandList->Submit(drawItem);
@@ -1033,9 +1041,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);
         }
     }

+ 20 - 17
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(RHI::MultiDevice::AllDevices, bufferPoolDesc);
 
-        m_inputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
+        m_inputAssemblyBuffer = aznew RHI::Buffer();
 
         RHI::BufferInitRequest request;
         request.m_buffer = m_inputAssemblyBuffer.get();
@@ -268,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();
@@ -286,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);
@@ -298,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);
             }
         };
@@ -368,20 +368,20 @@ 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_streamBufferView[0] = {*(inputAssemblyBufferView->GetBuffer()), 0, sizeof(BufferData), sizeof(BufferData::value_type)};
 
                     RHI::ValidateStreamBufferViews(m_inputStreamLayout, AZStd::span<const RHI::StreamBufferView>(&m_streamBufferView[0], 1));
                 }
             }
 
             {
-                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_streamBufferView[1] = {*(inputAssemblyBufferView->GetBuffer()), 0, sizeof(BufferData), sizeof(BufferData::value_type)};
 
                     RHI::ValidateStreamBufferViews(m_inputStreamLayout, AZStd::span<const RHI::StreamBufferView>(&m_streamBufferView[1], 1));
                 }
@@ -400,18 +400,21 @@ namespace AtomSampleViewer
             RHI::DrawLinear drawLinear;
             drawLinear.m_vertexCount = BufferData::array_size;
 
-            RHI::DrawItem drawItem;
+            RHI::DeviceDrawItem drawItem;
             drawItem.m_arguments = drawLinear;
-            drawItem.m_pipelineState = m_drawPipelineState.get();
+            drawItem.m_pipelineState = m_drawPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
             drawItem.m_indexBufferView = nullptr;
             drawItem.m_streamBufferViewCount = 1;
             drawItem.m_shaderResourceGroupCount = 1;
 
             for (uint32_t index = context.GetSubmitRange().m_startIndex; index < context.GetSubmitRange().m_endIndex; ++index)
             {
-                drawItem.m_streamBufferViews = &m_streamBufferView[index];
+                auto deviceStreamBufferView{m_streamBufferView[index].GetDeviceStreamBufferView(context.GetDeviceIndex())};
+                drawItem.m_streamBufferViews = &deviceStreamBufferView;
 
-                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);

+ 30 - 18
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(RHI::MultiDevice::AllDevices, 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;
 
@@ -292,16 +290,23 @@ namespace AtomSampleViewer
             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;
+            RHI::DeviceDrawItem drawItem;
             drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_pipelineStates[0].get();
-            drawItem.m_indexBufferView = &m_indexBufferView;
+            drawItem.m_pipelineState = m_pipelineStates[0]->GetDevicePipelineState(context.GetDeviceIndex()).get();
+            auto deviceIndexBufferView{m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+            drawItem.m_indexBufferView = &deviceIndexBufferView;
             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();
+            AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+            };
+            drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
             commandList->Submit(drawItem);
         };
@@ -350,9 +355,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);
@@ -372,16 +377,23 @@ namespace AtomSampleViewer
             drawIndexed.m_indexCount = 6;
             drawIndexed.m_instanceCount = 1;
 
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[1]->GetRHIShaderResourceGroup() };
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_shaderResourceGroups[1]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+            };
 
-            RHI::DrawItem drawItem;
+            RHI::DeviceDrawItem drawItem;
             drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_pipelineStates[1].get();
-            drawItem.m_indexBufferView = &m_indexBufferView;
+            drawItem.m_pipelineState = m_pipelineStates[1]->GetDevicePipelineState(context.GetDeviceIndex()).get();
+            auto deviceIndexBufferView{m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+            drawItem.m_indexBufferView = &deviceIndexBufferView;
             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();
+            AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+            };
+            drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
             commandList->Submit(drawItem);
         };

+ 0 - 2
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>
@@ -83,7 +82,6 @@ namespace AtomSampleViewer
         AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
         AZ::RHI::IndexBufferView m_indexBufferView;
 
-        AZ::RHI::DrawItem m_drawItem;
         AZStd::array<AZ::RHI::AttachmentId, 3> m_attachmentID;
         AZ::RHI::ClearValue m_clearValue;
         float m_time;

+ 32 - 27
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(RHI::MultiDevice::AllDevices, 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();
@@ -189,7 +189,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;
@@ -339,28 +339,30 @@ 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::DeviceIndexBufferView indexBufferView = { *m_triangleInputAssemblyBuffer->GetDeviceBuffer(context.GetDeviceIndex()),
+                                                                 offsetof(TriangleBufferData, m_indices),
+                                                                 sizeof(TriangleBufferData::m_indices), RHI::IndexFormat::Uint16 };
 
             RHI::DrawIndexed drawIndexed;
             drawIndexed.m_indexCount = 3;
             drawIndexed.m_instanceCount = 1;
 
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_triangleShaderResourceGroup->GetRHIShaderResourceGroup() };
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_triangleShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+            };
 
-            RHI::DrawItem drawItem;
+            RHI::DeviceDrawItem drawItem;
             drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_pipelineStates[msaaTypeIndex].get();
+            drawItem.m_pipelineState = m_pipelineStates[msaaTypeIndex]->GetDevicePipelineState(context.GetDeviceIndex()).get();
             drawItem.m_indexBufferView = &indexBufferView;
             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();
+            AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                m_triangleStreamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                m_triangleStreamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+            };
+            drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
             // Submit the triangle draw item.
             commandList->Submit(drawItem);
@@ -439,7 +441,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,28 +454,31 @@ 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
-            };
+            const RHI::DeviceIndexBufferView indexBufferView = { *m_quadInputAssemblyBuffer->GetDeviceBuffer(context.GetDeviceIndex()),
+                                                                 offsetof(QuadBufferData, m_indices), sizeof(QuadBufferData::m_indices),
+                                                                 RHI::IndexFormat::Uint16 };
 
             RHI::DrawIndexed drawIndexed;
             drawIndexed.m_indexCount = 6;
             drawIndexed.m_instanceCount = 1;
 
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_customMSAAResolveShaderResourceGroup->GetRHIShaderResourceGroup() };
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = { m_customMSAAResolveShaderResourceGroup
+                                                                                 ->GetRHIShaderResourceGroup()
+                                                                                 ->GetDeviceShaderResourceGroup(context.GetDeviceIndex())
+                                                                                 .get() };
 
-            RHI::DrawItem drawItem;
+            RHI::DeviceDrawItem drawItem;
             drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_customResolveMSAAPipelineState.get();
+            drawItem.m_pipelineState = m_customResolveMSAAPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
             drawItem.m_indexBufferView = &indexBufferView;
             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();
+            AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                m_quadStreamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                m_quadStreamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+            };
+            drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
             // Submit the triangle draw item.
             commandList->Submit(drawItem);

+ 16 - 14
Gem/Code/Source/RHI/MatrixAlignmentTestExampleComponent.cpp

@@ -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(RHI::MultiDevice::AllDevices, 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();
@@ -457,28 +457,30 @@ 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::DeviceIndexBufferView indexBufferView = { *m_inputAssemblyBuffer->GetDeviceBuffer(context.GetDeviceIndex()),
+                                                                     offsetof(BufferData, m_indices), sizeof(BufferData::m_indices),
+                                                                     RHI::IndexFormat::Uint16 };
 
                 RHI::DrawIndexed drawIndexed;
                 drawIndexed.m_indexCount = 6;
                 drawIndexed.m_instanceCount = 2;
 
-                const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroup->GetRHIShaderResourceGroup() };
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                    m_shaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+                };
 
-                RHI::DrawItem drawItem;
+                RHI::DeviceDrawItem drawItem;
                 drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_pipelineState.get();
+                drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_indexBufferView = &indexBufferView;
                 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();
+                AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                    m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                    m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+                };
+                drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
                 // Submit the triangle draw item.
                 commandList->Submit(drawItem);

+ 0 - 2
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>
@@ -177,7 +176,6 @@ namespace AtomSampleViewer
         };
 
         AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
-        AZ::RHI::DrawItem m_drawItem;
 
         // ImGui stuff.
         ImGuiSidebar m_imguiSidebar;

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

@@ -0,0 +1,714 @@
+/*
+ * 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
+            {
+                m_image = aznew RHI::Image;
+                RHI::ImageInitRequest initImageRequest;
+                initImageRequest.m_image = m_image.get();
+                initImageRequest.m_descriptor = RHI::ImageDescriptor::Create2D(
+                    RHI::ImageBindFlags::Color | RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead, m_outputWidth, m_outputHeight, m_outputFormat);
+                m_imagePool->InitImage(initImageRequest);
+            }
+
+            // Image holds rendered texture from GPU1 (on GPU0)
+            {
+                m_transferImage = aznew RHI::Image;
+                RHI::ImageInitRequest initImageRequest;
+                initImageRequest.m_image = m_transferImage.get();
+                initImageRequest.m_descriptor = RHI::ImageDescriptor::Create2D(
+                    RHI::ImageBindFlags::Color | RHI::ImageBindFlags::ShaderRead | RHI::ImageBindFlags::CopyWrite, m_outputWidth, m_outputHeight,
+                    m_outputFormat);
+                m_transferImagePool->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)};
+                //? Check BindFlags
+                request.m_initialData = initialData.data();
+                if (m_stagingBufferPoolToGPU->InitBuffer(request) != RHI::ResultCode::Success)
+                {
+                    AZ_Error("MultiGPUExampleComponent", false, "StagingBufferToGPU was not created");
+                }
+            }
+
+            {
+                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) }; //? Check BindFlags
+                if (m_stagingBufferPoolToCPU->InitBuffer(request) != 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_image);
+
+        frameGraphBuilder.GetAttachmentDatabase().ImportImage(
+            m_imageAttachmentIds[1], m_transferImage);
+
+        frameGraphBuilder.GetAttachmentDatabase().ImportBuffer(
+            m_bufferAttachmentIds[0], m_stagingBufferToGPU);
+
+        frameGraphBuilder.GetAttachmentDatabase().ImportBuffer(
+            m_bufferAttachmentIds[1], m_stagingBufferToCPU);
+
+        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
+
+        // ImagePool for the render target texture
+        {
+            m_imagePool = aznew RHI::ImagePool;
+            m_imagePool->SetName(Name("RenderTexturePool"));
+
+            RHI::ImagePoolDescriptor imagePoolDescriptor{};
+            imagePoolDescriptor.m_bindFlags =
+                RHI::ImageBindFlags::Color | RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead | RHI::ImageBindFlags::CopyWrite;
+
+            if (m_imagePool->Init(m_deviceMask, imagePoolDescriptor) != RHI::ResultCode::Success)
+            {
+                AZ_Error("MultiGPUExampleComponent", false, "Failed to initialize render texture image pool.");
+                return;
+            }
+        }
+
+        // ImagePool used to transfer the rendered texture from GPU 1 -> GPU 0
+        {
+            m_transferImagePool = aznew RHI::ImagePool;
+            m_transferImagePool->SetName(Name("TransferImagePool"));
+
+            RHI::ImagePoolDescriptor imagePoolDescriptor{};
+            imagePoolDescriptor.m_bindFlags =
+                RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead | RHI::ImageBindFlags::CopyWrite;
+
+            if (m_transferImagePool->Init(m_deviceMask_1, imagePoolDescriptor) != RHI::ResultCode::Success)
+            {
+                AZ_Error("MultiGPUExampleComponent", false, "Failed to initialize transfer 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;
+            if (m_stagingBufferPoolToGPU->Init(m_deviceMask_1, bufferPoolDesc) != RHI::ResultCode::Success)
+            {
+                AZ_Error("MultiGPUExampleComponent", false, "StagingBufferPoolToGPU was not initialized");
+            }
+        }
+
+        // Create staging buffer pools 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;
+            if (m_stagingBufferPoolToCPU->Init(m_deviceMask_2, bufferPoolDesc) != RHI::ResultCode::Success)
+            {
+                AZ_Error("MultiGPUExampleComponent", false, "StagingBufferPoolToCPU was not created");
+            }
+        }
+
+        // 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_stagingBufferToGPU = nullptr;
+        m_inputAssemblyBufferPoolComposite = nullptr;
+        m_inputAssemblyBufferComposite = nullptr;
+        m_pipelineStateComposite = nullptr;
+        m_shaderResourceGroupComposite = nullptr;
+        m_shaderResourceGroupDataComposite = RHI::ShaderResourceGroupData{};
+        m_shaderResourceGroupPoolComposite = nullptr;
+
+        m_stagingBufferPoolToCPU = nullptr;
+        m_stagingBufferToCPU = 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;
+            m_inputAssemblyBufferPool->Init(m_deviceMask, 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_streamBufferViews[0] = { *m_inputAssemblyBuffer,
+                                       offsetof(BufferDataTrianglePass, m_positions), sizeof(BufferDataTrianglePass::m_positions),
+                                       sizeof(VertexPosition) };
+
+            m_streamBufferViews[1] = { *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_streamBufferViews);
+        }
+
+        {
+            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 a scope for rendering 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 white.
+                RHI::ImageScopeAttachmentDescriptor descriptor;
+                descriptor.m_attachmentId = m_imageAttachmentIds[0];
+                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::DeviceIndexBufferView indexBufferView = { *m_inputAssemblyBuffer->GetDeviceBuffer(context.GetDeviceIndex()),
+                                                                     offsetof(BufferDataTrianglePass, m_indices),
+                                                                     sizeof(BufferDataTrianglePass::m_indices), RHI::IndexFormat::Uint16 };
+
+                RHI::DrawIndexed drawIndexed;
+                drawIndexed.m_indexCount = 3;
+                drawIndexed.m_instanceCount = 1;
+
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                    m_shaderResourceGroupShared->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+                };
+
+                RHI::DeviceDrawItem drawItem;
+                drawItem.m_arguments = drawIndexed;
+                drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                drawItem.m_indexBufferView = &indexBufferView;
+                drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
+                drawItem.m_shaderResourceGroups = shaderResourceGroups;
+                drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
+                AZStd::array<RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                    m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                    m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+                };
+                drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
+
+                // Submit the triangle draw item.
+                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
+        {
+            m_inputAssemblyBufferPoolComposite = aznew RHI::BufferPool();
+
+            RHI::BufferPoolDescriptor bufferPoolDesc;
+            bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
+            bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
+            m_inputAssemblyBufferPoolComposite->Init(m_deviceMask_1, bufferPoolDesc);
+
+            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;
+            m_inputAssemblyBufferPoolComposite->InitBuffer(request);
+
+            m_streamBufferViewsComposite[0] = { *m_inputAssemblyBufferComposite,
+                                                offsetof(BufferDataCompositePass, m_positions),
+                                                sizeof(BufferDataCompositePass::m_positions), sizeof(VertexPosition) };
+
+            m_streamBufferViewsComposite[1] = { *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_streamBufferViewsComposite);
+        }
+
+        // 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();
+
+            m_shaderResourceGroupPoolComposite = aznew RHI::ShaderResourceGroupPool;
+            m_shaderResourceGroupPoolComposite->Init(m_deviceMask_1, 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);
+
+                frameGraph.ExecuteAfter(RHI::ScopeId{ "MultiGPUTriangle0" });
+                frameGraph.ExecuteAfter(RHI::ScopeId{ "MultiGPUCopyToGPU" });
+            };
+
+            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::DeviceIndexBufferView indexBufferView = {
+                    *m_inputAssemblyBufferComposite->GetDeviceBuffer(context.GetDeviceIndex()),
+                    offsetof(BufferDataCompositePass, m_indices), sizeof(BufferDataCompositePass::m_indices), RHI::IndexFormat::Uint16
+                };
+
+                RHI::DrawIndexed drawIndexed;
+                drawIndexed.m_indexCount = 6;
+                drawIndexed.m_instanceCount = 1;
+
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                    m_shaderResourceGroupComposite->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+                };
+
+                RHI::DeviceDrawItem drawItem;
+                drawItem.m_arguments = drawIndexed;
+                drawItem.m_pipelineState = m_pipelineStateComposite->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                drawItem.m_indexBufferView = &indexBufferView;
+                drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
+                drawItem.m_shaderResourceGroups = shaderResourceGroups;
+                drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViewsComposite.size());
+                AZStd::array<RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                    m_streamBufferViewsComposite[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                    m_streamBufferViewsComposite[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+                };
+                drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
+
+                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_destinationImage = m_transferImage->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::BufferScopeAttachmentDescriptor descriptor{};
+                descriptor.m_attachmentId = m_bufferAttachmentIds[1];
+                descriptor.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, static_cast<uint32_t>(m_stagingBufferToCPU->GetDescriptor().m_byteCount));
+                descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::DontCare;
+                descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::Store;
+                frameGraph.UseCopyAttachment(descriptor, RHI::ScopeAttachmentAccess::Write);
+            }
+
+            {
+                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.UseCopyAttachment(descriptor, RHI::ScopeAttachmentAccess::Read);
+            }
+
+            frameGraph.ExecuteAfter(RHI::ScopeId{ "MultiGPUTriangle1" });
+        };
+
+        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_image->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

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

@@ -0,0 +1,130 @@
+/*
+ * 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;
+        };
+
+        AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
+
+        AZ::RHI::Ptr<AZ::RHI::ImagePool> m_imagePool{};
+        AZ::RHI::Ptr<AZ::RHI::Image> m_image{};
+        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("MultiGPUBufferToGPU"),
+                                                                          AZ::RHI::AttachmentId("MultiGPUBufferToCPU") } };
+        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::ImagePool> m_transferImagePool{};
+        AZ::RHI::Ptr<AZ::RHI::Image> m_transferImage{};
+        AZ::RHI::Ptr<AZ::RHI::BufferPool> m_inputAssemblyBufferPoolComposite{};
+        AZ::RHI::Ptr<AZ::RHI::Buffer> m_inputAssemblyBufferComposite{};
+        AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViewsComposite;
+        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

+ 15 - 8
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(AZ::RHI::MultiDevice::AllDevices, 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();
@@ -328,16 +328,23 @@ 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() };
+                const AZ::RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                    m_shaderResourceGroups[i]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+                };
 
-                AZ::RHI::DrawItem drawItem;
+                AZ::RHI::DeviceDrawItem drawItem;
                 drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_pipelineState.get();
-                drawItem.m_indexBufferView = &m_indexBufferView;
+                drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                auto deviceIndexBufferView{m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+                drawItem.m_indexBufferView = &deviceIndexBufferView;
                 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();
+                AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                    m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                    m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+                };
+                drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
                 commandList->Submit(drawItem, i);
             }

+ 30 - 14
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(RHI::MultiDevice::AllDevices, 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;
 
@@ -422,16 +422,24 @@ namespace AtomSampleViewer
             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;
+            AZ::RHI::DeviceDrawItem drawItem;
             drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_pipelineStates[0].get();
-            drawItem.m_indexBufferView = &m_indexBufferView;
+            drawItem.m_pipelineState = m_pipelineStates[0]->GetDevicePipelineState(context.GetDeviceIndex()).get();
+            auto deviceIndexBufferView{m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+            drawItem.m_indexBufferView = &deviceIndexBufferView;
             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();
+            AZStd::array<AZ::RHI::DeviceStreamBufferView, 3> deviceStreamBufferViews{
+                m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                m_streamBufferViews[2].GetDeviceStreamBufferView(context.GetDeviceIndex())
+            };
+            drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
             commandList->Submit(drawItem);
         };
@@ -497,7 +505,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();
@@ -515,16 +523,24 @@ namespace AtomSampleViewer
             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;
+            AZ::RHI::DeviceDrawItem drawItem;
             drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_pipelineStates[1].get();
-            drawItem.m_indexBufferView = &m_indexBufferView;
+            drawItem.m_pipelineState = m_pipelineStates[1]->GetDevicePipelineState(context.GetDeviceIndex()).get();
+            auto deviceIndexBufferView{m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+            drawItem.m_indexBufferView = &deviceIndexBufferView;
             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();
+            AZStd::array<AZ::RHI::DeviceStreamBufferView, 3> deviceStreamBufferViews{
+                m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                m_streamBufferViews[2].GetDeviceStreamBufferView(context.GetDeviceIndex())
+            };
+            drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
             commandList->Submit(drawItem);
         };

+ 0 - 2
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>
 
@@ -105,7 +104,6 @@ namespace AtomSampleViewer
         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;
     };

+ 35 - 35
Gem/Code/Source/RHI/QueryExampleComponent.cpp

@@ -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(RHI::MultiDevice::AllDevices, 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;
@@ -304,16 +304,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);
         };
@@ -432,26 +432,26 @@ 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::DeviceIndexBufferView quadIndexBufferView = {
+                    *m_quadInputAssemblyBuffer->GetDeviceBuffer(context.GetDeviceIndex()), offsetof(BufferData, m_indices),
+                    sizeof(BufferData::m_indices), RHI::IndexFormat::Uint16
                 };
 
                 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;
+                RHI::DeviceDrawItem drawItem;
                 drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_quadPipelineState.get();
+                drawItem.m_pipelineState = m_quadPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_indexBufferView = &quadIndexBufferView;
                 drawItem.m_streamBufferViewCount = 1;
-                drawItem.m_streamBufferViews = &m_quadStreamBufferView;
+                auto deviceStreamBufferView{m_quadStreamBufferView.GetDeviceStreamBufferView(context.GetDeviceIndex())};
+                drawItem.m_streamBufferViews = &deviceStreamBufferView;
                 drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                 drawItem.m_shaderResourceGroups = shaderResourceGroups;
 
@@ -459,13 +459,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)
@@ -488,7 +488,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;
@@ -497,28 +497,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;
             }
         };
@@ -556,8 +556,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(RHI::MultiDevice::AllDevices, queryPoolDesc);
         if (result != RHI::ResultCode::Success)
         {
             AZ_Assert(false, "Failed to createa query pool");
@@ -566,7 +566,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());
         }
     }
@@ -592,14 +592,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(RHI::MultiDevice::AllDevices, bufferPoolDesc);
 
-        m_predicationBuffer = RHI::Factory::Get().CreateBuffer();
+        m_predicationBuffer = aznew RHI::Buffer();
 
         RHI::BufferInitRequest request;
         request.m_buffer = m_predicationBuffer.get();

+ 46 - 48
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(RHI::MultiDevice::AllDevices, 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(RHI::MultiDevice::AllDevices, 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();
@@ -202,10 +200,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 +210,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 +246,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 +305,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 +323,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 +350,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 +380,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 +427,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);
 
@@ -457,10 +448,10 @@ namespace AtomSampleViewer
         {
             RHI::CommandList* commandList = context.GetCommandList();
 
-            commandList->BuildBottomLevelAccelerationStructure(*m_triangleRayTracingBlas);
-            commandList->BuildBottomLevelAccelerationStructure(*m_rectangleRayTracingBlas);
+            commandList->BuildBottomLevelAccelerationStructure(*m_triangleRayTracingBlas->GetDeviceRayTracingBlas(context.GetDeviceIndex()));
+            commandList->BuildBottomLevelAccelerationStructure(*m_rectangleRayTracingBlas->GetDeviceRayTracingBlas(context.GetDeviceIndex()));
             commandList->BuildTopLevelAccelerationStructure(
-                *m_rayTracingTlas, { m_triangleRayTracingBlas.get(), m_rectangleRayTracingBlas.get() });
+                *m_rayTracingTlas->GetDeviceRayTracingTlas(context.GetDeviceIndex()), { m_triangleRayTracingBlas->GetDeviceRayTracingBlas(context.GetDeviceIndex()).get(), m_rectangleRayTracingBlas->GetDeviceRayTracingBlas(context.GetDeviceIndex()).get() });
         };
 
         m_scopeProducers.emplace_back(
@@ -521,7 +512,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);
@@ -594,19 +585,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;
+            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.get();
-            dispatchRaysItem.m_rayTracingShaderTable = m_rayTracingShaderTable.get();
+            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);
@@ -673,14 +664,21 @@ namespace AtomSampleViewer
             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;
+            RHI::DeviceDrawItem drawItem;
             drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_drawPipelineState.get();
-            drawItem.m_indexBufferView = &m_fullScreenIndexBufferView;
+            drawItem.m_pipelineState = m_drawPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+            auto deviceIndexBufferView{m_fullScreenIndexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+            drawItem.m_indexBufferView = &deviceIndexBufferView;
             drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_fullScreenStreamBufferViews.size());
-            drawItem.m_streamBufferViews = m_fullScreenStreamBufferViews.data();
+            AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                m_fullScreenStreamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                m_fullScreenStreamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+            };
+            drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
             drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
             drawItem.m_shaderResourceGroups = shaderResourceGroups;
 

+ 10 - 11
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
 {
@@ -141,7 +141,6 @@ namespace AtomSampleViewer
         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;

+ 23 - 21
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(RHI::MultiDevice::AllDevices, 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;
@@ -307,35 +307,37 @@ namespace AtomSampleViewer
                 commandList->SetScissors(&m_scissor, 1);
 
                 // Bind ViewSrg
-                commandList->SetShaderResourceGroupForDraw(*m_viewShaderResourceGroup->GetRHIShaderResourceGroup());
+                commandList->SetShaderResourceGroupForDraw(*m_viewShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get());
 
-                const RHI::IndexBufferView indexBufferView =
-                {
-                    *m_indexBuffer,
-                    0,
-                    indexBufSize,
-                    RHI::IndexFormat::Uint16
-                };
+                const RHI::DeviceIndexBufferView indexBufferView = { *m_indexBuffer->GetDeviceBuffer(context.GetDeviceIndex()), 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()
+                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;
+                RHI::DeviceDrawItem drawItem;
                 drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_mode ? m_demoPipelineState.get() : m_renderPipelineState.get();
+                drawItem.m_pipelineState = m_mode ? m_demoPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get() : m_renderPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_indexBufferView = &indexBufferView;
                 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();
+                AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                    m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                    m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+                };
+                drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
                 commandList->Submit(drawItem);
             };

+ 0 - 2
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>
@@ -135,7 +134,6 @@ namespace AtomSampleViewer
             AZStd::array<uint16_t, 6> m_indices;
         };
 
-        AZ::RHI::DrawItem m_drawItem;
         AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
         // ------------------------------------------------------------
 

+ 14 - 15
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(RHI::MultiDevice::AllDevices, 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();
@@ -205,21 +204,21 @@ 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::DeviceIndexBufferView indexBufferView = { *m_inputAssemblyBuffer->GetDeviceBuffer(context.GetDeviceIndex()),
+                                                                     offsetof(BufferData, m_indices), sizeof(BufferData::m_indices),
+                                                                     RHI::IndexFormat::Uint16 };
 
                 RHI::DrawIndexed drawIndexed;
                 drawIndexed.m_instanceCount = 1;
 
-                RHI::DrawItem drawItem;
+                RHI::DeviceDrawItem drawItem;
                 drawItem.m_indexBufferView = &indexBufferView;
                 drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
-                drawItem.m_streamBufferViews = m_streamBufferViews.data();
+                AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                    m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                    m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+                };
+                drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
                 for (uint32_t i = context.GetSubmitRange().m_startIndex; i < context.GetSubmitRange().m_endIndex; ++i)
                 {
@@ -229,7 +228,7 @@ namespace AtomSampleViewer
 
                         // Draw color triangles
                         drawItem.m_arguments = drawIndexed;
-                        drawItem.m_pipelineState = m_pipelineStateBasePass.get();
+                        drawItem.m_pipelineState = m_pipelineStateBasePass->GetDevicePipelineState(context.GetDeviceIndex()).get();
                         commandList->Submit(drawItem, i);
                     }
                     else
@@ -240,7 +239,7 @@ namespace AtomSampleViewer
                         drawItem.m_stencilRef = 1;
 
                         drawItem.m_arguments = drawIndexed;
-                        drawItem.m_pipelineState = m_pipelineStateStencil[i-1].get();
+                        drawItem.m_pipelineState = m_pipelineStateStencil[i-1]->GetDevicePipelineState(context.GetDeviceIndex()).get();
                         commandList->Submit(drawItem, i);
 
                         drawIndexed.m_indexOffset += 3;

+ 0 - 3
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
@@ -63,7 +61,6 @@ namespace AtomSampleViewer
         };
 
         AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
-        AZ::RHI::DrawItem m_drawItem;
 
         AZ::RHI::AttachmentId m_depthStencilID;
         AZ::RHI::ClearValue m_depthClearValue;

+ 25 - 19
Gem/Code/Source/RHI/SubpassExampleComponent.cpp

@@ -391,7 +391,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);
@@ -401,21 +401,28 @@ 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())
                 {
-                    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_arguments = mesh.m_drawArguments.GetDeviceDrawArguments(context.GetDeviceIndex());
+                    drawItem.m_pipelineState = modelData.m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                    auto deviceIndexBufferView{mesh.m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+                    drawItem.m_indexBufferView = &deviceIndexBufferView;
                     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();
+                    AZStd::vector<AZ::RHI::DeviceStreamBufferView> deviceStreamBufferViews;
+                    for(const auto& streamBufferView : modelData.m_streamBufferList)
+                    {
+                        deviceStreamBufferViews.emplace_back(streamBufferView.GetDeviceStreamBufferView(context.GetDeviceIndex()));
+                    }
+                    drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
                     commandList->Submit(drawItem);
                 }
@@ -491,9 +498,9 @@ namespace AtomSampleViewer
 
         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);
@@ -506,16 +513,15 @@ 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;
@@ -524,9 +530,9 @@ namespace AtomSampleViewer
             drawArguments.m_vertexCount = 4;
             drawArguments.m_vertexOffset = 0;
 
-            RHI::DrawItem drawItem;
-            drawItem.m_arguments = RHI::DrawArguments(drawArguments);
-            drawItem.m_pipelineState = m_compositionPipeline.get();
+            RHI::DeviceDrawItem drawItem;
+            drawItem.m_arguments = RHI::DeviceDrawArguments(drawArguments);
+            drawItem.m_pipelineState = m_compositionPipeline->GetDevicePipelineState(context.GetDeviceIndex()).get();
             drawItem.m_indexBufferView = nullptr;
             drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
             drawItem.m_shaderResourceGroups = shaderResourceGroups;

+ 14 - 15
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(
@@ -62,8 +60,7 @@ namespace AtomSampleViewer
 
         // 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(RHI::MultiDevice::AllDevices, 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();
@@ -220,12 +217,14 @@ namespace AtomSampleViewer
                 drawIndexed.m_vertexCount = 4;
                 drawIndexed.m_instanceCount = 1;
 
-                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;
+                RHI::DeviceDrawItem drawItem;
                 drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_pipelineState.get();
+                drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_indexBufferView = nullptr;
                 drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                 drawItem.m_shaderResourceGroups = shaderResourceGroups;
@@ -277,7 +276,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);

+ 17 - 14
Gem/Code/Source/RHI/TextureArrayExampleComponent.cpp

@@ -59,7 +59,7 @@ namespace AtomSampleViewer
         };
 
         RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
-        m_inputAssemblyBufferPool = RHI::Factory::Get().CreateBufferPool();
+        m_inputAssemblyBufferPool = aznew RHI::BufferPool();
 
         // Load the shader
         {
@@ -132,8 +132,8 @@ namespace AtomSampleViewer
             RHI::BufferPoolDescriptor bufferPoolDesc;
             bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
             bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
-            m_inputAssemblyBufferPool->Init(*device, bufferPoolDesc);
-            m_rectangleInputAssemblyBuffer = RHI::Factory::Get().CreateBuffer();
+            m_inputAssemblyBufferPool->Init(RHI::MultiDevice::AllDevices, bufferPoolDesc);
+            m_rectangleInputAssemblyBuffer = aznew RHI::Buffer();
 
             RHI::ResultCode result = RHI::ResultCode::Success;
             RHI::BufferInitRequest request;
@@ -205,29 +205,32 @@ namespace AtomSampleViewer
                 commandList->SetViewports(&viewport, 1u);
                 commandList->SetScissors(&m_scissor, 1u);
 
-                const RHI::IndexBufferView indexBufferView =
-                {
-                    *m_rectangleInputAssemblyBuffer,
-                    offsetof(RectangleBufferData, m_indices),
-                    sizeof(RectangleBufferData::m_indices),
-                    RHI::IndexFormat::Uint16
+                const RHI::DeviceIndexBufferView indexBufferView = {
+                    *m_rectangleInputAssemblyBuffer->GetDeviceBuffer(context.GetDeviceIndex()), offsetof(RectangleBufferData, m_indices),
+                    sizeof(RectangleBufferData::m_indices), RHI::IndexFormat::Uint16
                 };
 
                 RHI::DrawIndexed drawIndexed;
                 drawIndexed.m_indexCount = 6u;
                 drawIndexed.m_instanceCount = 1u;
 
-                const RHI::ShaderResourceGroup* shaderResourceGroups[] = {
-                    m_textureArraySrg->GetRHIShaderResourceGroup(), m_textureIndexSrg->GetRHIShaderResourceGroup() };
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                    m_textureArraySrg->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get(),
+                    m_textureIndexSrg->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+                };
 
-                RHI::DrawItem drawItem;
+                RHI::DeviceDrawItem drawItem;
                 drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_pipelineState.get();
+                drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_indexBufferView = &indexBufferView;
                 drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
                 drawItem.m_shaderResourceGroups = shaderResourceGroups;
                 drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_rectangleStreamBufferViews.size());
-                drawItem.m_streamBufferViews = m_rectangleStreamBufferViews.data();
+                AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                    m_rectangleStreamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                    m_rectangleStreamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+                };
+                drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
                 // Submit the triangle draw item.
                 commandList->Submit(drawItem);

+ 17 - 18
Gem/Code/Source/RHI/TextureExampleComponent.cpp

@@ -14,8 +14,6 @@
 #include <Atom/RHI/Factory.h>
 #include <Atom/RHI/CommandList.h>
 #include <Atom/RHI/FrameScheduler.h>
-#include <Atom/RHI/Image.h>
-#include <Atom/RHI/ImagePool.h>
 #include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
 #include <Atom/RHI.Reflect/RenderAttachmentLayoutBuilder.h>
 
@@ -58,18 +56,18 @@ 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(RHI::MultiDevice::AllDevices, 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;
@@ -226,28 +224,29 @@ 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::DeviceIndexBufferView indexBufferView = { *m_indexBuffer->GetDeviceBuffer(context.GetDeviceIndex()), 0,
+                                                                     indexBufSize, RHI::IndexFormat::Uint16 };
 
                 RHI::DrawIndexed drawIndexed;
                 drawIndexed.m_indexCount = 6;
                 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;
+                RHI::DeviceDrawItem drawItem;
                 drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_pipelineState.get();
+                drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_indexBufferView = &indexBufferView;
                 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();
+                AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                    m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                    m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+                };
+                drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
                 commandList->Submit(drawItem);
             };

+ 1 - 2
Gem/Code/Source/RHI/TextureExampleComponent.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>
@@ -68,7 +67,7 @@ namespace AtomSampleViewer
             AZStd::array<uint16_t, 6> m_indices;
         };
 
-        AZ::RHI::DrawItem m_drawItem;
+        AZ::RHI::DeviceDrawItem m_drawItem;
         AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
 
         AZ::RHI::SamplerState m_samplerState;

+ 31 - 18
Gem/Code/Source/RHI/TextureMapExampleComponent.cpp

@@ -7,8 +7,6 @@
  */
 
 
-#include <Atom/RHI/ImagePool.h>
-
 #include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
 #include <Atom/RHI.Reflect/RenderAttachmentLayoutBuilder.h>
 #include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
@@ -206,12 +204,12 @@ namespace AtomSampleViewer
     void TextureMapExampleComponent::CreateInputAssemblyBufferPool()
     {
         const AZ::RHI::Ptr<AZ::RHI::Device> device = Utils::GetRHIDevice();
-        m_inputAssemblyBufferPool = AZ::RHI::Factory::Get().CreateBufferPool();
+        m_inputAssemblyBufferPool = aznew AZ::RHI::BufferPool();
 
         AZ::RHI::BufferPoolDescriptor bufferPoolDesc;
         bufferPoolDesc.m_bindFlags = AZ::RHI::BufferBindFlags::InputAssembly;
         bufferPoolDesc.m_heapMemoryLevel = AZ::RHI::HeapMemoryLevel::Device;
-        m_inputAssemblyBufferPool->Init(*device, bufferPoolDesc);
+        m_inputAssemblyBufferPool->Init(AZ::RHI::MultiDevice::AllDevices, bufferPoolDesc);
     }
 
     void TextureMapExampleComponent::InitRenderTargetBufferView()
@@ -515,14 +513,22 @@ namespace AtomSampleViewer
             drawIndexed.m_indexCount = 6;
             drawIndexed.m_instanceCount = 1;
 
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_targetSRGs[target]->GetRHIShaderResourceGroup() };
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_targetSRGs[target]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+            };
 
-            RHI::DrawItem drawItem;
+            RHI::DeviceDrawItem drawItem;
             drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_targetPipelineStates[target].get();
-            drawItem.m_indexBufferView = &m_bufferViews[RenderTargetIndex::BufferViewIndex].m_indexBufferView;
+            drawItem.m_pipelineState = m_targetPipelineStates[target]->GetDevicePipelineState(context.GetDeviceIndex()).get();
+            auto deviceIndexBufferView{m_bufferViews[RenderTargetIndex::BufferViewIndex].m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+            drawItem.m_indexBufferView = &deviceIndexBufferView;
             drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_bufferViews[RenderTargetIndex::BufferViewIndex].m_streamBufferViews.size());
-            drawItem.m_streamBufferViews = m_bufferViews[RenderTargetIndex::BufferViewIndex].m_streamBufferViews.data();
+            AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                m_bufferViews[RenderTargetIndex::BufferViewIndex].m_streamBufferViews[0].GetDeviceStreamBufferView(
+                    context.GetDeviceIndex()),
+                m_bufferViews[RenderTargetIndex::BufferViewIndex].m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+            };
+            drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
             drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
             drawItem.m_shaderResourceGroups = shaderResourceGroups;
 
@@ -574,7 +580,7 @@ namespace AtomSampleViewer
 
         const auto compileFunction = [this, target](const AZ::RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
         {
-            const AZ::RHI::ImageView* imageView = context.GetImageView(m_attachmentID[target]);
+            const auto* imageView = context.GetImageView(m_attachmentID[target]);
 
             m_screenSRGs[target]->SetImageView(m_shaderInputImageIndices[target], imageView);
             m_screenSRGs[target]->Compile();
@@ -592,14 +598,21 @@ namespace AtomSampleViewer
             drawIndexed.m_indexCount = m_bufferViews[target].m_indexBufferView.GetByteCount() / sizeof(uint16_t);
             drawIndexed.m_instanceCount = 1;
 
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_screenSRGs[target]->GetRHIShaderResourceGroup() };
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_screenSRGs[target]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+            };
 
-            RHI::DrawItem drawItem;
+            RHI::DeviceDrawItem drawItem;
             drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_screenPipelineStates[target].get();
-            drawItem.m_indexBufferView = &m_bufferViews[target].m_indexBufferView;
+            drawItem.m_pipelineState = m_screenPipelineStates[target]->GetDevicePipelineState(context.GetDeviceIndex()).get();
+            auto deviceIndexBufferView{m_bufferViews[target].m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+            drawItem.m_indexBufferView = &deviceIndexBufferView;
             drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_bufferViews[target].m_streamBufferViews.size());
-            drawItem.m_streamBufferViews = m_bufferViews[target].m_streamBufferViews.data();
+            AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                m_bufferViews[target].m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                m_bufferViews[target].m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+            };
+            drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
             drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
             drawItem.m_shaderResourceGroups = shaderResourceGroups;
 
@@ -767,7 +780,7 @@ namespace AtomSampleViewer
         uint32_t uvSize, void* uvData, uint32_t uvTypeSize,
         uint32_t indexSize, void* indexData)
     {
-        m_positionBuffer[target] = AZ::RHI::Factory::Get().CreateBuffer();
+        m_positionBuffer[target] = aznew AZ::RHI::Buffer();
         AZ::RHI::ResultCode result = AZ::RHI::ResultCode::Success;
         AZ::RHI::BufferInitRequest request;
         request.m_buffer = m_positionBuffer[target].get();
@@ -788,7 +801,7 @@ namespace AtomSampleViewer
             sizeof(VertexPosition)
         };
 
-        m_uvBuffer[target] = AZ::RHI::Factory::Get().CreateBuffer();
+        m_uvBuffer[target] = aznew AZ::RHI::Buffer();
         request.m_buffer = m_uvBuffer[target].get();
         request.m_descriptor = AZ::RHI::BufferDescriptor{ AZ::RHI::BufferBindFlags::InputAssembly, uvSize };
         request.m_initialData = uvData;
@@ -807,7 +820,7 @@ namespace AtomSampleViewer
             uvTypeSize
         };
 
-        m_indexBuffer[target] = AZ::RHI::Factory::Get().CreateBuffer();
+        m_indexBuffer[target] = aznew AZ::RHI::Buffer();
         request.m_buffer = m_indexBuffer[target].get();
         request.m_descriptor = AZ::RHI::BufferDescriptor{ AZ::RHI::BufferBindFlags::InputAssembly, indexSize };
         request.m_initialData = indexData;

+ 16 - 16
Gem/Code/Source/RHI/TriangleExampleComponent.cpp

@@ -61,17 +61,15 @@ namespace AtomSampleViewer
     {
         using namespace AZ;
 
-        RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
-
         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(RHI::MultiDevice::AllDevices, bufferPoolDesc);
 
             BufferData bufferData;
 
@@ -85,7 +83,7 @@ namespace AtomSampleViewer
 
             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();
@@ -196,28 +194,30 @@ 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::DeviceIndexBufferView indexBufferView = { *m_inputAssemblyBuffer->GetDeviceBuffer(context.GetDeviceIndex()),
+                                                                     offsetof(BufferData, m_indices), sizeof(BufferData::m_indices),
+                                                                     RHI::IndexFormat::Uint16 };
 
                 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;
+                RHI::DeviceDrawItem drawItem;
                 drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_pipelineState.get();
+                drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
                 drawItem.m_indexBufferView = &indexBufferView;
                 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();
+                AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                    m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                    m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+                };
+                drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
                 // Submit the triangle draw item.
                 commandList->Submit(drawItem);

+ 0 - 2
Gem/Code/Source/RHI/TriangleExampleComponent.h

@@ -13,7 +13,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>
@@ -62,6 +61,5 @@ namespace AtomSampleViewer
         };
 
         AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
-        AZ::RHI::DrawItem m_drawItem;
     };
 } // namespace AtomSampleViewer

+ 23 - 13
Gem/Code/Source/RHI/TrianglesConstantBufferExampleComponent.cpp

@@ -102,7 +102,10 @@ namespace AtomSampleViewer
         AZ::RHI::ResultCode resultCode = m_constantBufferPool->MapBuffer(mapRequest, mapResponse);
         if (resultCode == AZ::RHI::ResultCode::Success)
         {
-            memcpy(mapResponse.m_data, data, sizeof(InstanceInfo) * elementCount);
+            for(const auto& [_, bufferData] : mapResponse.m_data)
+            {
+                memcpy(bufferData, data, sizeof(InstanceInfo) * elementCount);
+            }
             m_constantBufferPool->UnmapBuffer(*mapRequest.m_buffer);
         }
     }
@@ -117,12 +120,12 @@ namespace AtomSampleViewer
 
         // Creates Input Assembly buffer and Streams/Index Views
         {
-            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(RHI::MultiDevice::AllDevices, bufferPoolDesc);
 
             IABufferData bufferData;
 
@@ -136,7 +139,7 @@ namespace AtomSampleViewer
 
             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();
@@ -178,12 +181,12 @@ namespace AtomSampleViewer
 
         // Create the buffer pool where both buffers get allocated from
         {
-            m_constantBufferPool = RHI::Factory::Get().CreateBufferPool();
+            m_constantBufferPool = aznew RHI::BufferPool();
             RHI::BufferPoolDescriptor constantBufferPoolDesc;
             constantBufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::Constant;
             constantBufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
             constantBufferPoolDesc.m_hostMemoryAccess = RHI::HostMemoryAccess::Write;
-            [[maybe_unused]] RHI::ResultCode result = m_constantBufferPool->Init(*device, constantBufferPoolDesc);
+            [[maybe_unused]] RHI::ResultCode result = m_constantBufferPool->Init(RHI::MultiDevice::AllDevices, constantBufferPoolDesc);
             AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialized constant buffer pool");
         }
 
@@ -260,16 +263,23 @@ namespace AtomSampleViewer
                 drawIndexed.m_indexCount = 3;
                 drawIndexed.m_instanceCount = s_numberOfTrianglesTotal;
 
-                const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroup->GetRHIShaderResourceGroup() };
+                const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                    m_shaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+                };
 
-                RHI::DrawItem drawItem;
+                RHI::DeviceDrawItem drawItem;
                 drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_pipelineState.get();
-                drawItem.m_indexBufferView = &m_indexBufferView;
+                drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                auto deviceIndexBufferView{m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+                drawItem.m_indexBufferView = &deviceIndexBufferView;
                 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();
+                AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                    m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                    m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+                };
+                drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
                 // Submit the triangle draw item.
                 commandList->Submit(drawItem);
@@ -297,7 +307,7 @@ namespace AtomSampleViewer
         using namespace AZ;
         const uint32_t constantBufferSize = sizeof(InstanceInfo) * s_numberOfTrianglesTotal;
 
-        m_constantBuffer = RHI::Factory::Get().CreateBuffer();
+        m_constantBuffer = aznew RHI::Buffer();
         RHI::BufferInitRequest request;
         request.m_buffer = m_constantBuffer.get();
         request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::Constant, constantBufferSize };
@@ -305,7 +315,7 @@ namespace AtomSampleViewer
         AZ_Assert(result == RHI::ResultCode::Success, "Failed to initialize constant buffer");
 
         RHI::BufferViewDescriptor bufferDesc = RHI::BufferViewDescriptor::CreateStructured(0, 1u, constantBufferSize);
-        m_constantBufferView = m_constantBuffer->GetBufferView(bufferDesc);
+        m_constantBufferView = m_constantBuffer->BuildBufferView(bufferDesc);
     }
 
     void TrianglesConstantBufferExampleComponent::Deactivate()

+ 0 - 3
Gem/Code/Source/RHI/TrianglesConstantBufferExampleComponent.h

@@ -18,7 +18,6 @@
 
 #include <Atom/RHI/ScopeProducer.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>
@@ -77,8 +76,6 @@ namespace AtomSampleViewer
         void UploadDataToConstantBuffer(InstanceInfo* data, uint32_t elementSize, uint32_t elementCount);
         void CreateConstantBufferView();
         
-        AZ::RHI::DrawItem m_drawItem;
-
         float m_time = 0.0f;
 
         // -------------------------------------------------

+ 38 - 31
Gem/Code/Source/RHI/VariableRateShadingExampleComponent.cpp

@@ -169,10 +169,10 @@ namespace AtomSampleViewer
         const auto& tileSize = device->GetLimits().m_shadingRateTileSize;
         m_shadingRateImageSize = Vector2(ceil(static_cast<float>(m_outputWidth) / tileSize.m_width), ceil(static_cast<float>(m_outputHeight) / tileSize.m_height));
 
-        m_imagePool = RHI::Factory::Get().CreateImagePool();
+        m_imagePool = aznew RHI::ImagePool();
         RHI::ImagePoolDescriptor imagePoolDesc;
         imagePoolDesc.m_bindFlags = RHI::ImageBindFlags::ShadingRate | RHI::ImageBindFlags::ShaderReadWrite;
-        m_imagePool->Init(*device, imagePoolDesc);
+        m_imagePool->Init(RHI::MultiDevice::AllDevices, imagePoolDesc);
 
         // Initialize the shading rate images with proper values. Invalid values may cause a crash.
         uint32_t width = static_cast<uint32_t>(m_shadingRateImageSize.GetX());
@@ -200,7 +200,7 @@ namespace AtomSampleViewer
         m_shadingRateImages.resize(device->GetFeatures().m_dynamicShadingRateImage ? 1 : device->GetDescriptor().m_frameCountMax+3);
         for (auto& image : m_shadingRateImages)
         {
-            image = RHI::Factory::Get().CreateImage();
+            image = aznew RHI::Image();
             RHI::ImageInitRequest initImageRequest;
             RHI::ClearValue clearValue = RHI::ClearValue::CreateVector4Float(1, 1, 1, 1);
             initImageRequest.m_image = image.get();
@@ -215,14 +215,9 @@ namespace AtomSampleViewer
             RHI::ImageUpdateRequest request;
             request.m_image = image.get();
             request.m_sourceData = shadingRatePatternData.data();
-            request.m_sourceSubresourceLayout = RHI::ImageSubresourceLayout(
-                RHI::Size(width, height, 1),
-                height,
-                width * formatSize,
-                bufferSize,
-                1,
-                1
-            );
+            request.m_sourceSubresourceLayout = RHI::ImageSubresourceLayout();
+            request.m_sourceSubresourceLayout.Init(
+                RHI::MultiDevice::AllDevices, { RHI::Size(width, height, 1), height, width * formatSize, bufferSize, 1, 1 });
 
             m_imagePool->UpdateImageContents(request);
         }        
@@ -444,13 +439,11 @@ namespace AtomSampleViewer
 
     void VariableRateShadingExampleComponent::CreateInputAssemblyBuffersAndViews()
     {
-        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(RHI::MultiDevice::AllDevices, bufferPoolDesc);
 
         struct BufferData
         {
@@ -462,7 +455,7 @@ namespace AtomSampleViewer
         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;
 
@@ -558,7 +551,9 @@ namespace AtomSampleViewer
                 commandList->SetFragmentShadingRate(m_shadingRate, combinators);
             }
 
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_modelShaderResourceGroup->GetRHIShaderResourceGroup() };
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_modelShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+            };
             // We have to wait until the updating of the initial contents of the shading rate image is done if
             // dynamic mode is not supported (since the CPU would try to read it while the GPU is updating the contents)
             bool useImageShadingRate = m_useImageShadingRate && (device->GetFeatures().m_dynamicShadingRateImage || m_frameCount > device->GetDescriptor().m_frameCountMax);
@@ -567,14 +562,19 @@ namespace AtomSampleViewer
             drawIndexed.m_indexCount = 6;
             drawIndexed.m_instanceCount = 1;
 
-            RHI::DrawItem drawItem;
+            RHI::DeviceDrawItem drawItem;
             drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_modelPipelineState[useImageShadingRate ? 0 : 1].get();
-            drawItem.m_indexBufferView = &m_indexBufferView;
+            drawItem.m_pipelineState = m_modelPipelineState[useImageShadingRate ? 0 : 1]->GetDevicePipelineState(context.GetDeviceIndex()).get();
+            auto deviceIndexBufferView{m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+            drawItem.m_indexBufferView = &deviceIndexBufferView;
             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();
+            AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+            };
+            drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
             commandList->Submit(drawItem);
 
@@ -630,7 +630,7 @@ namespace AtomSampleViewer
             if (m_useImageShadingRate)
             {
                 Vector2 center = m_cursorPos * m_shadingRateImageSize;
-                const RHI::ImageView* shadingRateImageView = context.GetImageView(RHI::AttachmentId(shadingRateAttachmentId));
+                const auto* shadingRateImageView = context.GetImageView(RHI::AttachmentId(shadingRateAttachmentId));
                 m_computeShaderResourceGroup->SetImageView(m_shadingRateIndex, shadingRateImageView);
                 m_computeShaderResourceGroup->SetConstant(m_centerIndex, center);
                 m_computeShaderResourceGroup->Compile();
@@ -646,8 +646,8 @@ namespace AtomSampleViewer
 
             RHI::CommandList* commandList = context.GetCommandList();
 
-            RHI::DispatchItem dispatchItem;
-            decltype(dispatchItem.m_shaderResourceGroups) shaderResourceGroups = { { m_computeShaderResourceGroup->GetRHIShaderResourceGroup() } };
+            RHI::DeviceDispatchItem dispatchItem;
+            decltype(dispatchItem.m_shaderResourceGroups) shaderResourceGroups = { { m_computeShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get() } };
 
             RHI::DispatchDirect dispatchArgs;
 
@@ -662,7 +662,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_computePipelineState.get();
+            dispatchItem.m_pipelineState = m_computePipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
             dispatchItem.m_shaderResourceGroupCount = 1;
             dispatchItem.m_shaderResourceGroups = shaderResourceGroups;
 
@@ -717,7 +717,7 @@ namespace AtomSampleViewer
         {
             if (m_showShadingRateImage)
             {
-                const RHI::ImageView* shadingRateImageView = context.GetImageView(RHI::AttachmentId(VariableRateShading::ShadingRateAttachmentId));
+                const auto* shadingRateImageView = context.GetImageView(RHI::AttachmentId(VariableRateShading::ShadingRateAttachmentId));
                 m_imageShaderResourceGroup->SetImageView(m_shadingRateDisplayIndex, shadingRateImageView);
                 m_imageShaderResourceGroup->Compile();
             }
@@ -736,20 +736,27 @@ namespace AtomSampleViewer
             commandList->SetViewports(&m_viewport, 1);
             commandList->SetScissors(&m_scissor, 1);
 
-            const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_imageShaderResourceGroup->GetRHIShaderResourceGroup() };
+            const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                m_imageShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+            };
 
             RHI::DrawIndexed drawIndexed;
             drawIndexed.m_indexCount = 6;
             drawIndexed.m_instanceCount = 1;
 
-            RHI::DrawItem drawItem;
+            RHI::DeviceDrawItem drawItem;
             drawItem.m_arguments = drawIndexed;
-            drawItem.m_pipelineState = m_imagePipelineState.get();
-            drawItem.m_indexBufferView = &m_indexBufferView;
+            drawItem.m_pipelineState = m_imagePipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+            auto deviceIndexBufferView{m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+            drawItem.m_indexBufferView = &deviceIndexBufferView;
             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();
+            AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+            };
+            drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
             commandList->Submit(drawItem);
         };

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

@@ -21,10 +21,10 @@
 #include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
 
 #include <Atom/RHI/BufferPool.h>
-#include <Atom/RHI/DrawItem.h>
 #include <Atom/RHI/Device.h>
+#include <Atom/RHI/DeviceCopyItem.h>
+#include <Atom/RHI/DrawItem.h>
 #include <Atom/RHI/Factory.h>
-#include <Atom/RHI/CopyItem.h>
 #include <Atom/RHI/FrameScheduler.h>
 #include <Atom/RHI/PipelineState.h>
 

+ 15 - 8
Gem/Code/Source/RHI/XRExampleComponent.cpp

@@ -194,11 +194,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(RHI::MultiDevice::AllDevices, bufferPoolDesc);
         if (result != AZ::RHI::ResultCode::Success)
         {
             AZ_Error("XRExampleComponent", false, "Failed to initialize buffer pool with error code %d", result);
@@ -207,7 +207,7 @@ namespace AtomSampleViewer
 
         SingleCubeBufferData bufferData = CreateSingleCubeBufferData();
 
-        m_inputAssemblyBuffer = AZ::RHI::Factory::Get().CreateBuffer();
+        m_inputAssemblyBuffer = aznew AZ::RHI::Buffer();
         AZ::RHI::BufferInitRequest request;
 
         request.m_buffer = m_inputAssemblyBuffer.get();
@@ -375,16 +375,23 @@ namespace AtomSampleViewer
 
             for (uint32_t i = indexStart; i < indexEnd; ++i)
             {
-                const AZ::RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[i]->GetRHIShaderResourceGroup() };
+                const AZ::RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
+                    m_shaderResourceGroups[i]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
+                };
 
-                AZ::RHI::DrawItem drawItem;
+                AZ::RHI::DeviceDrawItem drawItem;
                 drawItem.m_arguments = drawIndexed;
-                drawItem.m_pipelineState = m_pipelineState.get();
-                drawItem.m_indexBufferView = &m_indexBufferView;
+                drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
+                auto deviceIndexBufferView{m_indexBufferView.GetDeviceIndexBufferView(context.GetDeviceIndex())};
+                drawItem.m_indexBufferView = &deviceIndexBufferView;
                 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();
+                AZStd::array<AZ::RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
+                    m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
+                    m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
+                };
+                drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
 
                 commandList->Submit(drawItem);
             }

+ 0 - 2
Gem/Code/Source/RHI/XRExampleComponent.h

@@ -14,7 +14,6 @@
 #include <Atom/RPI.Public/XR/XRSpaceNotificationBus.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>
@@ -108,7 +107,6 @@ namespace AtomSampleViewer
         };
 
         AZStd::array<AZ::RHI::StreamBufferView, 2> m_streamBufferViews;
-        AZ::RHI::DrawItem m_drawItem;
         float m_time = 0.0f;
         AZStd::array<AZ::Data::Instance<AZ::RPI::ShaderResourceGroup>, NumberOfCubes> m_shaderResourceGroups;
         AZStd::array<AZ::Matrix4x4, NumberOfCubes> m_modelMatrices;

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

@@ -276,9 +276,8 @@ namespace AtomSampleViewer
 
     void ReadbackExampleComponent::ReadbackCallback(const AZ::RPI::AttachmentReadback::ReadbackResult& result)
     {
-        const AZ::RHI::ImageSubresourceRange range(0, 0, 0, 0);
         AZ::RHI::ImageSubresourceLayout layout;
-        m_previewImage->GetRHIImage()->GetSubresourceLayouts(range, &layout, nullptr);
+        m_previewImage->GetRHIImage()->GetSubresourceLayout(layout);
 
         m_textureNeedsUpdate = true;
         m_resultData = result.m_dataBuffer;
@@ -291,9 +290,8 @@ namespace AtomSampleViewer
 
     void ReadbackExampleComponent::UploadReadbackResult() const
     {
-        const AZ::RHI::ImageSubresourceRange range(0, 0, 0, 0);
         AZ::RHI::ImageSubresourceLayout layout;
-        m_previewImage->GetRHIImage()->GetSubresourceLayouts(range, &layout, nullptr);
+        m_previewImage->GetRHIImage()->GetSubresourceLayout(layout);
         AZ::RHI::ImageUpdateRequest updateRequest;
         updateRequest.m_image = m_previewImage->GetRHIImage();
         updateRequest.m_sourceSubresourceLayout = layout;

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

@@ -303,7 +303,7 @@ namespace AtomSampleViewer
                 auto const& mesh = meshes[i];
 
                 // Build draw packet and set the values of the inline constants.
-                RHI::DrawPacketBuilder drawPacketBuilder;
+                RHI::DrawPacketBuilder drawPacketBuilder{RHI::MultiDevice::DefaultDevice};
                 drawPacketBuilder.Begin(nullptr);
                 drawPacketBuilder.SetDrawArguments(mesh.m_drawArguments);
                 drawPacketBuilder.SetIndexBufferView(mesh.m_indexBufferView);
@@ -317,7 +317,7 @@ namespace AtomSampleViewer
                 drawRequest.m_sortKey = 0;
                 drawPacketBuilder.AddDrawItem(drawRequest);
 
-                AZStd::unique_ptr<const RHI::DrawPacket> drawPacket(drawPacketBuilder.End());
+                auto drawPacket{drawPacketBuilder.End()};
                 m_dynamicDraw->AddDrawPacket(m_scene, AZStd::move(drawPacket));
             }
         }

+ 7 - 3
Gem/Code/Source/SampleComponentManager.cpp

@@ -52,6 +52,7 @@
 #include <RHI/MRTExampleComponent.h>
 #include <RHI/MSAAExampleComponent.h>
 #include <RHI/MultiThreadComponent.h>
+#include <RHI/MultiGPUExampleComponent.h>
 #include <RHI/MultiViewportSwapchainComponent.h>
 #include <RHI/MultipleViewsComponent.h>
 #include <RHI/QueryExampleComponent.h>
@@ -91,6 +92,7 @@
 #include <LightCullingExampleComponent.h>
 #include <MeshExampleComponent.h>
 #include <MSAA_RPI_ExampleComponent.h>
+#include <MultiGPURPIExampleComponent.h>
 #include <MultiRenderPipelineExampleComponent.h>
 #include <MultiSceneExampleComponent.h>
 #include <ParallaxMappingExampleComponent.h>
@@ -280,6 +282,7 @@ namespace AtomSampleViewer
             NewRHISample<MultipleViewsComponent>("MultipleViews"),
             NewRHISample<MRTExampleComponent>("MultiRenderTarget"),
             NewRHISample<MultiThreadComponent>("MultiThread"),
+            NewRHISample<MultiGPUExampleComponent>("MultiGPU", []() { return AZ::RHI::RHISystemInterface::Get()->GetDeviceCount() >= 2; }),
             NewRHISample<MultiViewportSwapchainComponent>("MultiViewportSwapchainComponent", [] { return IsMultiViewportSwapchainSampleSupported(); }),
             NewRHISample<QueryExampleComponent>("Queries"),
             NewRHISample<RayTracingExampleComponent>("RayTracing", []() {return Utils::GetRHIDevice()->GetFeatures().m_rayTracing; }),
@@ -306,6 +309,7 @@ namespace AtomSampleViewer
             NewRPISample<DynamicMaterialTestComponent>("DynamicMaterialTest"),
             NewRPISample<MeshExampleComponent>("Mesh"),
             NewRPISample<MSAA_RPI_ExampleComponent>("MSAA"),
+            NewRPISample<MultiGPURPIExampleComponent>("MultiGPU", []() { return AZ::RHI::RHISystemInterface::Get()->GetDeviceCount() >= 2; }),
             NewRPISample<MultiRenderPipelineExampleComponent>("MultiRenderPipeline"),
             NewRPISample<MultiSceneExampleComponent>("MultiScene"),
             NewRPISample<MultiViewSingleSceneAuxGeomExampleComponent>("MultiViewSingleSceneAuxGeom"),
@@ -1178,10 +1182,10 @@ namespace AtomSampleViewer
 
     void SampleComponentManager::ShowTransientAttachmentProfilerWindow()
     {
-        auto* transientStats = AZ::RHI::RHIMemoryStatisticsInterface::Get()->GetTransientAttachmentStatistics();
-        if (transientStats)
+        const auto& transientStats = AZ::RHI::RHIMemoryStatisticsInterface::Get()->GetTransientAttachmentStatistics();
+        if (!transientStats.empty())
         {
-            m_showTransientAttachmentProfiler = m_imguiTransientAttachmentProfiler.Draw(*transientStats);
+            m_showTransientAttachmentProfiler = m_imguiTransientAttachmentProfiler.Draw(transientStats);
         }
     }
 

+ 7 - 7
Gem/Code/Source/StreamingImageExampleComponent.cpp

@@ -372,7 +372,7 @@ namespace AtomSampleViewer
         if (m_imguiSidebar.Begin())
         {
             Data::Instance<RPI::StreamingImagePool> streamingImagePool = RPI::ImageSystemInterface::Get()->GetSystemStreamingPool();
-            const RHI::StreamingImagePool* rhiPool = streamingImagePool->GetRHIPool();
+            const auto* rhiPool = streamingImagePool->GetRHIPool();
             const RHI::HeapMemoryUsage& memoryUsage = rhiPool->GetHeapMemoryUsage(RHI::HeapMemoryLevel::Device);
 
             const size_t MB = 1024*1024;
@@ -390,7 +390,7 @@ namespace AtomSampleViewer
             size_t budgetInBytes = memoryUsage.m_budgetInBytes;
             int budgetInMB = aznumeric_cast<int>(budgetInBytes/MB);
             ImGui::Text("GPU memory budget: %d MB", budgetInMB);
-            if (ScriptableImGui::SliderInt("MB", &budgetInMB, RHI::StreamingImagePool::ImagePoolMininumSizeInBytes/MB, 512))
+            if (ScriptableImGui::SliderInt("MB", &budgetInMB, RHI::DeviceStreamingImagePool::ImagePoolMininumSizeInBytes / MB, 512))
             {
                 streamingImagePool->SetMemoryBudget(budgetInMB * (1024*1024));
             }
@@ -490,7 +490,7 @@ namespace AtomSampleViewer
         for (Image3dToDraw& image3d : m_3dImages)
         {
             // Build draw packet...
-            RHI::DrawPacketBuilder drawPacketBuilder;
+            RHI::DrawPacketBuilder drawPacketBuilder{RHI::MultiDevice::DefaultDevice};
             drawPacketBuilder.Begin(nullptr);
             RHI::DrawLinear drawLinear;
             drawLinear.m_vertexCount = 4;
@@ -505,7 +505,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));
         }
     }
@@ -513,7 +513,7 @@ namespace AtomSampleViewer
     void StreamingImageExampleComponent::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;
@@ -528,7 +528,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));
     }
 
@@ -653,7 +653,7 @@ namespace AtomSampleViewer
         // will be uploaded with a single command
         {
             AZStd::vector<uint8_t> imageData;
-            RHI::ImageSubresourceLayout layout;
+            RHI::DeviceImageSubresourceLayout layout;
             RHI::Format format = {};
             BasicRHIComponent::CreateImage3dData(imageData, layout, format, {
                                                             "textures/streaming/streaming13.dds.streamingimage",

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

@@ -271,7 +271,7 @@ namespace AtomSampleViewer
     void TonemappingExampleComponent::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;
@@ -285,7 +285,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 - 1
Gem/Code/Source/Utils/Utils.cpp

@@ -214,7 +214,7 @@ namespace AtomSampleViewer
 
             uint32_t pitch = width * pixelSize;
 
-            AZ::RHI::ImageSubresourceLayout layout;
+            AZ::RHI::DeviceImageSubresourceLayout layout;
             layout.m_bytesPerImage = pixelDataSize;
             layout.m_rowCount = layout.m_bytesPerImage / pitch;
             layout.m_size = AZ::RHI::Size(width, height, 1);

+ 4 - 0
Gem/Code/atomsampleviewergem_private_files.cmake

@@ -53,6 +53,8 @@ set(FILES
     Source/RHI/MRTExampleComponent.cpp
     Source/RHI/MSAAExampleComponent.h
     Source/RHI/MSAAExampleComponent.cpp
+    Source/RHI/MultiGPUExampleComponent.cpp
+    Source/RHI/MultiGPUExampleComponent.h
     Source/RHI/MultiThreadComponent.cpp
     Source/RHI/MultiThreadComponent.h
     Source/RHI/MultipleViewsComponent.cpp
@@ -141,6 +143,8 @@ set(FILES
     Source/MeshExampleComponent.h
     Source/MSAA_RPI_ExampleComponent.cpp
     Source/MSAA_RPI_ExampleComponent.h
+    Source/MultiGPURPIExampleComponent.cpp
+    Source/MultiGPURPIExampleComponent.h
     Source/MultiRenderPipelineExampleComponent.cpp
     Source/MultiRenderPipelineExampleComponent.h
     Source/MultiSceneExampleComponent.cpp

+ 28 - 0
Passes/ASV/PassTemplates.azasset

@@ -64,6 +64,34 @@
                 "Name": "FullscreenPipeline",
                 "Path": "Passes/FullscreenPipeline.pass"
             },
+            {
+                "Name": "MultiGPUPipeline",
+                "Path": "Passes/MultiGPUPipeline.pass"
+            },
+            {
+                "Name": "MultiGPUCopyTestPipeline",
+                "Path": "Passes/MultiGPUCopyTestPipeline.pass"
+            },
+            {
+                "Name": "MultiGPUCopyImageToBufferPassTemplate",
+                "Path": "Passes/MultiGPUCopyImageToBuffer.pass"
+            },
+            {
+                "Name": "MultiGPUCopyBufferToBufferPassTemplate",
+                "Path": "Passes/MultiGPUCopyBufferToBuffer.pass"
+            },
+            {
+                "Name": "MultiGPUCopyBufferToImagePassTemplate",
+                "Path": "Passes/MultiGPUCopyBufferToImage.pass"
+            },
+            {
+                "Name": "MultiGPUCompositePassTemplate",
+                "Path": "Passes/MultiGPUCompositePass.pass"
+            },
+            {
+                "Name": "MultiGPUTrianglePassTemplate",
+                "Path": "Passes/MultiGPUTrianglePass.pass"
+            },
             {
                 "Name": "ReadbackFillerPassTemplate",
                 "Path": "Passes/ReadbackFiller.pass"

+ 54 - 0
Passes/MultiGPUCompositePass.pass

@@ -0,0 +1,54 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "PassAsset",
+    "ClassData": {
+        "PassTemplate": {
+            "Name": "MultiGPUCompositePassTemplate",
+            "PassClass": "FullScreenTriangle",
+            "Slots": [
+                {
+                    "Name": "Input1",
+                    "ShaderInputName": "m_image1",
+                    "SlotType": "Input",
+                    "ScopeAttachmentUsage": "Shader"
+                },
+                {
+                    "Name": "Input2",
+                    "ShaderInputName": "m_image2",
+                    "SlotType": "Input",
+                    "ScopeAttachmentUsage": "Shader"
+                },
+                {
+                    "Name": "Output",
+                    "SlotType": "Output",
+                    "ScopeAttachmentUsage": "RenderTarget"
+                }
+            ],
+            "ImageAttachments": [
+                {
+                    "Name": "OutputAttachment",
+                    "SizeSource": {
+                        "Source": {
+                            "Pass": "Parent",
+                            "Attachment": "PipelineOutput"
+                        }
+                    },
+                    "FormatSource": {
+                        "Pass": "Parent",
+                        "Attachment": "PipelineOutput"
+                    }
+                }
+            ],
+            "Connections": [
+                {
+                    "LocalSlot": "Output",
+                    "AttachmentRef": {
+                        "Pass": "This",
+                        "Attachment": "OutputAttachment"
+                    }
+                }
+            ]
+        }
+    }
+}

+ 52 - 0
Passes/MultiGPUCopyBufferToBuffer.pass

@@ -0,0 +1,52 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "PassAsset",
+    "ClassData": {
+        "PassTemplate": {
+            "Name": "MultiGPUCopyImageToBufferPassTemplate",
+            "PassClass": "CopyPass",
+            "Slots": [
+                {
+                    "Name": "Input",
+                    "SlotType": "Input",
+                    "ScopeAttachmentUsage": "Copy",
+                    "BufferViewDesc": {
+                        "m_elementOffset": 0,
+                        "m_elementCount": 153600,
+                        "m_elementSize": 4
+                    }
+                },
+                {
+                    "Name": "Output",
+                    "SlotType": "Output",
+                    "ScopeAttachmentUsage": "Copy",
+                    "BufferViewDesc": {
+                        "m_elementOffset": 0,
+                        "m_elementCount": 153600,
+                        "m_elementSize": 4
+                    }
+                }
+            ],
+            "BufferAttachments": [
+                {
+                    "Name": "OutputAttachment",
+                    "BufferDescriptor": {
+                        "m_byteCount": 614400,
+                        "m_alignment": 16,
+                        "m_bindFlags": "ShaderReadWrite"
+                    }
+                }
+            ],
+            "Connections": [
+                {
+                    "LocalSlot": "Output",
+                    "AttachmentRef": {
+                        "Pass": "This",
+                        "Attachment": "OutputAttachment"
+                    }
+                }
+            ]
+        }
+    }
+}

+ 50 - 0
Passes/MultiGPUCopyBufferToImage.pass

@@ -0,0 +1,50 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "PassAsset",
+    "ClassData": {
+        "PassTemplate": {
+            "Name": "MultiGPUCopyImageToBufferPassTemplate",
+            "PassClass": "CopyPass",
+            "Slots": [
+                {
+                    "Name": "Input",
+                    "SlotType": "Input",
+                    "ScopeAttachmentUsage": "Copy",
+                    "BufferViewDesc": {
+                        "m_elementOffset": 0,
+                        "m_elementCount": 153600,
+                        "m_elementSize": 4
+                    }
+                },
+                {
+                    "Name": "Output",
+                    "SlotType": "Output",
+                    "ScopeAttachmentUsage": "Copy"
+                }
+            ],
+            "ImageAttachments": [
+                {
+                    "Name": "OutputAttachment",
+                    "ImageDescriptor": {
+                        "Format": "R8G8B8A8_UNORM",
+                        "Size": {
+                            "Width": 320,
+                            "Height": 480,
+                            "Depth": 1
+                        }
+                    }
+                }
+            ],
+            "Connections": [
+                {
+                    "LocalSlot": "Output",
+                    "AttachmentRef": {
+                        "Pass": "This",
+                        "Attachment": "OutputAttachment"
+                    }
+                }
+            ]
+        }
+    }
+}

+ 47 - 0
Passes/MultiGPUCopyImageToBuffer.pass

@@ -0,0 +1,47 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "PassAsset",
+    "ClassData": {
+        "PassTemplate": {
+            "Name": "MultiGPUCopyImageToBufferPassTemplate",
+            "PassClass": "CopyPass",
+            "Slots": [
+                {
+                    "Name": "Input",
+                    "SlotType": "Input",
+                    "ScopeAttachmentUsage": "Copy"
+                },
+                {
+                    "Name": "Output",
+                    "SlotType": "Output",
+                    "ScopeAttachmentUsage": "Copy",
+                    "BufferViewDesc": {
+                        "m_elementOffset": 0,
+                        "m_elementCount": 153600,
+                        "m_elementSize": 4
+                    }
+                }
+            ],
+            "BufferAttachments": [
+                {
+                    "Name": "OutputAttachment",
+                    "BufferDescriptor": {
+                        "m_byteCount": 614400,
+                        "m_alignment": 16,
+                        "m_bindFlags": "ShaderReadWrite"
+                    }
+                }
+            ],
+            "Connections": [
+                {
+                    "LocalSlot": "Output",
+                    "AttachmentRef": {
+                        "Pass": "This",
+                        "Attachment": "OutputAttachment"
+                    }
+                }
+            ]
+        }
+    }
+}

+ 299 - 0
Passes/MultiGPUCopyTestPipeline.pass

@@ -0,0 +1,299 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "PassAsset",
+    "ClassData": {
+        "PassTemplate": {
+            "Name": "MultiGPUCopyTestPipeline",
+            "PassClass": "ParentPass",
+            "Slots": [
+                {
+                    "Name": "PipelineOutput",
+                    "SlotType": "InputOutput",
+                    "ScopeAttachmentUsage": "RenderTarget"
+                }
+            ],
+            "ImageAttachments": [
+                {
+                    "Name": "TriangleAttachment1",
+                    "ImageDescriptor": {
+                        "Format": "R8G8B8A8_UNORM",
+                        "Size": {
+                            "Width": 320,
+                            "Height": 480,
+                            "Depth": 1
+                        }
+                    }
+                },
+                {
+                    "Name": "TriangleAttachment2",
+                    "ImageDescriptor": {
+                        "Format": "R8G8B8A8_UNORM",
+                        "Size": {
+                            "Width": 320,
+                            "Height": 480,
+                            "Depth": 1
+                        }
+                    }
+                }
+            ],
+            "PassRequests": [
+                {
+                    "Name": "TrianglePass1",
+                    "TemplateName": "MultiGPUTrianglePassTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "Output",
+                            "AttachmentRef": {
+                                "Pass": "Parent",
+                                "Attachment": "TriangleAttachment1"
+                            }
+                        }
+                    ],
+                    "PassData": {
+                        "$type": "FullscreenTrianglePassData",
+                        "ShaderAsset": {
+                            "FilePath": "Shaders/MultiGPURPIExample/Triangle.shader"
+                        },
+                        "ShaderDataMappings": {
+                            "Matrix4x4Mappings": [
+                                {
+                                    "Name": "m_objectMatrix",
+                                    "Value": [
+                                        2.0,
+                                        0.0,
+                                        0.0,
+                                        0.0,
+                                        0.0,
+                                        1.0,
+                                        0.0,
+                                        0.0,
+                                        0.0,
+                                        0.0,
+                                        1.0,
+                                        0.0,
+                                        1.0,
+                                        0.0,
+                                        0.0,
+                                        1.0
+                                    ]
+                                }
+                            ]
+                        }
+                    }
+                },
+                {
+                    "Name": "TrianglePass2",
+                    "TemplateName": "MultiGPUTrianglePassTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "Output",
+                            "AttachmentRef": {
+                                "Pass": "Parent",
+                                "Attachment": "TriangleAttachment2"
+                            }
+                        }
+                    ],
+                    "PassData": {
+                        "$type": "FullscreenTrianglePassData",
+                        "ShaderAsset": {
+                            "FilePath": "Shaders/MultiGPURPIExample/Triangle.shader"
+                        },
+                        "DeviceIndex": 1,
+                        "ShaderDataMappings": {
+                            "Matrix4x4Mappings": [
+                                {
+                                    "Name": "m_objectMatrix",
+                                    "Value": [
+                                        2.0,
+                                        0.0,
+                                        0.0,
+                                        0.0,
+                                        0.0,
+                                        1.0,
+                                        0.0,
+                                        0.0,
+                                        0.0,
+                                        0.0,
+                                        1.0,
+                                        0.0,
+                                        -1.0,
+                                        0.0,
+                                        0.0,
+                                        1.0
+                                    ]
+                                }
+                            ]
+                        }
+                    }
+                },
+                {
+                    "Name": "CopyPass1",
+                    "TemplateName": "MultiGPUCopyImageToBufferPassTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "Input",
+                            "AttachmentRef": {
+                                "Pass": "TrianglePass2",
+                                "Attachment": "Output"
+                            }
+                        }
+                    ],
+                    "PassData": {
+                        "$type": "CopyPassData",
+                        "CloneInput": false,
+                        "SourceDeviceIndex": 1,
+                        "DestinationDeviceIndex": 0
+                    }
+                },
+                {
+                    "Name": "CopyPass2",
+                    "TemplateName": "MultiGPUCopyBufferToBufferPassTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "Input",
+                            "AttachmentRef": {
+                                "Pass": "CopyPass1",
+                                "Attachment": "Output"
+                            }
+                        }
+                    ],
+                    "PassData": {
+                        "$type": "CopyPassData",
+                        "CloneInput": true,
+                        "SourceDeviceIndex": 0,
+                        "DestinationDeviceIndex": 1
+                    }
+                },
+                {
+                    "Name": "CopyPass3",
+                    "TemplateName": "MultiGPUCopyBufferToImagePassTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "Input",
+                            "AttachmentRef": {
+                                "Pass": "CopyPass2",
+                                "Attachment": "Output"
+                            }
+                        }
+                    ],
+                    "PassData": {
+                        "$type": "CopyPassData",
+                        "CloneInput": false,
+                        "SourceDeviceIndex": 1,
+                        "DestinationDeviceIndex": 0,
+                        "BufferSourceOffset": 0,
+                        "BufferSourceBytesPerRow": 1280,
+                        "BufferSourceBytesPerImage": 614400,
+                        "ImageSourceSize": {
+                            "Width": 320,
+                            "Height": 480
+                        }
+                    }
+                },
+                {
+                    "Name": "CopyPass4",
+                    "TemplateName": "CopyPassTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "Input",
+                            "AttachmentRef": {
+                                "Pass": "CopyPass3",
+                                "Attachment": "Output"
+                            }
+                        }
+                    ],
+                    "PassData": {
+                        "$type": "CopyPassData",
+                        "CloneInput": true,
+                        "SourceDeviceIndex": 0,
+                        "DestinationDeviceIndex": 1
+                    }
+                },
+                {
+                    "Name": "CopyPass5",
+                    "TemplateName": "CopyPassTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "Input",
+                            "AttachmentRef": {
+                                "Pass": "CopyPass4",
+                                "Attachment": "Output"
+                            }
+                        }
+                    ],
+                    "PassData": {
+                        "$type": "CopyPassData",
+                        "CloneInput": true,
+                        "SourceDeviceIndex": 1,
+                        "DestinationDeviceIndex": 0
+                    }
+                },
+                {
+                    "Name": "CompositePass",
+                    "TemplateName": "MultiGPUCompositePassTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "Input1",
+                            "AttachmentRef": {
+                                "Pass": "TrianglePass1",
+                                "Attachment": "Output"
+                            }
+                        },
+                        {
+                            "LocalSlot": "Input2",
+                            "AttachmentRef": {
+                                "Pass": "CopyPass5",
+                                "Attachment": "Output"
+                            }
+                        }
+                    ],
+                    "PassData": {
+                        "$type": "FullscreenTrianglePassData",
+                        "ShaderAsset": {
+                            "FilePath": "Shaders/MultiGPURPIExample/Composite.shader"
+                        }
+                    }
+                },
+                {
+                    "Name": "ImGuiPass",
+                    "TemplateName": "ImGuiPassTemplate",
+                    "Enabled": true,
+                    "Connections": [
+                        {
+                            "LocalSlot": "InputOutput",
+                            "AttachmentRef": {
+                                "Pass": "CompositePass",
+                                "Attachment": "Output"
+                            }
+                        }
+                    ],
+                    "PassData": {
+                        "$type": "ImGuiPassData",
+                        "IsDefaultImGui": true
+                    }
+                },
+                {
+                    "Name": "CopyToSwapChain",
+                    "TemplateName": "FullscreenCopyTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "Input",
+                            "AttachmentRef": {
+                                "Pass": "ImGuiPass",
+                                "Attachment": "InputOutput"
+                            }
+                        },
+                        {
+                            "LocalSlot": "Output",
+                            "AttachmentRef": {
+                                "Pass": "Parent",
+                                "Attachment": "PipelineOutput"
+                            }
+                        }
+                    ]
+                }
+            ]
+        }
+    }
+}

+ 230 - 0
Passes/MultiGPUPipeline.pass

@@ -0,0 +1,230 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "PassAsset",
+    "ClassData": {
+        "PassTemplate": {
+            "Name": "MultiGPUPipeline",
+            "PassClass": "ParentPass",
+            "Slots": [
+                {
+                    "Name": "PipelineOutput",
+                    "SlotType": "InputOutput",
+                    "ScopeAttachmentUsage": "RenderTarget"
+                }
+            ],
+            "ImageAttachments": [
+                {
+                    "Name": "TriangleAttachment1",
+                    "SizeSource": {
+                        "Source": {
+                            "Pass": "This",
+                            "Attachment": "PipelineOutput"
+                        },
+                        "Multipliers": {
+                            "WidthMultiplier": 0.5
+                        }
+                    },
+                    "ImageDescriptor": {
+                        "Format": "R8G8B8A8_UNORM"
+                    }
+                },
+                {
+                    "Name": "TriangleAttachment2",
+                    "SizeSource": {
+                        "Source": {
+                            "Pass": "This",
+                            "Attachment": "PipelineOutput"
+                        },
+                        "Multipliers": {
+                            "WidthMultiplier": 0.5
+                        }
+                    },
+                    "ImageDescriptor": {
+                        "Format": "R8G8B8A8_UNORM"
+                    }
+                }
+            ],
+            "PassRequests": [
+                {
+                    "Name": "TrianglePass1",
+                    "TemplateName": "MultiGPUTrianglePassTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "Output",
+                            "AttachmentRef": {
+                                "Pass": "Parent",
+                                "Attachment": "TriangleAttachment1"
+                            }
+                        }
+                    ],
+                    "PassData": {
+                        "$type": "FullscreenTrianglePassData",
+                        "ShaderAsset": {
+                            "FilePath": "Shaders/MultiGPURPIExample/Triangle.shader"
+                        },
+                        "ShaderDataMappings": {
+                            "Matrix4x4Mappings": [
+                                {
+                                    "Name": "m_objectMatrix",
+                                    "Value": [
+                                        2.0,
+                                        0.0,
+                                        0.0,
+                                        0.0,
+                                        0.0,
+                                        1.0,
+                                        0.0,
+                                        0.0,
+                                        0.0,
+                                        0.0,
+                                        1.0,
+                                        0.0,
+                                        1.0,
+                                        0.0,
+                                        0.0,
+                                        1.0
+                                    ]
+                                }
+                            ]
+                        }
+                    }
+                },
+                {
+                    "Name": "TrianglePass2",
+                    "TemplateName": "MultiGPUTrianglePassTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "Output",
+                            "AttachmentRef": {
+                                "Pass": "Parent",
+                                "Attachment": "TriangleAttachment2"
+                            }
+                        }
+                    ],
+                    "PassData": {
+                        "$type": "FullscreenTrianglePassData",
+                        "ShaderAsset": {
+                            "FilePath": "Shaders/MultiGPURPIExample/Triangle.shader"
+                        },
+                        "DeviceIndex": 1,
+                        "ShaderDataMappings": {
+                            "Matrix4x4Mappings": [
+                                {
+                                    "Name": "m_objectMatrix",
+                                    "Value": [
+                                        2.0,
+                                        0.0,
+                                        0.0,
+                                        0.0,
+                                        0.0,
+                                        1.0,
+                                        0.0,
+                                        0.0,
+                                        0.0,
+                                        0.0,
+                                        1.0,
+                                        0.0,
+                                        -1.0,
+                                        0.0,
+                                        0.0,
+                                        1.0
+                                    ]
+                                }
+                            ]
+                        }
+                    }
+                },
+                {
+                    "Name": "CopyPass",
+                    "TemplateName": "CopyPassTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "Input",
+                            "AttachmentRef": {
+                                "Pass": "TrianglePass2",
+                                "Attachment": "Output"
+                            }
+                        },
+                        {
+                            "LocalSlot": "Output",
+                            "AttachmentRef": {
+                                "Pass": "TrianglePass2",
+                                "Attachment": "Output"
+                            }
+                        }
+                    ],
+                    "PassData": {
+                        "$type": "CopyPassData",
+                        "DestinationDeviceIndex": 0,
+                        "SourceDeviceIndex": 1
+                    }
+                },
+                {
+                    "Name": "CompositePass",
+                    "TemplateName": "MultiGPUCompositePassTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "Input1",
+                            "AttachmentRef": {
+                                "Pass": "TrianglePass1",
+                                "Attachment": "Output"
+                            }
+                        },
+                        {
+                            "LocalSlot": "Input2",
+                            "AttachmentRef": {
+                                "Pass": "CopyPass",
+                                "Attachment": "Output"
+                            }
+                        }
+                    ],
+                    "PassData": {
+                        "$type": "FullscreenTrianglePassData",
+                        "ShaderAsset": {
+                            "FilePath": "Shaders/MultiGPURPIExample/Composite.shader"
+                        }
+                    }
+                },
+                {
+                    "Name": "ImGuiPass",
+                    "TemplateName": "ImGuiPassTemplate",
+                    "Enabled": true,
+                    "Connections": [
+                        {
+                            "LocalSlot": "InputOutput",
+                            "AttachmentRef": {
+                                "Pass": "CompositePass",
+                                "Attachment": "Output"
+                            }
+                        }
+                    ],
+                    "PassData": {
+                        "$type": "ImGuiPassData",
+                        "IsDefaultImGui": true
+                    }
+                },
+                {
+                    "Name": "CopyToSwapChain",
+                    "TemplateName": "FullscreenCopyTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "Input",
+                            "AttachmentRef": {
+                                "Pass": "ImGuiPass",
+                                "Attachment": "InputOutput"
+                            }
+                        },
+                        {
+                            "LocalSlot": "Output",
+                            "AttachmentRef": {
+                                "Pass": "Parent",
+                                "Attachment": "PipelineOutput"
+                            }
+                        }
+                    ]
+                }
+            ]
+        }
+    }
+}

+ 29 - 0
Passes/MultiGPUTrianglePass.pass

@@ -0,0 +1,29 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "PassAsset",
+    "ClassData": {
+        "PassTemplate": {
+            "Name": "MultiGPUTrianglePassTemplate",
+            "PassClass": "FullScreenTriangle",
+            "Slots": [
+                {
+                    "Name": "Output",
+                    "SlotType": "Output",
+                    "ScopeAttachmentUsage": "RenderTarget",
+                    "LoadStoreAction": {
+                        "ClearValue": {
+                            "Value": [
+                                0.0,
+                                0.0,
+                                0.0,
+                                0.0
+                            ]
+                        },
+                        "LoadAction": "Clear"
+                    }
+                }
+            ]
+        }
+    }
+}

+ 44 - 0
Shaders/MultiGPURPIExample/Composite.azsl

@@ -0,0 +1,44 @@
+/*
+ * 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 <Atom/Features/SrgSemantics.azsli>
+
+#include <Atom/Features/PostProcessing/FullscreenPixelInfo.azsli>
+#include <Atom/Features/PostProcessing/FullscreenVertex.azsli>
+
+ShaderResourceGroup PassSrg : SRG_PerPass
+{
+    Texture2D<float4> m_image1;
+    Texture2D<float4> m_image2;
+
+    Sampler m_sampler
+    {
+        MinFilter = Linear;
+        MagFilter = Linear;
+        MipFilter = Linear;
+        AddressU = Clamp;
+        AddressV = Clamp;
+        AddressW = Clamp;
+    };
+}
+
+PSOutput MainPS(VSOutput IN)
+{
+    PSOutput OUT;
+
+    if(IN.m_texCoord.x <= 0.5)
+    {
+        OUT.m_color = PassSrg::m_image1.Sample(PassSrg::m_sampler, float2(IN.m_texCoord.x * 2, IN.m_texCoord.y));
+    }
+    else
+    {
+        OUT.m_color = PassSrg::m_image2.Sample(PassSrg::m_sampler, float2(IN.m_texCoord.x * 2 - 1, IN.m_texCoord.y));
+    }
+
+    return OUT;
+}

+ 22 - 0
Shaders/MultiGPURPIExample/Composite.shader

@@ -0,0 +1,22 @@
+{
+    "Source" : "Composite.azsl",
+
+    "DepthStencilState" : { 
+        "Depth" : { "Enable" : false, "CompareFunc" : "GreaterEqual" }
+    },
+
+    "ProgramSettings":
+    {
+      "EntryPoints":
+      [
+        {
+          "name": "MainVS",
+          "type": "Vertex"
+        },
+        {
+          "name": "MainPS",
+          "type": "Fragment"
+        }
+      ]
+    }
+}

+ 66 - 0
Shaders/MultiGPURPIExample/Triangle.azsl

@@ -0,0 +1,66 @@
+/*
+ * 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 <Atom/Features/SrgSemantics.azsli>
+
+ShaderResourceGroup PassSrg : SRG_PerPass
+{
+    column_major float4x4 m_objectMatrix;
+}
+
+struct VSInput
+{
+    uint m_vertexID : SV_VertexID;
+};
+
+struct VSOutput
+{
+    float4 m_position : SV_Position;
+    float4 m_color : COLOR0;
+};
+
+VSOutput MainVS(VSInput vsInput)
+{
+    VSOutput OUT;
+
+    switch(vsInput.m_vertexID)
+    {
+    case 0:
+        OUT.m_position = float4(0.0,  0.5, 0.0, 1.0);
+        OUT.m_color = float4(1.0, 0.0, 0.0, 1.0);
+        break;
+    case 1:
+        OUT.m_position = float4(-0.5, -0.5, 0.0, 1.0);
+        OUT.m_color = float4(0.0, 1.0, 0.0, 1.0);
+        break;
+    default:
+        OUT.m_position = float4(0.5, -0.5, 0.0, 1.0);
+        OUT.m_color = float4(0.0, 0.0, 1.0, 1.0);
+        break;
+    }
+
+    OUT.m_position = mul(PassSrg::m_objectMatrix, OUT.m_position);
+
+    return OUT;
+}
+
+struct PSOutput
+{
+    float4 m_color : SV_Target0;
+};
+
+PSOutput MainPS(VSOutput vsOutput)
+{
+    PSOutput OUT;
+
+    OUT.m_color = vsOutput.m_color;
+
+    // Simple tonemapping:
+    OUT.m_color.rgb = pow(OUT.m_color.rgb, 1.0/2.2);
+    return OUT;
+}

+ 22 - 0
Shaders/MultiGPURPIExample/Triangle.shader

@@ -0,0 +1,22 @@
+{
+    "Source" : "Triangle.azsl",
+
+    "DepthStencilState" : { 
+        "Depth" : { "Enable" : false, "CompareFunc" : "GreaterEqual" }
+    },
+
+    "ProgramSettings":
+    {
+      "EntryPoints":
+      [
+        {
+          "name": "MainVS",
+          "type": "Vertex"
+        },
+        {
+          "name": "MainPS",
+          "type": "Fragment"
+        }
+      ]
+    }
+}

+ 64 - 0
Shaders/RHI/MultiGPUComposite.azsl

@@ -0,0 +1,64 @@
+/*
+ * 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 <Atom/Features/SrgSemantics.azsli>
+
+ShaderResourceGroup CompositeSrg : SRG_PerObject
+{
+    Texture2D m_inputTextureLeft;
+    Texture2D m_inputTextureRight;
+
+    Sampler m_sampler
+    {
+        MinFilter = Linear;
+        MagFilter = Linear;
+        MipFilter = Linear;
+        AddressU = Clamp;
+        AddressV = Clamp;
+        AddressW = Clamp;
+    };
+}
+
+struct VSInput 
+{
+    float3 m_position : POSITION;
+    float2 m_uv : UV0;
+};
+
+struct VSOutput
+{
+    float4 m_position : SV_Position;
+    float2 m_uv : UV0;
+};
+
+VSOutput MainVS(VSInput vsInput)
+{
+    VSOutput OUT;
+    OUT.m_position = float4(vsInput.m_position, 1.0);
+    OUT.m_uv = vsInput.m_uv;
+    return OUT;
+}
+
+struct PSOutput
+{
+    float4 m_color : SV_Target0;
+};
+
+PSOutput MainPS(VSOutput psInput)
+{
+    PSOutput OUT;
+    if(psInput.m_uv.x <= 0.5)
+    {
+        OUT.m_color = CompositeSrg::m_inputTextureLeft.Sample(CompositeSrg::m_sampler, psInput.m_uv);
+    }
+    else
+    {
+        OUT.m_color = CompositeSrg::m_inputTextureRight.Sample(CompositeSrg::m_sampler, psInput.m_uv);
+    }
+	return OUT;
+}

+ 22 - 0
Shaders/RHI/MultiGPUComposite.shader

@@ -0,0 +1,22 @@
+{
+    "Source": "MultiGPUComposite.azsl",
+    "DepthStencilState": {
+        "Depth": {
+            "Enable": false,
+            "CompareFunc": "GreaterEqual"
+        }
+    },
+    "DrawList": "forward",
+    "ProgramSettings": {
+        "EntryPoints": [
+            {
+                "name": "MainVS",
+                "type": "Vertex"
+            },
+            {
+                "name": "MainPS",
+                "type": "Fragment"
+            }
+        ]
+    }
+}

+ 10 - 0
atomsampleviewer_asset_files.cmake

@@ -17,6 +17,9 @@ set(FILES
     Passes/CheckerboardPipeline.pass
     Passes/Fullscreen.pass
     Passes/FullscreenPipeline.pass
+    Passes/MultiGPUPipeline.pass
+    Passes/MultiGPUCompositePass.pass
+    Passes/MultiGPUTrianglePass.pass
     Passes/RayTracingAmbientOcclusion.pass
     Passes/ReadbackFiller.pass
     Passes/ReadbackPipeline.pass
@@ -26,6 +29,7 @@ set(FILES
     Passes/RHISamplePipeline.pass
     Passes/SelectorPass.pass
     Passes/SsaoPipeline.pass
+    Passes/ASV/PassTemplates.azasset
     scripts/AreaLightTest.bv.lua
     scripts/AuxGeom.bv.lua
     scripts/CheckerboardTest.bv.lua
@@ -63,6 +67,10 @@ set(FILES
     Shaders/Instanced.azsl
     Shaders/DynamicDraw/DynamicDrawExample.azsl
     Shaders/DynamicDraw/DynamicDrawExample.shader
+    Shaders/MultiGPURPIExample/Composite.azsl
+    Shaders/MultiGPURPIExample/Composite.shader
+    Shaders/MultiGPURPIExample/Triangle.azsl
+    Shaders/MultiGPURPIExample/Triangle.shader
     Shaders/OptimizationTests/DummyTransformColor.azsl
     Shaders/OptimizationTests/DummyTransformColor.shader
     Shaders/PostProcessing/ColorInvertCS.azsl
@@ -117,6 +125,8 @@ set(FILES
     Shaders/RHI/MRTTarget.shader
     Shaders/RHI/MSAAResolve.azsl
     Shaders/RHI/MSAAResolve.shader
+    Shaders/RHI/MultiGPUComposite.azsl
+    Shaders/RHI/MultiGPUComposite.shader
     Shaders/RHI/MultipleViewsDepth.azsl
     Shaders/RHI/MultipleViewsDepth.shader
     Shaders/RHI/MultipleViewsShadow.azsl