Quellcode durchsuchen

Expand Bindless RHI sample to test all bindless resource types (#626)

* Expand RHi Bindless sample to include coverage for read only buffers, read write buffers, read write textures and read only cubemap textures

Signed-off-by: moudgils <[email protected]>

* Minor fix related to Bindless RHI sample

Signed-off-by: moudgils <[email protected]>

---------

Signed-off-by: moudgils <[email protected]>
moudgils vor 2 Jahren
Ursprung
Commit
445dccb6d4

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

@@ -256,7 +256,7 @@ namespace AtomSampleViewer
 
 
                 float minimumExposure = m_exposureControlSettings->GetEyeAdaptationExposureMin();
-                if (ImGui::SliderFloat("Minumum Exposure", &minimumExposure, -16.0f, 16.0f, "%0.4f") || !m_isInitParameters)
+                if (ImGui::SliderFloat("Minimum Exposure", &minimumExposure, -16.0f, 16.0f, "%0.4f") || !m_isInitParameters)
                 {
                     m_exposureControlSettings->SetEyeAdaptationExposureMin(minimumExposure);
                     m_exposureControlSettings->OnConfigChanged();

+ 621 - 189
Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.cpp

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

+ 79 - 9
Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.h

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

+ 1 - 1
Gem/Code/Source/SampleComponentManager.cpp

@@ -288,7 +288,7 @@ namespace AtomSampleViewer
         return {
             NewRHISample<AlphaToCoverageExampleComponent>("AlphaToCoverage"),
             NewRHISample<AsyncComputeExampleComponent>("AsyncCompute"),
-            NewRHISample<BindlessPrototypeExampleComponent>("BindlessPrototype", []() {return Utils::GetRHIDevice()->GetFeatures().m_unboundedArrays; }),
+            NewRHISample<BindlessPrototypeExampleComponent>("BindlessPrototype"),
             NewRHISample<ComputeExampleComponent>("Compute"),
             NewRHISample<CopyQueueComponent>("CopyQueue"),
             NewRHISample<DualSourceBlendingComponent>("DualSourceBlending", []() {return Utils::GetRHIDevice()->GetFeatures().m_dualSourceBlending; }),

+ 23 - 0
Shaders/RHI/BindlessBufferComputeDispatch.azsl

@@ -0,0 +1,23 @@
+/*
+ * 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
+ *
+ */
+
+ShaderResourceGroupSemantic SRG_Frequency0
+{
+    FrequencyId = 0;
+};
+
+ShaderResourceGroup BufferSrg : SRG_Frequency0
+{
+    RWByteAddressBuffer m_colorBufferMultiplier;
+};
+
+[numthreads(1,1,1)]
+void MainCS(uint3 thread_id: SV_DispatchThreadID)
+{
+    BufferSrg::m_colorBufferMultiplier.Store(0, 4);  
+}

+ 15 - 0
Shaders/RHI/BindlessBufferComputeDispatch.shader

@@ -0,0 +1,15 @@
+{
+    "Source": "BindlessBufferComputeDispatch.azsl",
+
+    "ProgramSettings":
+    {
+      "EntryPoints":
+      [
+        {
+          "name": "MainCS",
+          "type": "Compute"
+        }
+      ]
+    }
+
+}

+ 23 - 0
Shaders/RHI/BindlessImageComputeDispatch.azsl

@@ -0,0 +1,23 @@
+/*
+ * 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
+ *
+ */
+
+ShaderResourceGroupSemantic SRG_Frequency0
+{
+    FrequencyId = 0;
+};
+
+ShaderResourceGroup ImageSrg : SRG_Frequency0
+{
+    RWTexture2D<float4> m_colorImageMultiplier;
+};
+
+[numthreads(1,1,1)]
+void MainCS(uint3 thread_id: SV_DispatchThreadID)
+{
+    ImageSrg::m_colorImageMultiplier[uint2(0,0)] = float4(0.5, 0.5, 0.5, 0.5);  
+}

+ 15 - 0
Shaders/RHI/BindlessImageComputeDispatch.shader

@@ -0,0 +1,15 @@
+{
+    "Source": "BindlessImageComputeDispatch.azsl",
+
+    "ProgramSettings":
+    {
+      "EntryPoints":
+      [
+        {
+          "name": "MainCS",
+          "type": "Compute"
+        }
+      ]
+    }
+
+}

+ 71 - 34
Shaders/RHI/BindlessPrototype.azsl

@@ -12,29 +12,34 @@
 
 #include <Atom/Features/Bindless.azsli>
 
+// Material type to test read only buffer, read write buffer, read write texture
 struct BindlessMaterial0
 {
-    uint4 materialIndex;
-
-    float4 m_diffuseColor;
+    uint materialIndex;
+    uint m_colorBufferId; // id to to robuffer
+    uint m_colorBufferMultiplierBufferId; // id to rwbuffer
+    uint m_colorImageMultiplierBufferId; // id to rwtexture
 };
 
+// Material type to test unbounded array in a non-bindless srg
 struct BindlessMaterial1
 {
-    uint4 materialIndex;
-
-    float4 m_diffuseColor;
-    uint m_diffuseTextureIndex;
+    uint materialIndex;
+    uint m_diffuseTextureIndex; // id to read only texture
 };
 
+// Material type to test read only texture
 struct BindlessMaterial2
 {
-    uint4 materialIndex;
+    uint materialIndex;
+    uint m_diffuseTextureIndex;  // id to rotexture
+};
 
-    float4 m_diffuseColor;
-    uint m_diffuseTextureIndex;
-    uint m_normalTextureIndex;
-    uint m_specularTextureIndex;
+// Material type to test read only cubemap texture
+struct BindlessMaterial3
+{
+    uint materialIndex;
+    uint m_cubeTextureIndex; // id to read only cube map texture
 };
 
 struct PerObject
@@ -114,55 +119,87 @@ PixelOutput MainPS(VertexOutput psInput)
     uint offset = 0;
     
     // Read the material index to identify the material type
-    uint4 materialIndex;
-    {
-        ReadFromFloatBuffer(materialIndex, HandleSrg::m_materialHandle, offset);
-    }
+    uint materialIndex;
+    ReadFromFloatBuffer(materialIndex, HandleSrg::m_materialHandle, offset);
     
-    // Read the material data from the FloatBuffer depending ont he material index
+    // Read the material data from the FloatBuffer depending on the material index.
+    // BindlessMaterial0 is suppose to test the following resource types
+    //     roBuffer - We read the color value from a roBuffer via Bindless SRG
+    //     rwBuffer - We read a multiplier vlue from a rwBuffer via Bindless SRG
+    //     roTexture - We read another color multiplier from a rwTexture via Bindless SRG
     if(materialIndex.x == 0) // Albedo material
     {
         BindlessMaterial0 bindlessMaterial0; 
-        ReadFromFloatBuffer(bindlessMaterial0.m_diffuseColor, HandleSrg::m_materialHandle, offset);
+        //Read all the ids for the resources in question
+        ReadFromFloatBuffer(bindlessMaterial0.m_colorBufferId, HandleSrg::m_materialHandle, offset);
+        ReadFromFloatBuffer(bindlessMaterial0.m_colorBufferMultiplierBufferId, HandleSrg::m_materialHandle, offset);
+        ReadFromFloatBuffer(bindlessMaterial0.m_colorImageMultiplierBufferId, HandleSrg::m_materialHandle, offset);
         
-        OUT.m_color = float4(bindlessMaterial0.m_diffuseColor.xyz, 1.0);
+        //Get the bindless resource id and then extract the color value from roBuffer
+        uint colorDataIndex = IndirectionBufferSrg::m_bufferIndirectionBuffer.Load(bindlessMaterial0.m_colorBufferId * 4);
+        ByteAddressBuffer colorData = Bindless::GetByteAddressBuffer(colorDataIndex);
+        float4 colorVal = asfloat(colorData.Load4(0));
+
+        //Get the bindless resource id and then extract the color multiplier from rwBuffer
+        uint colorBufferMultiplierIndex = IndirectionBufferSrg::m_bufferIndirectionBuffer.Load(bindlessMaterial0.m_colorBufferMultiplierBufferId * 4);
+        RWByteAddressBuffer colorMultiplier = Bindless::GetRWByteAddressBuffer(colorBufferMultiplierIndex);
+        uint colorMultiplierVal = colorMultiplier.Load(0);
+
+        //Get the bindless resource id and then extract another color multiplier from rwTexture
+        uint colorImageMultiplierIndex = IndirectionBufferSrg::m_imageIndirectionBuffer.Load(bindlessMaterial0.m_colorImageMultiplierBufferId * 4);
+        RWTexture2D<float4> colorImageMultiplier = Bindless::GetRWTexture2D(colorImageMultiplierIndex);
+        float colorImageMultiplierVal = colorImageMultiplier.Load(0).x;
+       
+        //Calculate the final color
+        OUT.m_color = colorVal * colorMultiplierVal * colorImageMultiplierVal;
     }
-    else if(materialIndex.x == 1) // Texture sample material
+    else if(materialIndex.x == 1) // Test unbounded array in a non-bindless srg. For metal we fallback to bindless srg
     {
         BindlessMaterial1 bindlessMaterial1; 
-        ReadFromFloatBuffer(bindlessMaterial1.m_diffuseColor, HandleSrg::m_materialHandle, offset);
+        //Read the id for read only texture
         ReadFromFloatBuffer(bindlessMaterial1.m_diffuseTextureIndex, HandleSrg::m_materialHandle, offset);
         
+        //Extract the color value. UB_DIRECTBINDING_NOTSUPPPORTED assumes that the RHI can not support unbounded arrays in a non-bindless SRG
 #ifdef UB_DIRECTBINDING_NOTSUPPPORTED 
         Texture2D texture = Bindless::GetTexture2D(
-            IndirectionBufferSrg::m_indirectionBuffer.Load(bindlessMaterial1.m_diffuseTextureIndex * 4));
+            IndirectionBufferSrg::m_imageIndirectionBuffer.Load(bindlessMaterial1.m_diffuseTextureIndex * 4));
 #else   
-        // % 8 for wrap-around texture index as specified in ImageSrg.m_textureArray
+        //% 8 for wrap-around texture index as specified in ImageSrg.m_textureArray
         Texture2D texture = ImageSrg::m_textureArray[bindlessMaterial1.m_diffuseTextureIndex % 8]; 
 #endif
-        OUT.m_color = texture.Sample(SamplerSrg::m_sampler, psInput.m_uv);
+        OUT.m_color = texture.Sample(SamplerSrg::m_sampler, psInput.m_uv) * 10;
     }
-    else if(materialIndex.x == 2) // Shaded material
+    else if(materialIndex.x == 2) // Test read only texture via Bindless SRG
     {
-        float4 color; 
         BindlessMaterial2 bindlessMaterial2; 
-        ReadFromFloatBuffer(bindlessMaterial2.m_diffuseColor, HandleSrg::m_materialHandle, offset);
+        //Read the id related to the read only texture we are testing
         ReadFromFloatBuffer(bindlessMaterial2.m_diffuseTextureIndex, HandleSrg::m_materialHandle, offset);
-        ReadFromFloatBuffer(bindlessMaterial2.m_normalTextureIndex, HandleSrg::m_materialHandle, offset);
-        ReadFromFloatBuffer(bindlessMaterial2.m_specularTextureIndex, HandleSrg::m_materialHandle, offset);
-
-        Texture2D texture = Bindless::GetTexture2D(
-            IndirectionBufferSrg::m_indirectionBuffer.Load(bindlessMaterial2.m_diffuseTextureIndex * 4));
-        color = texture.Sample(SamplerSrg::m_sampler, psInput.m_uv);
+        
+        //Get the bindless id for the texture in question
+        uint index = IndirectionBufferSrg::m_imageIndirectionBuffer.Load(bindlessMaterial2.m_diffuseTextureIndex * 4);
+        Texture2D texture = Bindless::GetTexture2D(index);
+        //Get the color value from the texture
+        float4 color = texture.Sample(SamplerSrg::m_sampler, psInput.m_uv);
 
+        //Apply basic shading
         float3 lightDir;
         uint lightOffset = 0;
         ReadFromFloatBuffer(lightDir, HandleSrg::m_lightHandle, lightOffset); 
         lightDir = normalize(-lightDir);
-
         color *= dot(lightDir, psInput.m_normal) * 8.0;
         OUT.m_color = color;
     }
+    else if(materialIndex.x == 3) // Test read only cubemap texture
+    {
+        BindlessMaterial3 bindlessMaterial3; 
+        //Read the id related to the read only cubemap texture we are testing
+        ReadFromFloatBuffer(bindlessMaterial3.m_cubeTextureIndex, HandleSrg::m_materialHandle, offset);   
+
+        //Get the bindless resource id and then extract color fromt he cubemap texture
+        uint index = IndirectionBufferSrg::m_imageIndirectionBuffer.Load(bindlessMaterial3.m_cubeTextureIndex * 4);
+        TextureCube texture = Bindless::GetTextureCube(index);
+        OUT.m_color = texture.Sample(SamplerSrg::m_sampler, float3(psInput.m_uv,1));
+    }
     else
     {
         OUT.m_color = float4(1.0, 1.0, 1.0, 1.0);

+ 2 - 1
Shaders/RHI/BindlessPrototypeSrg.azsli

@@ -42,7 +42,8 @@ ShaderResourceGroup FloatBufferSrg : FloatBufferSemanticId
 
 ShaderResourceGroup IndirectionBufferSrg : IndirectionBufferSemanticId
 {
-    ByteAddressBuffer m_indirectionBuffer;
+    ByteAddressBuffer m_imageIndirectionBuffer;
+    ByteAddressBuffer m_bufferIndirectionBuffer;
 }; 
 
 ShaderResourceGroup SamplerSrg : FrequencyPerScene