Explorar el Código

Merge branch 'development' into VRS

Akio Gaule hace 2 años
padre
commit
cb1d047c6e
Se han modificado 61 ficheros con 1763 adiciones y 856 borrados
  1. 60 0
      .clang-format
  2. 0 44
      Gem/Code/Lib/MaterialFunctors/StacksShaderCollectionFunctor.cpp
  3. 0 39
      Gem/Code/Lib/MaterialFunctors/StacksShaderCollectionFunctor.h
  4. 0 41
      Gem/Code/Lib/MaterialFunctors/StacksShaderInputFunctor.cpp
  5. 0 41
      Gem/Code/Lib/MaterialFunctors/StacksShaderInputFunctor.h
  6. 0 4
      Gem/Code/Source/AtomSampleViewerSystemComponent.cpp
  7. 6 0
      Gem/Code/Source/CommonSampleComponentBase.cpp
  8. 3 0
      Gem/Code/Source/CommonSampleComponentBase.h
  9. 9 0
      Gem/Code/Source/Platform/Android/BindlessPrototype_Traits_Platform.h
  10. 9 0
      Gem/Code/Source/Platform/Linux/BindlessPrototype_Traits_Platform.h
  11. 9 0
      Gem/Code/Source/Platform/Mac/BindlessPrototype_Traits_Platform.h
  12. 9 0
      Gem/Code/Source/Platform/Windows/BindlessPrototype_Traits_Platform.h
  13. 12 0
      Gem/Code/Source/Platform/Windows/SampleComponentManager_Windows.cpp
  14. 9 0
      Gem/Code/Source/Platform/iOS/BindlessPrototype_Traits_Platform.h
  15. 16 1
      Gem/Code/Source/RHI/BasicRHIComponent.cpp
  16. 12 0
      Gem/Code/Source/RHI/BasicRHIComponent.h
  17. 97 44
      Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.cpp
  18. 18 1
      Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.h
  19. 407 0
      Gem/Code/Source/RHI/XRExampleComponent.cpp
  20. 101 0
      Gem/Code/Source/RHI/XRExampleComponent.h
  21. 211 52
      Gem/Code/Source/SampleComponentManager.cpp
  22. 9 2
      Gem/Code/Source/SampleComponentManager.h
  23. 6 0
      Gem/Code/Source/SampleComponentManagerBus.h
  24. 296 0
      Gem/Code/Source/XRRPIExampleComponent.cpp
  25. 86 0
      Gem/Code/Source/XRRPIExampleComponent.h
  26. 0 7
      Gem/Code/Tools/AtomSampleViewerToolsSystemComponent.cpp
  27. 0 43
      Gem/Code/Tools/MaterialFunctors/StacksShaderCollectionFunctorSourceData.cpp
  28. 0 32
      Gem/Code/Tools/MaterialFunctors/StacksShaderCollectionFunctorSourceData.h
  29. 0 35
      Gem/Code/Tools/MaterialFunctors/StacksShaderInputFunctorSourceData.cpp
  30. 0 31
      Gem/Code/Tools/MaterialFunctors/StacksShaderInputFunctorSourceData.h
  31. 0 4
      Gem/Code/atomsampleviewergem_lib_files.cmake
  32. 4 0
      Gem/Code/atomsampleviewergem_private_files.cmake
  33. 0 4
      Gem/Code/atomsampleviewergem_tools_files.cmake
  34. 2 0
      Gem/Code/enabled_gems.cmake
  35. 26 0
      Materials/XR/XR_Hand_Controller_ControlerMAT.material
  36. 3 0
      Materials/XR/textures/ControlerMAT_BaseColor.png
  37. 3 0
      Materials/XR/textures/ControlerMAT_Emissive.png
  38. 3 0
      Materials/XR/textures/ControlerMAT_Roughness.png
  39. 3 0
      Objects/Left_Hand_Controller.fbx
  40. 3 0
      Objects/Right_Hand_Controller.fbx
  41. 8 0
      Passes/ASV/PassTemplates.azasset
  42. 44 0
      Passes/MainRenderPipeline.azasset
  43. 71 0
      Passes/RHISamplePipelineXRLeft.pass
  44. 71 0
      Passes/RHISamplePipelineXRRight.pass
  45. 1 0
      Scripts/MaterialScreenshotTests.bv.lua
  46. 0 3
      Shaders/ComprehensiveTestMaterial/Comprehensive.material
  47. 0 183
      Shaders/ComprehensiveTestMaterial/Comprehensive.materialtype
  48. 0 10
      Shaders/ComprehensiveTestMaterial/Comprehensive_variant.material
  49. 0 152
      Shaders/ComprehensiveTestMaterial/Stacks.azsl
  50. 0 26
      Shaders/ComprehensiveTestMaterial/Stacks.shader
  51. 0 37
      Shaders/ComprehensiveTestMaterial/Stacks.shadervariantlist
  52. 17 9
      Shaders/RHI/BindlessPrototype.azsl
  53. 4 2
      Shaders/RHI/BindlessPrototype.shader
  54. 23 5
      Shaders/RHI/BindlessPrototypeSrg.azsli
  55. 49 0
      Shaders/RHI/OpenXrSample.azsl
  56. 23 0
      Shaders/RHI/OpenXrSample.shader
  57. 3 0
      Textures/ControlerMAT_BaseColor.png
  58. 3 0
      Textures/ControlerMAT_Emissive.png
  59. 3 0
      Textures/ControlerMAT_Roughness.png
  60. 0 3
      atomsampleviewer_asset_files.cmake
  61. 11 1
      project.json

+ 60 - 0
.clang-format

@@ -0,0 +1,60 @@
+Language: Cpp
+
+AccessModifierOffset: -4
+AlignAfterOpenBracket: AlwaysBreak
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlines: Right
+AlignOperands: false
+AlignTrailingComments: false
+AllowAllArgumentsOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortFunctionsOnASingleLine: None
+AllowShortLambdasOnASingleLine: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakTemplateDeclarations: true
+BinPackParameters: false
+BreakBeforeBraces: Custom
+BraceWrapping:
+    AfterClass: true
+    AfterControlStatement: true
+    AfterEnum: true
+    AfterFunction: true
+    AfterNamespace: true
+    BeforeLambdaBody: true
+    AfterStruct: true
+    BeforeElse: true
+    SplitEmptyFunction: true
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: BeforeComma
+BreakInheritanceList: BeforeComma
+ColumnLimit: 140
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: false
+FixNamespaceComments: true
+IncludeBlocks: Preserve
+IndentCaseBlocks: true
+IndentCaseLabels: false
+IndentPPDirectives: None
+IndentWidth: 4
+KeepEmptyLinesAtTheStartOfBlocks: false
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: All
+PenaltyReturnTypeOnItsOwnLine: 1000
+PointerAlignment: Left
+SortIncludes: true
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyParentheses: false
+SpacesInAngles: false
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+Standard: c++17
+UseTab: Never

+ 0 - 44
Gem/Code/Lib/MaterialFunctors/StacksShaderCollectionFunctor.cpp

@@ -1,44 +0,0 @@
-/*
- * 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 <MaterialFunctors/StacksShaderCollectionFunctor.h>
-#include <Atom/RPI.Public/Material/Material.h>
-#include <Atom/RPI.Reflect/Shader/ShaderOptionGroup.h>
-
-namespace AtomSampleViewer
-{
-    void StacksShaderCollectionFunctor::Reflect(AZ::ReflectContext* context)
-    {
-        if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
-        {
-            serializeContext->Class<StacksShaderCollectionFunctor, AZ::RPI::MaterialFunctor>()
-                ->Version(1)
-                ->Field("m_stackCountProperty", &StacksShaderCollectionFunctor::m_stackCountProperty)
-                ->Field("m_highlightLastStackProperty", &StacksShaderCollectionFunctor::m_highlightLastStackProperty)
-                ->Field("m_highlightLastStackOption", &StacksShaderCollectionFunctor::m_highlightLastStackOption)
-                ;
-        }
-    }
-
-    void StacksShaderCollectionFunctor::Process(RuntimeContext& context)
-    {
-        using namespace AZ::RPI;
-
-        const uint32_t stackCount = context.GetMaterialPropertyValue<uint32_t>(m_stackCountProperty);
-        const bool highlightLastStack = context.GetMaterialPropertyValue<bool>(m_highlightLastStackProperty);
-
-        static const int AvailableStackCount = 4;
-        for (uint32_t i = 0; i < AvailableStackCount; ++i)
-        {
-            const bool isLastStack = (i == stackCount - 1);
-            const bool shouldHighlight = highlightLastStack && isLastStack;
-            context.SetShaderOptionValue(i, m_highlightLastStackOption, ShaderOptionValue{ shouldHighlight });
-            context.SetShaderEnabled(i, i < stackCount);
-        }
-    }
-} // namespace AtomSampleViewer

+ 0 - 39
Gem/Code/Lib/MaterialFunctors/StacksShaderCollectionFunctor.h

@@ -1,39 +0,0 @@
-/*
- * 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 <Atom/RPI.Reflect/Material/MaterialFunctor.h>
-#include <Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h>
-
-namespace AtomSampleViewer
-{
-    //! This is an example of a custom hard-coded MaterialFunctor used to dynamically select shaders/variants/passes.
-    //! It is used by comprehensive.materialtype to enable/disable variants of the "stacks" shader.
-    class StacksShaderCollectionFunctor final
-        : public AZ::RPI::MaterialFunctor
-    {
-        friend class StacksShaderCollectionFunctorSourceData;
-    public:
-        AZ_RTTI(StacksShaderCollectionFunctor, "{4E51A7D5-7DF1-4402-8975-F6C9DFDEDC1E}", AZ::RPI::MaterialFunctor);
-
-        static void Reflect(AZ::ReflectContext* context);
-        
-        using AZ::RPI::MaterialFunctor::Process;
-        void Process(RuntimeContext& context) override;
-
-    private:
-
-        // Indexes used to look up material property values at runtime
-        AZ::RPI::MaterialPropertyIndex m_stackCountProperty;
-        AZ::RPI::MaterialPropertyIndex m_highlightLastStackProperty;
-
-        // Indexes used to access ShaderOption values at runtime
-        AZ::RPI::ShaderOptionIndex m_highlightLastStackOption;
-    };
-} // namespace AtomSampleViewer

+ 0 - 41
Gem/Code/Lib/MaterialFunctors/StacksShaderInputFunctor.cpp

@@ -1,41 +0,0 @@
-/*
- * 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 <MaterialFunctors/StacksShaderInputFunctor.h>
-#include <Atom/RPI.Public/Material/Material.h>
-#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
-#include <AzCore/Math/Matrix4x4.h>
-
-namespace AtomSampleViewer
-{
-    void StacksShaderInputFunctor::Reflect(AZ::ReflectContext* context)
-    {
-        if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
-        {
-            serializeContext->Class<StacksShaderInputFunctor, AZ::RPI::MaterialFunctor>()
-                ->Version(1)
-                ->Field("m_azimuthDegreesIndex", &StacksShaderInputFunctor::m_azimuthDegreesIndex)
-                ->Field("m_elevationDegreesIndex", &StacksShaderInputFunctor::m_elevationDegreesIndex)
-                ->Field("m_lightDirectionIndex", &StacksShaderInputFunctor::m_lightDirectionIndex)
-                ;
-        }
-    }
-
-    void StacksShaderInputFunctor::Process(RuntimeContext& context)
-    {
-        float azimuthDegrees = context.GetMaterialPropertyValue<float>(m_azimuthDegreesIndex);
-        float elevationDegrees = context.GetMaterialPropertyValue<float>(m_elevationDegreesIndex);
-
-        AZ::Vector3 lightDir = AZ::Vector3(1,0,0) * AZ::Matrix4x4::CreateRotationZ(AZ::DegToRad(elevationDegrees)) * AZ::Matrix4x4::CreateRotationY(AZ::DegToRad(azimuthDegrees));
-
-        float floats[3];
-        lightDir.StoreToFloat3(floats);
-        context.GetShaderResourceGroup()->SetConstantRaw(m_lightDirectionIndex, floats, 3 * sizeof(float));
-    }
-
-} // namespace AtomSampleViewer

+ 0 - 41
Gem/Code/Lib/MaterialFunctors/StacksShaderInputFunctor.h

@@ -1,41 +0,0 @@
-/*
- * 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 <Atom/RPI.Reflect/Material/MaterialFunctor.h>
-#include <Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h>
-
-namespace AtomSampleViewer
-{
-    //! This is an example of a custom hard-coded MaterialFunctor used to perform calculations on material 
-    //! property values to produce shader input values.
-    //! It is used by comprehensive.materialtype to transform angle values into a light direction vector.
-    class StacksShaderInputFunctor final
-        : public AZ::RPI::MaterialFunctor
-    {
-        friend class StacksShaderInputFunctorSourceData;
-    public:
-        AZ_RTTI(StacksShaderInputFunctor, "{7F607170-1BC2-4510-A252-8A665FC02052}", AZ::RPI::MaterialFunctor);
-
-        static void Reflect(AZ::ReflectContext* context);
-        
-        using AZ::RPI::MaterialFunctor::Process;
-        void Process(RuntimeContext& context) override;
-
-    private:
-
-        // Indices used to look up material property values at runtime
-        AZ::RPI::MaterialPropertyIndex m_azimuthDegreesIndex;
-        AZ::RPI::MaterialPropertyIndex m_elevationDegreesIndex;
-
-        // Indices used to look up ShaderResourceGroup inputs at runtime
-        AZ::RHI::ShaderInputConstantIndex m_lightDirectionIndex;
-    };
-
-} // namespace AtomSampleViewer

+ 0 - 4
Gem/Code/Source/AtomSampleViewerSystemComponent.cpp

@@ -7,8 +7,6 @@
  */
 
 #include <AtomSampleViewerSystemComponent.h>
-#include <MaterialFunctors/StacksShaderCollectionFunctor.h>
-#include <MaterialFunctors/StacksShaderInputFunctor.h>
 #include <Automation/ImageComparisonConfig.h>
 
 #include <EntityLatticeTestComponent.h>
@@ -54,8 +52,6 @@ namespace AtomSampleViewer
         ImGuiAssetBrowser::Reflect(context);
         ImGuiSidebar::Reflect(context);
         ImGuiSaveFilePath::Reflect(context);
-        StacksShaderCollectionFunctor::Reflect(context);
-        StacksShaderInputFunctor::Reflect(context);
 
         ImageComparisonConfig::Reflect(context);
 

+ 6 - 0
Gem/Code/Source/CommonSampleComponentBase.cpp

@@ -235,6 +235,12 @@ namespace AtomSampleViewer
         }
     }
 
+    void CommonSampleComponentBase::IterateToNextLightingPreset()
+    {
+        m_currentLightingPresetIndex = (m_currentLightingPresetIndex + 1) % m_lightingPresets.size();
+        OnLightingPresetSelected(m_lightingPresets[m_currentLightingPresetIndex].m_preset, m_useAlternateSkybox);
+    }
+
     void CommonSampleComponentBase::OnLightingPresetSelected(const AZ::Render::LightingPreset& preset, bool useAlternateSkybox)
     {
         AZ::Render::SkyBoxFeatureProcessorInterface* skyboxFeatureProcessor = AZ::RPI::Scene::GetFeatureProcessorForEntityContextId<AZ::Render::SkyBoxFeatureProcessorInterface>(m_entityContextId);

+ 3 - 0
Gem/Code/Source/CommonSampleComponentBase.h

@@ -78,6 +78,9 @@ namespace AtomSampleViewer
         // Preload assets 
         void PreloadAssets(const AZStd::vector<AZ::AssetCollectionAsyncLoader::AssetToLoadInfo>& assetList);
 
+        //! Iterate to the next lighting preset in a loop
+        void IterateToNextLightingPreset();
+
         //! Async asset load
         AZ::AssetCollectionAsyncLoader m_assetLoadManager;
 

+ 9 - 0
Gem/Code/Source/Platform/Android/BindlessPrototype_Traits_Platform.h

@@ -0,0 +1,9 @@
+/*
+ * 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
+ *
+ */
+
+#define ATOMSAMPLEVIEWER_TRAIT_BINDLESS_PROTOTYPE_SUPPORTS_DIRECT_BOUND_UNBOUNDED_ARRAY       1

+ 9 - 0
Gem/Code/Source/Platform/Linux/BindlessPrototype_Traits_Platform.h

@@ -0,0 +1,9 @@
+/*
+ * 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
+ *
+ */
+
+#define ATOMSAMPLEVIEWER_TRAIT_BINDLESS_PROTOTYPE_SUPPORTS_DIRECT_BOUND_UNBOUNDED_ARRAY       1

+ 9 - 0
Gem/Code/Source/Platform/Mac/BindlessPrototype_Traits_Platform.h

@@ -0,0 +1,9 @@
+/*
+ * 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
+ *
+ */
+
+#define ATOMSAMPLEVIEWER_TRAIT_BINDLESS_PROTOTYPE_SUPPORTS_DIRECT_BOUND_UNBOUNDED_ARRAY       0

+ 9 - 0
Gem/Code/Source/Platform/Windows/BindlessPrototype_Traits_Platform.h

@@ -0,0 +1,9 @@
+/*
+ * 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
+ *
+ */
+
+#define ATOMSAMPLEVIEWER_TRAIT_BINDLESS_PROTOTYPE_SUPPORTS_DIRECT_BOUND_UNBOUNDED_ARRAY       1

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

@@ -21,11 +21,23 @@ namespace AtomSampleViewer
 
     const char* SampleComponentManager::GetRootPassTemplateName()
     {
+        // Use Low end pipeline template for VR
+        AZ::RPI::XRRenderingInterface* xrSystem = AZ::RPI::RPISystemInterface::Get()->GetXRSystem();
+        if(xrSystem)
+        {
+            return "LowEndPipelineTemplate";
+        }
         return "MainPipeline";
     }
 
     int SampleComponentManager::GetDefaultNumMSAASamples()
     {
+        // Use sample count of 1 for VR pipelines
+        AZ::RPI::XRRenderingInterface* xrSystem = AZ::RPI::RPISystemInterface::Get()->GetXRSystem();
+        if (xrSystem)
+        {
+            return 1;
+        }
         return 4;
     }
 } // namespace AtomSampleViewer

+ 9 - 0
Gem/Code/Source/Platform/iOS/BindlessPrototype_Traits_Platform.h

@@ -0,0 +1,9 @@
+/*
+ * 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
+ *
+ */
+
+#define ATOMSAMPLEVIEWER_TRAIT_BINDLESS_PROTOTYPE_SUPPORTS_DIRECT_BOUND_UNBOUNDED_ARRAY       0

+ 16 - 1
Gem/Code/Source/RHI/BasicRHIComponent.cpp

@@ -57,7 +57,7 @@ namespace AtomSampleViewer
         // The RHISamplePass template should have one owned image attachment which is the render target
         m_outputAttachment = m_ownedAttachments[0];
 
-        // Force udpate pass attachment to get currect size and save it to local variables
+        // Force update pass attachment to get correct size and save it to local variables
         m_outputAttachment->Update();
 
         // update output info for the rhi sample
@@ -90,6 +90,16 @@ namespace AtomSampleViewer
         }
     }
 
+    uint32_t RHISamplePass::GetViewIndex() const
+    {
+        return m_viewIndex;
+    }
+
+    void RHISamplePass::SetViewIndex(const uint32_t viewIndex)
+    {
+        m_viewIndex = viewIndex;
+    }
+
     bool BasicRHIComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
     {
         using namespace AZ;
@@ -610,4 +620,9 @@ namespace AtomSampleViewer
     {
         return m_viewport.m_maxY - m_viewport.m_minY;
     }
+
+    void BasicRHIComponent::SetViewIndex(const uint32_t viewIndex)
+    {
+        m_viewIndex = viewIndex;
+    }
 }

+ 12 - 0
Gem/Code/Source/RHI/BasicRHIComponent.h

@@ -66,6 +66,10 @@ namespace AtomSampleViewer
         // Pass overrides
         const AZ::RPI::PipelineViewTag& GetPipelineViewTag() const override;
 
+        // Return the view index of the pass
+        uint32_t GetViewIndex() const;
+        void SetViewIndex(const uint32_t viewIndex);
+
     protected:
         explicit RHISamplePass(const AZ::RPI::PassDescriptor& descriptor);
 
@@ -78,6 +82,9 @@ namespace AtomSampleViewer
         AZ::RPI::Ptr<AZ::RPI::PassAttachment> m_outputAttachment;
 
         AZ::RPI::PipelineViewTag m_pipelineViewTag;
+
+        // Used to determine view index for XR sample
+        uint32_t m_viewIndex = 0;
     };
 
     class BasicRHIComponent
@@ -103,6 +110,8 @@ namespace AtomSampleViewer
         
         float GetViewportHeight();
         
+        void SetViewIndex(const uint32_t viewIndex);
+       
     protected:
         AZ_DISABLE_COPY(BasicRHIComponent);
 
@@ -237,5 +246,8 @@ namespace AtomSampleViewer
 
         // whether this sample supports RHI sample render pipeline or not
         bool m_supportRHISamplePipeline = false;
+        
+        // view index. Used by XR related samples
+        uint32_t m_viewIndex = 0;
     };
 } // namespace AtomSampleViewer

+ 97 - 44
Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.cpp

@@ -52,6 +52,11 @@ namespace AtomSampleViewer
         // Randomizer, used to generate unique diffuse colors for the materials
         AZ::SimpleLcgRandom g_randomizer;
 
+        static uint32_t RandomValue()
+        {
+            return g_randomizer.GetRandom() % ImageCount;
+        }
+
         template<typename Asset, typename Instance>
         Data::Instance<Instance> CreateResourceImmediate(Data::AssetId assetId)
         {
@@ -91,8 +96,7 @@ namespace AtomSampleViewer
                 AZ::Color color;
                 color.FromU32(colorUint);
 
-                const uint32_t diffuseTextureIndex = g_randomizer.GetRandom() % InternalBP::ImageCount;
-                m_diffuseTextureIndex = diffuseTextureIndex;
+                m_diffuseTextureIndex = RandomValue();
             }
 
             const uint32_t m_materialIndex = 1u;
@@ -109,14 +113,9 @@ namespace AtomSampleViewer
                 color.FromU32(colorUint);
                 m_diffuseColor = color;
 
-                const uint32_t diffuseTextureIndex = g_randomizer.GetRandom() % InternalBP::ImageCount;
-                m_diffuseTextureIndex = diffuseTextureIndex;
-
-                const uint32_t normalTextureIndex = g_randomizer.GetRandom() + 1u % InternalBP::ImageCount;
-                m_normalTextureIndex = normalTextureIndex;
-
-                const uint32_t specularTextureIndex = g_randomizer.GetRandom() + 2u % InternalBP::ImageCount;
-                m_specularTextureIndex = specularTextureIndex;
+                m_diffuseTextureIndex = RandomValue();
+                m_normalTextureIndex = RandomValue();
+                m_specularTextureIndex = RandomValue();
             }
 
             const uint32_t m_materialIndex = 2u;
@@ -177,22 +176,15 @@ namespace AtomSampleViewer
 
             m_imguiSidebar.End();
 
-            static const auto recreate = [this]()
-            {
-                ClearObjects();
-                CreateObjects();
-            };
-
             // Recreate the objects when the quantity changed
             if (latticeChanged && enableDynamicUpdates)
             {
-                recreate();
+                Recreate();
             }
-
             // If the dynamic update is toggled
             else if (enableDynamicUpdates != previousEnableDynamicUpdates && enableDynamicUpdates == true)
             {
-                recreate();
+                Recreate();
             }
 
             if (randomizeMaterials)
@@ -202,6 +194,12 @@ namespace AtomSampleViewer
         }
     }
 
+    void BindlessPrototypeExampleComponent::Recreate()
+    {
+        ClearObjects();
+        CreateObjects();
+    }
+
     void BindlessPrototypeExampleComponent::CreateBufferPool()
     {
         m_bufferPool = RHI::Factory::Get().CreateBufferPool();
@@ -335,8 +333,10 @@ namespace AtomSampleViewer
     {
         // FloatBuffer ID
         const char* floatBufferId = "m_floatBuffer";
+#if ATOMSAMPLEVIEWER_TRAIT_BINDLESS_PROTOTYPE_SUPPORTS_DIRECT_BOUND_UNBOUNDED_ARRAY
         // TextureArray ID
         const char* textureArrayId = "m_textureArray";
+#endif
 
         m_scopeId = RHI::ScopeId("BindlessPrototype");
 
@@ -386,16 +386,22 @@ namespace AtomSampleViewer
         }
 
         // Set up the SRGs
-        {
-            // Set the FloatBufferSrg
-            m_bindlessSrg = std::make_unique<BindlessSrg>(
-                BindlessSrg(m_shader, {m_floatBufferSrgName, m_imageSrgName})
-            );
-        }
+         m_bindlessSrg = std::make_unique<BindlessSrg>(
+            BindlessSrg(m_shader, 
+                        { 
+                            m_samplerSrgName, 
+                            m_floatBufferSrgName, 
+                            m_indirectionBufferSrgName, 
+#if ATOMSAMPLEVIEWER_TRAIT_BINDLESS_PROTOTYPE_SUPPORTS_DIRECT_BOUND_UNBOUNDED_ARRAY
+                            m_imageSrgName
+#endif
+                         }));
 
         // Create the BufferPool
         CreateBufferPool();
 
+        m_bindlessSrg->CompileSrg(m_samplerSrgName);
+
         // Initialize the float buffer, and set the view
         {
             const uint32_t byteCount = m_bufferFloatCount * static_cast<uint32_t>(sizeof(float));
@@ -452,27 +458,47 @@ namespace AtomSampleViewer
             }
         }
 
-        // Set the images
+        //Load appropriate textures used by the unbounded texture array
+        AZStd::vector<const RHI::ImageView*> imageViews;
+        for (uint32_t textureIdx = 0u; textureIdx < InternalBP::ImageCount; textureIdx++)
         {
-            Data::Instance<AZ::RPI::ShaderResourceGroup> imageSrg = m_bindlessSrg->GetSrg(m_imageSrgName);
-            AZ::RHI::ShaderInputImageUnboundedArrayIndex imageIndex = imageSrg->FindShaderInputImageUnboundedArrayIndex(AZ::Name(textureArrayId));
-
-            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);
-                m_images.push_back(image);
-                imageViews.push_back(image->GetImageView());
-            }
-
-            [[maybe_unused]] bool result = imageSrg->SetImageViewUnboundedArray(imageIndex, imageViews);
-            AZ_Assert(result, "Failed to set image view unbounded array into shader resource group.");
-
-            // Compile the image SRG
-            imageSrg->Compile();
+            AZ::Data::Instance<AZ::RPI::StreamingImage> image = LoadStreamingImage(InternalBP::Images[textureIdx], InternalBP::SampleName);
+            m_images.push_back(image);
+            m_imageViews.push_back(image->GetImageView());
         }
 
-        // Create and allocate the materials in the FloatBuffer
+        // Set the indirect buffer for the images
+        {
+            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);
+        }
+        
+#if ATOMSAMPLEVIEWER_TRAIT_BINDLESS_PROTOTYPE_SUPPORTS_DIRECT_BOUND_UNBOUNDED_ARRAY
+       // Set the images
+       {
+           Data::Instance<AZ::RPI::ShaderResourceGroup> imageSrg = m_bindlessSrg->GetSrg(m_imageSrgName);
+           AZ::RHI::ShaderInputImageUnboundedArrayIndex imageIndex =
+               imageSrg->FindShaderInputImageUnboundedArrayIndex(AZ::Name(textureArrayId));
+
+           [[maybe_unused]] bool result = imageSrg->SetImageViewUnboundedArray(imageIndex, m_imageViews);
+           AZ_Assert(result, "Failed to set image view unbounded array into shader resource group.");
+
+           // Compile the image SRG
+           imageSrg->Compile();
+       }
+#endif
+        
+        // Create and allocate the materials in the FloatBufferimageSrg
         {
             m_materialHandleArray.resize(m_materialCount);
             CreateMaterials();
@@ -564,9 +590,13 @@ 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_imageSrgName)->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;
@@ -675,6 +705,29 @@ namespace AtomSampleViewer
                                               static_cast<void *>(&m_lightDir),
                                               static_cast<uint32_t>(sizeof(Vector3)));
 
+        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);
+
+        AZStd::vector<const RHI::ImageView*> views;
+        for(AZ::Data::Instance<AZ::RPI::StreamingImage> image : m_images)
+        {
+            views.push_back(image->GetImageView());
+        }
+        
+        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);
     }
 

+ 18 - 1
Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.h

@@ -22,6 +22,8 @@
 
 #include <Utils/ImGuiSidebar.h>
 
+#include <BindlessPrototype_Traits_Platform.h>
+
 namespace AZ
 {
     namespace RHI
@@ -180,6 +182,9 @@ namespace AtomSampleViewer
         // Create the BufferPol
         void CreateBufferPool();
 
+        //Recreate objects
+        void Recreate();
+        
         // ImGui sidebar
         ImGuiSidebar m_imguiSidebar;
 
@@ -216,6 +221,8 @@ namespace AtomSampleViewer
         // Image array holding all of the StreamImages
         AZStd::vector<AZ::Data::Instance<AZ::RPI::StreamingImage>> m_images;
 
+        AZStd::vector<const AZ::RHI::ImageView*> m_imageViews;
+
         // Light direction handle
         FloatBufferHandle m_lightDirectionHandle;
 
@@ -238,6 +245,12 @@ namespace AtomSampleViewer
         // BufferPool used to allocate buffers in this example
         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;
+
+        // 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;
 
@@ -246,8 +259,12 @@ namespace AtomSampleViewer
         static constexpr uint32_t m_modelLod = 0u;
 
         AZ::RHI::ScopeId m_scopeId;
-        static constexpr const char* m_imageSrgName = "ImageSrg";
+        static constexpr const char* m_samplerSrgName = "SamplerSrg";
         static constexpr const char* m_floatBufferSrgName = "FloatBufferSrg";
+        static constexpr const char* m_indirectionBufferSrgName = "IndirectionBufferSrg";
+#if ATOMSAMPLEVIEWER_TRAIT_BINDLESS_PROTOTYPE_SUPPORTS_DIRECT_BOUND_UNBOUNDED_ARRAY
+        static constexpr const char* m_imageSrgName = "ImageSrg";
+#endif
 
         // Material count
         static constexpr uint32_t m_materialCount = 1024u;

+ 407 - 0
Gem/Code/Source/RHI/XRExampleComponent.cpp

@@ -0,0 +1,407 @@
+/*
+ * 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/XRExampleComponent.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/RPI.Reflect/Shader/ShaderAsset.h>
+#include <AzCore/Math/Color.h>
+#include <AzCore/Serialization/SerializeContext.h>
+#include <SampleComponentManager.h>
+#include <Utils/Utils.h>
+
+namespace AtomSampleViewer
+{
+    void XRExampleComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<XRExampleComponent, AZ::Component>()->Version(0);
+        }
+    }
+
+    XRExampleComponent::XRExampleComponent()
+    {
+        m_supportRHISamplePipeline = true;
+        
+    }
+
+    void XRExampleComponent::Activate()
+    {
+        if (!AZ::RPI::RPISystemInterface::Get()->GetXRSystem())
+        {
+            return;
+        }
+
+        m_depthStencilID = AZ::RHI::AttachmentId{ AZStd::string::format("DepthStencilID_%llu", GetId()) };
+        CreateCubeInputAssemblyBuffer();
+        CreateCubePipeline();
+        CreateScope();
+        AZ::RHI::RHISystemNotificationBus::Handler::BusConnect();
+        AZ::TickBus::Handler::BusConnect();
+    }
+
+    void XRExampleComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
+    {
+        m_time += deltaTime;
+    }
+
+    void XRExampleComponent::OnFramePrepare(AZ::RHI::FrameGraphBuilder& frameGraphBuilder)
+    {
+        AZ::Matrix4x4 projection = AZ::Matrix4x4::CreateIdentity();
+
+        AZ::RPI::XRRenderingInterface* xrSystem = AZ::RPI::RPISystemInterface::Get()->GetXRSystem();
+        if (xrSystem && xrSystem->ShouldRender())
+        {
+            AZ::RPI::FovData fovData;
+            AZ::RPI::PoseData poseData, frontViewPoseData;
+            [[maybe_unused]] AZ::RHI::ResultCode resultCode = xrSystem->GetViewFov(m_viewIndex, fovData);
+            resultCode = xrSystem->GetViewPose(m_viewIndex, poseData);
+                
+            static const float clip_near = 0.05f;
+            static const float clip_far = 100.0f;
+            bool reverseDepth = false;
+            projection = xrSystem->CreateStereoscopicProjection(fovData.m_angleLeft, fovData.m_angleRight,
+                                                          fovData.m_angleDown, fovData.m_angleUp, 
+                                                          clip_near, clip_far, reverseDepth);
+
+            AZ::Quaternion poseOrientation = poseData.m_orientation; 
+            poseOrientation.InvertFast(); 
+            AZ::Matrix4x4 viewMat = AZ::Matrix4x4::CreateFromQuaternionAndTranslation(poseOrientation, -poseData.m_position);
+            m_viewProjMatrix = projection * viewMat;
+ 
+            const AZ::Matrix4x4 initialScaleMat = AZ::Matrix4x4::CreateScale(AZ::Vector3(0.1f, 0.1f, 0.1f));
+
+            //Model matrix for the cube related to the front view
+            resultCode = xrSystem->GetViewFrontPose(frontViewPoseData);
+            m_modelMatrices[0] = AZ::Matrix4x4::CreateFromQuaternionAndTranslation(frontViewPoseData.m_orientation, frontViewPoseData.m_position) * initialScaleMat;
+                      
+            //Model matrix for the cube related to the left controller
+            AZ::RPI::PoseData controllerLeftPose, controllerRightPose;
+            resultCode = xrSystem->GetControllerPose(0, controllerLeftPose);
+            AZ::Matrix4x4 leftScaleMat = initialScaleMat * AZ::Matrix4x4::CreateScale(AZ::Vector3(xrSystem->GetControllerScale(0)));
+            m_modelMatrices[1] = AZ::Matrix4x4::CreateFromQuaternionAndTranslation(controllerLeftPose.m_orientation, controllerLeftPose.m_position) * leftScaleMat;
+
+            //Model matrix for the cube related to the right controller
+            AZ::Matrix4x4 rightScaleMat = initialScaleMat * AZ::Matrix4x4::CreateScale(AZ::Vector3(xrSystem->GetControllerScale(1)));
+            resultCode = xrSystem->GetControllerPose(1, controllerRightPose);
+            m_modelMatrices[2] = AZ::Matrix4x4::CreateFromQuaternionAndTranslation(controllerRightPose.m_orientation, controllerRightPose.m_position) * rightScaleMat;
+        }      
+        
+        for (int i = 0; i < NumberOfCubes; ++i)
+        {
+            m_shaderResourceGroups[i]->SetConstant(m_shaderIndexWorldMat, m_modelMatrices[i]);
+            m_shaderResourceGroups[i]->SetConstant(m_shaderIndexViewProj, m_viewProjMatrix);
+            m_shaderResourceGroups[i]->Compile();
+        }
+
+        BasicRHIComponent::OnFramePrepare(frameGraphBuilder);
+    }
+
+    XRExampleComponent::SingleCubeBufferData XRExampleComponent::CreateSingleCubeBufferData()
+    {
+        const AZStd::fixed_vector<AZ::Color, GeometryVertexCount> vertexColor =
+        {
+            //Front Face
+            AZ::Colors::DarkBlue,   AZ::Colors::DarkBlue,   AZ::Colors::DarkBlue,   AZ::Colors::DarkBlue,
+            //Back Face                                                                       
+            AZ::Colors::Blue,       AZ::Colors::Blue,       AZ::Colors::Blue,       AZ::Colors::Blue,
+            //Left Face                                                                      
+            AZ::Colors::DarkGreen,  AZ::Colors::DarkGreen,  AZ::Colors::DarkGreen,  AZ::Colors::DarkGreen,
+            //Right Face                                                                    
+            AZ::Colors::Green,      AZ::Colors::Green,      AZ::Colors::Green,      AZ::Colors::Green,
+            //Top Face                                                                  
+            AZ::Colors::DarkRed,    AZ::Colors::DarkRed,    AZ::Colors::DarkRed,    AZ::Colors::DarkRed,
+            //Bottom Face                                                                    
+            AZ::Colors::Red,        AZ::Colors::Red,        AZ::Colors::Red,        AZ::Colors::Red,
+        };
+
+        // Create vertices, colors and normals for a cube and a plane
+        SingleCubeBufferData bufferData;
+        {
+            
+            const AZStd::fixed_vector<AZ::Vector3, GeometryVertexCount> vertices =
+            {
+                //Front Face
+                AZ::Vector3(1.0, 1.0, 1.0),         AZ::Vector3(-1.0, 1.0, 1.0),     AZ::Vector3(-1.0, -1.0, 1.0),    AZ::Vector3(1.0, -1.0, 1.0),
+                //Back Face                                                                       
+                AZ::Vector3(1.0, 1.0, -1.0),        AZ::Vector3(-1.0, 1.0, -1.0),    AZ::Vector3(-1.0, -1.0, -1.0),   AZ::Vector3(1.0, -1.0, -1.0),
+                //Left Face                                                                      
+                AZ::Vector3(-1.0, 1.0, 1.0),        AZ::Vector3(-1.0, -1.0, 1.0),    AZ::Vector3(-1.0, -1.0, -1.0),   AZ::Vector3(-1.0, 1.0, -1.0),
+                //Right Face                                                                    
+                AZ::Vector3(1.0, 1.0, 1.0),         AZ::Vector3(1.0, -1.0, 1.0),     AZ::Vector3(1.0, -1.0, -1.0),    AZ::Vector3(1.0, 1.0, -1.0),
+                //Top Face                                                                  
+                AZ::Vector3(1.0, 1.0, 1.0),         AZ::Vector3(-1.0, 1.0, 1.0),     AZ::Vector3(-1.0, 1.0, -1.0),    AZ::Vector3(1.0, 1.0, -1.0),
+                //Bottom Face                                                                    
+                AZ::Vector3(1.0, -1.0, 1.0),        AZ::Vector3(-1.0, -1.0, 1.0),    AZ::Vector3(-1.0, -1.0, -1.0),   AZ::Vector3(1.0, -1.0, -1.0),
+            };
+
+            for (int i = 0; i < GeometryVertexCount; ++i)
+            {
+                SetVertexPosition(bufferData.m_positions.data(), i, vertices[i]);
+                SetVertexColor(bufferData.m_colors.data(), i, vertexColor[i].GetAsVector4());
+            }
+
+            bufferData.m_indices =
+            {
+                {
+                    //Back
+                    2, 0, 1,
+                    0, 2, 3,
+                    //Front
+                    4, 6, 5,
+                    6, 4, 7,
+                    //Left
+                    8, 10, 9,
+                    10, 8, 11,
+                    //Right
+                    14, 12, 13,
+                    15, 12, 14,
+                    //Top
+                    16, 18, 17,
+                    18, 16, 19,
+                    //Bottom
+                    22, 20, 21,
+                    23, 20, 22,
+                }
+            };
+        }
+        return bufferData;
+    }
+
+    void XRExampleComponent::CreateCubeInputAssemblyBuffer()
+    {
+        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();
+        AZ::RHI::BufferPoolDescriptor bufferPoolDesc;
+        bufferPoolDesc.m_bindFlags = AZ::RHI::BufferBindFlags::InputAssembly;
+        bufferPoolDesc.m_heapMemoryLevel = AZ::RHI::HeapMemoryLevel::Device;
+        result = m_bufferPool->Init(*device, bufferPoolDesc);
+        if (result != AZ::RHI::ResultCode::Success)
+        {
+            AZ_Error("XRExampleComponent", false, "Failed to initialize buffer pool with error code %d", result);
+            return;
+        }
+
+        SingleCubeBufferData bufferData = CreateSingleCubeBufferData();
+
+        m_inputAssemblyBuffer = AZ::RHI::Factory::Get().CreateBuffer();
+        AZ::RHI::BufferInitRequest request;
+
+        request.m_buffer = m_inputAssemblyBuffer.get();
+        request.m_descriptor = AZ::RHI::BufferDescriptor{ AZ::RHI::BufferBindFlags::InputAssembly, sizeof(SingleCubeBufferData) };
+        request.m_initialData = &bufferData;
+        result = m_bufferPool->InitBuffer(request);
+        if (result != AZ::RHI::ResultCode::Success)
+        {
+            AZ_Error("XRExampleComponent", false, "Failed to initialize buffer with error code %d", result);
+            return;
+        }
+
+        m_streamBufferViews[0] =
+        {
+            *m_inputAssemblyBuffer,
+            offsetof(SingleCubeBufferData, m_positions),
+            sizeof(SingleCubeBufferData::m_positions),
+            sizeof(VertexPosition)
+        };
+
+        m_streamBufferViews[1] =
+        {
+            *m_inputAssemblyBuffer,
+            offsetof(SingleCubeBufferData, m_colors),
+            sizeof(SingleCubeBufferData::m_colors),
+            sizeof(VertexColor)
+        };
+
+        m_indexBufferView =
+        {
+            *m_inputAssemblyBuffer,
+            offsetof(SingleCubeBufferData, m_indices),
+            sizeof(SingleCubeBufferData::m_indices),
+            AZ::RHI::IndexFormat::Uint16
+        };
+
+        AZ::RHI::InputStreamLayoutBuilder layoutBuilder;
+        layoutBuilder.SetTopology(AZ::RHI::PrimitiveTopology::TriangleList);
+        layoutBuilder.AddBuffer()->Channel("POSITION", AZ::RHI::Format::R32G32B32_FLOAT);
+        layoutBuilder.AddBuffer()->Channel("COLOR", AZ::RHI::Format::R32G32B32A32_FLOAT);
+        m_streamLayoutDescriptor.Clear();
+        m_streamLayoutDescriptor = layoutBuilder.End();
+
+        AZ::RHI::ValidateStreamBufferViews(m_streamLayoutDescriptor, m_streamBufferViews);
+    }
+
+    void XRExampleComponent::CreateCubePipeline()
+    {
+        const char* shaderFilePath = "Shaders/RHI/OpenXrSample.azshader";
+        const char* sampleName = "XRExampleComponent";
+
+        auto shader = LoadShader(shaderFilePath, sampleName);
+        if (shader == nullptr)
+        { 
+            return;
+        }
+
+        const AZ::RHI::Ptr<AZ::RHI::Device> device = Utils::GetRHIDevice();
+        AZ::RHI::PipelineStateDescriptorForDraw pipelineDesc;
+        shader->GetVariant(AZ::RPI::ShaderAsset::RootShaderVariantStableId).ConfigurePipelineState(pipelineDesc);
+        pipelineDesc.m_inputStreamLayout = m_streamLayoutDescriptor;
+        pipelineDesc.m_renderStates.m_depthStencilState.m_depth.m_enable = 1;
+        pipelineDesc.m_renderStates.m_depthStencilState.m_depth.m_func = AZ::RHI::ComparisonFunc::LessEqual;
+
+        AZ::RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
+        attachmentsBuilder.AddSubpass()
+            ->RenderTargetAttachment(m_outputFormat)
+            ->DepthStencilAttachment(device->GetNearestSupportedFormat(AZ::RHI::Format::D24_UNORM_S8_UINT, AZ::RHI::FormatCapabilities::DepthStencil));
+            
+        [[maybe_unused]] AZ::RHI::ResultCode result = attachmentsBuilder.End(pipelineDesc.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
+        AZ_Assert(result == AZ::RHI::ResultCode::Success, "Failed to create render attachment layout");
+
+        m_pipelineState = shader->AcquirePipelineState(pipelineDesc);
+        if (!m_pipelineState)
+        {
+            AZ_Error("XRExampleComponent", false, "Failed to acquire default pipeline state for shader '%s'", shaderFilePath);
+            return;
+        }
+
+        auto perInstanceSrgLayout = shader->FindShaderResourceGroupLayout(AZ::Name{ "OpenXrSrg" });
+        if (!perInstanceSrgLayout)
+        {
+            AZ_Error("XRExampleComponent", false, "Failed to get shader resource group layout");
+            return;
+        }
+
+        for (int i = 0; i < NumberOfCubes; ++i)
+        {
+            m_shaderResourceGroups[i] = CreateShaderResourceGroup(shader, "OpenXrSrg", sampleName);
+        }
+
+        // Using the first SRG to get the correct index as all the SRGs will have the same indices.
+        FindShaderInputIndex(&m_shaderIndexWorldMat, m_shaderResourceGroups[0], AZ::Name{ "m_worldMatrix" }, "XRExampleComponent");
+        FindShaderInputIndex(&m_shaderIndexViewProj, m_shaderResourceGroups[0], AZ::Name{ "m_viewProjMatrix" }, "XRExampleComponent");
+    }
+
+    void XRExampleComponent::CreateScope()
+    {
+        // Creates a scope for rendering the triangle.
+        struct ScopeData
+        {
+
+        };
+        const auto prepareFunction = [this](AZ::RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
+        {
+            // Binds the swap chain as a color attachment. Clears it to black.
+            {
+                AZ::RHI::ImageScopeAttachmentDescriptor descriptor;
+                descriptor.m_attachmentId = m_outputAttachmentId;
+                descriptor.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Load;
+                frameGraph.UseColorAttachment(descriptor);
+            }
+
+            // Create & Binds DepthStencil image
+            {
+                const AZ::RHI::Ptr<AZ::RHI::Device> device = Utils::GetRHIDevice();
+                const AZ::RHI::ImageDescriptor imageDescriptor = AZ::RHI::ImageDescriptor::Create2D(
+                    AZ::RHI::ImageBindFlags::DepthStencil,
+                    m_outputWidth,
+                    m_outputHeight,
+                    device->GetNearestSupportedFormat(AZ::RHI::Format::D24_UNORM_S8_UINT, AZ::RHI::FormatCapabilities::DepthStencil));
+                const AZ::RHI::TransientImageDescriptor transientImageDescriptor(m_depthStencilID, imageDescriptor);
+
+                frameGraph.GetAttachmentDatabase().CreateTransientImage(transientImageDescriptor);
+
+                AZ::RHI::ImageScopeAttachmentDescriptor dsDesc;
+                dsDesc.m_attachmentId = m_depthStencilID;
+                dsDesc.m_imageViewDescriptor.m_overrideFormat = device->GetNearestSupportedFormat(AZ::RHI::Format::D24_UNORM_S8_UINT, AZ::RHI::FormatCapabilities::DepthStencil);
+                dsDesc.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateDepthStencil(1.0f, 0);
+                dsDesc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Clear;
+                dsDesc.m_loadStoreAction.m_loadActionStencil = AZ::RHI::AttachmentLoadAction::DontCare;
+                frameGraph.UseDepthStencilAttachment(dsDesc, AZ::RHI::ScopeAttachmentAccess::Write);
+            }
+
+            // We will submit NumberOfCubes draw items.
+            frameGraph.SetEstimatedItemCount(NumberOfCubes);
+        };
+
+        AZ::RHI::EmptyCompileFunction<ScopeData> compileFunction;
+
+        const auto executeFunction = [this](const AZ::RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
+        {
+            AZ::RHI::CommandList* commandList = context.GetCommandList();
+
+            // Set persistent viewport and scissor state.
+            commandList->SetViewports(&m_viewport, 1);
+            commandList->SetScissors(&m_scissor, 1);
+
+            AZ::RHI::DrawIndexed drawIndexed;
+            drawIndexed.m_indexCount = GeometryIndexCount;
+            drawIndexed.m_instanceCount = 1;
+
+            // Dividing NumberOfCubes by context.GetCommandListCount() to balance to number 
+            // of draw call equally between each thread.
+            uint32_t numberOfCubesPerCommandList = NumberOfCubes / context.GetCommandListCount();
+            uint32_t indexStart = context.GetCommandListIndex() * numberOfCubesPerCommandList;
+            uint32_t indexEnd = indexStart + numberOfCubesPerCommandList;
+
+            if (context.GetCommandListIndex() == context.GetCommandListCount() - 1)
+            {
+                indexEnd = NumberOfCubes;
+            }
+
+            for (uint32_t i = indexStart; i < indexEnd; ++i)
+            {
+                const AZ::RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroups[i]->GetRHIShaderResourceGroup() };
+
+                AZ::RHI::DrawItem drawItem;
+                drawItem.m_arguments = drawIndexed;
+                drawItem.m_pipelineState = m_pipelineState.get();
+                drawItem.m_indexBufferView = &m_indexBufferView;
+                drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(AZ::RHI::ArraySize(shaderResourceGroups));
+                drawItem.m_shaderResourceGroups = shaderResourceGroups;
+                drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
+                drawItem.m_streamBufferViews = m_streamBufferViews.data();
+
+                commandList->Submit(drawItem);
+            }
+        };
+
+        m_scopeProducers.emplace_back(
+            aznew AZ::RHI::ScopeProducerFunction<
+            ScopeData,
+            decltype(prepareFunction),
+            decltype(compileFunction),
+            decltype(executeFunction)>(
+                AZ::RHI::ScopeId{ AZStd::string::format("XRSample_Id_%llu", GetId()) },
+                ScopeData{},
+                prepareFunction,
+                compileFunction,
+                executeFunction));
+    }
+
+    void XRExampleComponent::Deactivate()
+    {
+        if (!AZ::RPI::RPISystemInterface::Get()->GetXRSystem())
+        {
+            return;
+        }
+
+        m_inputAssemblyBuffer = nullptr;
+        m_bufferPool = nullptr;
+        m_pipelineState = nullptr;
+        m_shaderResourceGroups.fill(nullptr);
+
+        AZ::RHI::RHISystemNotificationBus::Handler::BusDisconnect();
+        m_windowContext = nullptr;
+        m_scopeProducers.clear();
+    }
+} 

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

@@ -0,0 +1,101 @@
+/*
+ * 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 <AzCore/Math/Matrix4x4.h>
+
+#include <RHI/BasicRHIComponent.h>
+#include <AzCore/Component/TickBus.h>
+
+namespace AtomSampleViewer
+{
+    //! The purpose of this sample is to establish a simple XR sample utilizing a simple VR pipeline
+    //! It will render a mesh per controller plus one for the front view. It will prove out all the 
+    //! code related related to openxr device, instance, swapchain, session, input, space.       
+    class XRExampleComponent final
+        : public BasicRHIComponent
+        , public AZ::TickBus::Handler
+    {
+    public:
+        AZ_COMPONENT(XRExampleComponent, "{A7D9A921-1FF9-4078-92BD-169E258456E7}");
+        AZ_DISABLE_COPY(XRExampleComponent);
+
+        static void Reflect(AZ::ReflectContext* context);
+
+        XRExampleComponent();
+        ~XRExampleComponent() override = default;
+
+    protected:
+        // 1 cube for view + 2 cubes for the controller
+        static const uint32_t NumberOfCubes = 3; 
+        static const uint32_t GeometryVertexCount = 24;
+        static const uint32_t GeometryIndexCount = 36;
+
+        struct SingleCubeBufferData
+        {
+            AZStd::array<VertexPosition, GeometryVertexCount> m_positions;
+            AZStd::array<VertexColor, GeometryVertexCount> m_colors;
+            AZStd::array<uint16_t, GeometryIndexCount> m_indices;
+        };
+
+        // AZ::Component
+        void Activate() override;
+        void Deactivate() override;
+
+        // RHISystemNotificationBus::Handler
+        void OnFramePrepare(AZ::RHI::FrameGraphBuilder& frameGraphBuilder) override;
+
+        // TickBus::Handler
+        void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
+
+        //! Create IA data
+        void CreateCubeInputAssemblyBuffer();
+        //! Create Cube data
+        SingleCubeBufferData CreateSingleCubeBufferData();
+        //! Create PSO data
+        void CreateCubePipeline();
+        //! Create the relevant Scope
+        void CreateScope();
+
+        AZ::RHI::Ptr<AZ::RHI::BufferPool> m_bufferPool;
+        AZ::RHI::IndexBufferView m_indexBufferView;
+        AZ::RHI::Ptr<AZ::RHI::Buffer> m_inputAssemblyBuffer;
+        AZ::RHI::InputStreamLayout m_streamLayoutDescriptor;
+        AZ::RHI::ConstPtr<AZ::RHI::PipelineState> m_pipelineState;
+
+        struct BufferData
+        {
+            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::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;
+        AZ::Matrix4x4 m_viewProjMatrix;
+
+        AZ::RHI::ShaderInputConstantIndex m_shaderIndexWorldMat;
+        AZ::RHI::ShaderInputConstantIndex m_shaderIndexViewProj;  
+        AZ::RHI::AttachmentId m_depthStencilID;
+    };
+} // namespace AtomSampleViewer

+ 211 - 52
Gem/Code/Source/SampleComponentManager.cpp

@@ -60,6 +60,7 @@
 #include <RHI/TextureExampleComponent.h>
 #include <RHI/TextureMapExampleComponent.h>
 #include <RHI/TriangleExampleComponent.h>
+#include <RHI/XRExampleComponent.h>
 #include <RHI/TrianglesConstantBufferExampleComponent.h>
 #include <RHI/BindlessPrototypeExampleComponent.h>
 #include <RHI/RayTracingExampleComponent.h>
@@ -103,6 +104,7 @@
 #include <TransparencyExampleComponent.h>
 #include <DiffuseGIExampleComponent.h>
 #include <SSRExampleComponent.h>
+#include <XRRPIExampleComponent.h>
 #include <ShaderReloadTestComponent.h>
 #include <ReadbackExampleComponent.h>
 
@@ -306,6 +308,7 @@ namespace AtomSampleViewer
             NewRHISample<TextureMapExampleComponent>("TextureMap"),
             NewRHISample<TriangleExampleComponent>("Triangle"),
             NewRHISample<TrianglesConstantBufferExampleComponent>("TrianglesConstantBuffer"),
+            NewRHISample<XRExampleComponent>("OpenXr", []() { return AZ::RHI::RHISystemInterface::Get()->GetXRSystem() != nullptr; }),
             NewRHISample<MatrixAlignmentTestExampleComponent>("MatrixAlignmentTest"),
             NewRHISample<VariableRateShadingExampleComponent>("VariableRateShading", []() { return Utils::GetRHIDevice()->GetFeatures().m_shadingRateTypeMask != RHI::ShadingRateTypeFlags::None; }),
             NewRPISample<AssetLoadTestComponent>("AssetLoadTest"),
@@ -341,6 +344,7 @@ namespace AtomSampleViewer
             NewFeaturesSample<SkinnedMeshExampleComponent>("SkinnedMesh"),
             NewFeaturesSample<SsaoExampleComponent>("SSAO"),
             NewFeaturesSample<SSRExampleComponent>("SSR"),
+            NewFeaturesSample<XRRPIExampleComponent>("OpenXR", []() { return AZ::RPI::RPISystemInterface::Get()->GetXRSystem() != nullptr; }),
             NewFeaturesSample<TonemappingExampleComponent>("Tonemapping"),
             NewFeaturesSample<TransparencyExampleComponent>("Transparency"),
             NewPerfSample<_100KDrawableExampleComponent>("100KDrawable_SingleView"),
@@ -573,6 +577,14 @@ namespace AtomSampleViewer
 
     void SampleComponentManager::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
     {
+        if (auto* xrSystem = AZ::RPI::RPISystemInterface::Get()->GetXRSystem())
+        {
+            EnableRenderPipeline(xrSystem->GetRHIXRRenderingInterface()->IsDefaultRenderPipelineEnabledOnHost());
+
+            //Only enable XR pipelines if the XR drivers indicate we have accurate pose information from the device
+            EnableXrPipelines(xrSystem->ShouldRender());
+        }
+
         if (m_imGuiFrameTimer)
         {
             m_imGuiFrameTimer->PushValue(deltaTime * 1000.0f);
@@ -1300,6 +1312,39 @@ namespace AtomSampleViewer
         ReleaseRPIScene();
     }
 
+    void SampleComponentManager::EnableRenderPipeline(bool value)
+    {
+        if (m_renderPipeline)
+        {
+            if (value)
+            {
+                m_renderPipeline->AddToRenderTick();
+            }
+            else
+            {
+                m_renderPipeline->RemoveFromRenderTick();
+            }
+        }
+    }
+
+    void SampleComponentManager::EnableXrPipelines(bool value)
+    {
+        for (RPI::RenderPipelinePtr xrPipeline : m_xrPipelines)
+        {
+            if (xrPipeline)
+            {
+                if (value)
+                {
+                    xrPipeline->AddToRenderTick();
+                }
+                else
+                {
+                    xrPipeline->RemoveFromRenderTick();
+                }
+            }
+        }
+    }
+
     void SampleComponentManager::ShowFrameCaptureDialog()
     {
         static bool requestCaptureOnNextFrame = false;
@@ -1353,22 +1398,21 @@ namespace AtomSampleViewer
     {
         m_exampleEntity->Deactivate();
 
-        // Pointer to the m_activeSample must be nullified before m_activeSample is destroyed.
-        if (m_rhiSamplePass)
-        {
-            m_rhiSamplePass->SetRHISample(nullptr);
-        }
+        // Pointer to all the passes within m_rhiSamplePasses must be nullified before all the samples within m_activeSamples are destroyed.
+        SetRHISamplePass(nullptr);
 
-        if (m_activeSample != nullptr)
+        for (AZ::Component* activeComponent : m_activeSamples)
         {
-            // Disable the camera controller just in case the active sample enabled it and didn't disable in Deactivate().
-            AZ::Debug::CameraControllerRequestBus::Event(m_cameraEntity->GetId(), &AZ::Debug::CameraControllerRequestBus::Events::Disable);
+            if (activeComponent != nullptr)
+            {
+                // Disable the camera controller just in case the active sample enabled it and didn't disable in Deactivate().
+                AZ::Debug::CameraControllerRequestBus::Event(m_cameraEntity->GetId(), &AZ::Debug::CameraControllerRequestBus::Events::Disable);
 
-            m_exampleEntity->RemoveComponent(m_activeSample);
-            delete m_activeSample;
+                m_exampleEntity->RemoveComponent(activeComponent);
+                delete activeComponent;
+            }
         }
-
-        m_activeSample = nullptr;
+        m_activeSamples.clear();
 
         // Force a reset of the shader variant finder to get more consistent testing of samples every time they are run, rather
         // than the first time for each sample being "special".
@@ -1384,7 +1428,7 @@ namespace AtomSampleViewer
 
         // Reset to RHI sample pipeline
         SwitchSceneForRHISample();
-        m_rhiSamplePass->SetRHISample(nullptr);
+        SetRHISamplePass(nullptr);
     }
 
     void SampleComponentManager::CreateDefaultCamera()
@@ -1505,24 +1549,33 @@ namespace AtomSampleViewer
             SwitchSceneForRPISample();
         }
 
-        SampleComponentConfig config(m_windowContext, m_cameraEntity->GetId(), m_entityContextId);
-        m_activeSample = m_exampleEntity->CreateComponent(sampleEntry.m_sampleUuid);
-        m_activeSample->SetConfiguration(config);
-
+        SampleComponentConfig config(m_windowContext, m_cameraEntity->GetId(), m_entityContextId); 
         // special setup for RHI samples
         if (sampleEntry.m_pipelineType == SamplePipelineType::RHI)
         {
-            BasicRHIComponent* rhiSampleComponent = static_cast<BasicRHIComponent*>(m_activeSample);
-            if (rhiSampleComponent->IsSupportedRHISamplePipeline())
+            for (AZ::RPI::Ptr<RHISamplePass> samplePass : m_rhiSamplePasses)
             {
-                m_rhiSamplePass->SetRHISample(rhiSampleComponent);
-            }
-            else
-            {
-                m_rhiSamplePass->SetRHISample(nullptr);
-            }
+                BasicRHIComponent* rhiSampleComponent = static_cast<BasicRHIComponent*>(m_exampleEntity->CreateComponent(sampleEntry.m_sampleUuid));
+                rhiSampleComponent->SetConfiguration(config);
+                rhiSampleComponent->SetViewIndex(samplePass->GetViewIndex());
+                if (rhiSampleComponent->IsSupportedRHISamplePipeline())
+                {
+                    samplePass->SetRHISample(rhiSampleComponent);
+                }
+                else
+                {
+                    samplePass->SetRHISample(nullptr);
+                }
+                m_activeSamples.push_back(rhiSampleComponent);
+            }   
         }
-
+        else
+        {
+            AZ::Component* newComponent = m_exampleEntity->CreateComponent(sampleEntry.m_sampleUuid);
+            newComponent->SetConfiguration(config);
+            m_activeSamples.push_back(newComponent);
+        }
+        
         m_exampleEntity->Activate();
 
         // Even though this is done in CameraReset(), the example component wasn't activated at the time so we have to send this event again.
@@ -1550,22 +1603,74 @@ namespace AtomSampleViewer
         m_rhiScene = RPI::Scene::CreateScene(sceneDesc);
         m_rhiScene->Activate();
 
-        RPI::RenderPipelineDescriptor pipelineDesc;
-        pipelineDesc.m_name = "RHISamplePipeline";
-        pipelineDesc.m_rootPassTemplate = "RHISamplePipelineTemplate";
-        // Add view to pipeline since there are few RHI samples are using ViewSrg
-        pipelineDesc.m_mainViewTagName = "MainCamera";
+        auto* xrSystem = AZ::RHI::RHISystemInterface::Get()->GetXRSystem();
+        const bool createDefaultRenderPipeline = !xrSystem || xrSystem->IsDefaultRenderPipelineNeeded();
 
-        RPI::RenderPipelinePtr renderPipeline = RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineDesc, *m_windowContext.get());
-        m_rhiScene->AddRenderPipeline(renderPipeline);
-        renderPipeline->SetDefaultViewFromEntity(m_cameraEntity->GetId());
+        if (createDefaultRenderPipeline)
+        {
+            RPI::RenderPipelineDescriptor pipelineDesc;
+            pipelineDesc.m_name = "RHISamplePipeline";
+            pipelineDesc.m_rootPassTemplate = "RHISamplePipelineTemplate";
+            // Add view to pipeline since there are few RHI samples are using ViewSrg
+            pipelineDesc.m_mainViewTagName = "MainCamera";
 
-        RPI::RPISystemInterface::Get()->RegisterScene(m_rhiScene);
+            m_renderPipeline = RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineDesc, *m_windowContext.get());
+            m_rhiScene->AddRenderPipeline(m_renderPipeline);
+            m_renderPipeline->SetDefaultViewFromEntity(m_cameraEntity->GetId());
 
-        // Get RHISamplePass
-        AZ::RPI::PassFilter passFilter = AZ::RPI::PassFilter::CreateWithPassName(AZ::Name("RHISamplePass"), renderPipeline.get());
-        m_rhiSamplePass = azrtti_cast<RHISamplePass*>(AZ::RPI::PassSystemInterface::Get()->FindFirstPass(passFilter));
+            // Get RHISamplePass
+            AZ::RPI::PassFilter passFilter = AZ::RPI::PassFilter::CreateWithPassName(AZ::Name("RHISamplePass"), m_renderPipeline.get());
+            m_rhiSamplePasses.push_back(azrtti_cast<RHISamplePass*>(AZ::RPI::PassSystemInterface::Get()->FindFirstPass(passFilter)));
 
+            // Enable or disable default pipeline on host while there is an xr system.
+            if (xrSystem)
+            {
+                EnableRenderPipeline(xrSystem->IsDefaultRenderPipelineEnabledOnHost());
+            }
+        }
+
+        if (xrSystem)
+        {
+            RPI::RenderPipelineDescriptor xrPipelineDesc;
+            xrPipelineDesc.m_mainViewTagName = "MainCamera";
+
+            // Build the pipeline for left eye
+            xrPipelineDesc.m_name = "RHISamplePipelineXRLeft";
+            xrPipelineDesc.m_rootPassTemplate = "RHISamplePipelineXRLeftTemplate";
+            RPI::RenderPipelinePtr renderPipelineLeft = RPI::RenderPipeline::CreateRenderPipelineForWindow(xrPipelineDesc, *m_windowContext.get(), AZ::RPI::ViewType::XrLeft);
+
+            // Build the pipeline for right eye
+            xrPipelineDesc.m_name = "RHISamplePipelineXRRight";
+            xrPipelineDesc.m_rootPassTemplate = "RHISamplePipelineXRRightTemplate";
+            RPI::RenderPipelinePtr renderPipelineRight = RPI::RenderPipeline::CreateRenderPipelineForWindow(xrPipelineDesc, *m_windowContext.get(), AZ::RPI::ViewType::XrRight);
+
+            //Add both the pipelines to the scene
+            m_rhiScene->AddRenderPipeline(renderPipelineLeft);
+            m_rhiScene->AddRenderPipeline(renderPipelineRight);
+            renderPipelineLeft->SetDefaultViewFromEntity(m_cameraEntity->GetId());
+            renderPipelineRight->SetDefaultViewFromEntity(m_cameraEntity->GetId());
+            
+            // Set the correct view index for the RHI sample passes
+            AZ::RPI::PassFilter rhiSamplePassFilterLeft = AZ::RPI::PassFilter::CreateWithPassName(AZ::Name("RHISamplePass"), renderPipelineLeft.get());
+            AZ::RPI::Ptr<RHISamplePass> rhiSamplePassLeft = azrtti_cast<RHISamplePass*>(AZ::RPI::PassSystemInterface::Get()->FindFirstPass(rhiSamplePassFilterLeft));
+            rhiSamplePassLeft->SetViewIndex(0);
+            m_rhiSamplePasses.push_back(rhiSamplePassLeft);
+
+            AZ::RPI::PassFilter rhiSamplePassFilterRight = AZ::RPI::PassFilter::CreateWithPassName(AZ::Name("RHISamplePass"), renderPipelineRight.get());
+            AZ::RPI::Ptr<RHISamplePass> rhiSamplePassRight = azrtti_cast<RHISamplePass*>(AZ::RPI::PassSystemInterface::Get()->FindFirstPass(rhiSamplePassFilterRight));
+            rhiSamplePassRight->SetViewIndex(1);
+            m_rhiSamplePasses.push_back(rhiSamplePassRight);
+
+            //Cache the pipelines in case we want to enable/disable them
+            m_xrPipelines.push_back(renderPipelineLeft);
+            m_xrPipelines.push_back(renderPipelineRight);
+
+            //Disable XR pipelines by default
+            EnableXrPipelines(false);
+        }
+
+        // Register the RHi scene
+        RPI::RPISystemInterface::Get()->RegisterScene(m_rhiScene);  
         // Setup imGui since a new render pipeline with imgui pass was created
         SetupImGuiContext();
     }
@@ -1574,7 +1679,9 @@ namespace AtomSampleViewer
     {
         if (m_rhiScene)
         {
-            m_rhiSamplePass = nullptr;
+            m_rhiSamplePasses.clear();
+            m_xrPipelines.clear();
+            m_renderPipeline = nullptr;
             RPI::RPISystemInterface::Get()->UnregisterScene(m_rhiScene);
             m_rhiScene = nullptr;
         }
@@ -1609,24 +1716,67 @@ namespace AtomSampleViewer
         // Register scene to RPI system so it will be processed/rendered per tick
         RPI::RPISystemInterface::Get()->RegisterScene(m_rpiScene);
 
-        // Create MainPipeline as its render pipeline
-        RPI::RenderPipelineDescriptor pipelineDesc;
-        pipelineDesc.m_name = "RPISamplePipeline";
-        pipelineDesc.m_rootPassTemplate = GetRootPassTemplateName();
-        pipelineDesc.m_mainViewTagName = "MainCamera";
-        pipelineDesc.m_allowModification = true;
-
         // set pipeline MSAA samples
         AZ_Assert(IsValidNumMSAASamples(m_numMSAASamples), "Invalid MSAA sample setting");
-        pipelineDesc.m_renderSettings.m_multisampleState.m_samples = static_cast<uint16_t>(m_numMSAASamples);
-        bool isNonMsaaPipeline = (pipelineDesc.m_renderSettings.m_multisampleState.m_samples == 1);
+        const bool isNonMsaaPipeline = (m_numMSAASamples == 1);
         const char* supervariantName = isNonMsaaPipeline ? AZ::RPI::NoMsaaSupervariantName : "";
         AZ::RPI::ShaderSystemInterface::Get()->SetSupervariantName(AZ::Name(supervariantName));
 
-        RPI::RenderPipelinePtr renderPipeline = RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineDesc, *m_windowContext.get());
-        m_rpiScene->AddRenderPipeline(renderPipeline);
+        auto* xrSystem = AZ::RHI::RHISystemInterface::Get()->GetXRSystem();
+        const bool createDefaultRenderPipeline = !xrSystem || xrSystem->IsDefaultRenderPipelineNeeded();
 
-        renderPipeline->SetDefaultViewFromEntity(m_cameraEntity->GetId());
+        if (createDefaultRenderPipeline)
+        {
+            // Create MainPipeline as its render pipeline
+            RPI::RenderPipelineDescriptor pipelineDesc;
+            pipelineDesc.m_name = "RPISamplePipeline";
+            pipelineDesc.m_rootPassTemplate = GetRootPassTemplateName();
+            pipelineDesc.m_mainViewTagName = "MainCamera";
+            pipelineDesc.m_allowModification = true;
+            pipelineDesc.m_renderSettings.m_multisampleState.m_samples = static_cast<uint16_t>(m_numMSAASamples);
+
+            m_renderPipeline = RPI::RenderPipeline::CreateRenderPipelineForWindow(pipelineDesc, *m_windowContext.get());
+            m_rpiScene->AddRenderPipeline(m_renderPipeline);
+
+            m_renderPipeline->SetDefaultViewFromEntity(m_cameraEntity->GetId());
+
+            // Enable or disable default pipeline on host while there is an xr system.
+            if (xrSystem)
+            {
+                EnableRenderPipeline(xrSystem->IsDefaultRenderPipelineEnabledOnHost());
+            }
+        }
+
+        if (xrSystem)
+        {
+            RPI::RenderPipelineDescriptor xrPipelineDesc;
+            xrPipelineDesc.m_mainViewTagName = "MainCamera";
+            xrPipelineDesc.m_renderSettings.m_multisampleState.m_samples = static_cast<uint16_t>(m_numMSAASamples);
+
+            // Build the pipeline for left eye
+            xrPipelineDesc.m_name = "RPISamplePipelineXRLeft";
+            xrPipelineDesc.m_rootPassTemplate = "LowEndPipelineXRLeftTemplate";
+            RPI::RenderPipelinePtr renderPipelineLeft = RPI::RenderPipeline::CreateRenderPipelineForWindow(xrPipelineDesc, *m_windowContext.get(), AZ::RPI::ViewType::XrLeft);
+
+            // Build the pipeline for right eye
+            xrPipelineDesc.m_name = "RHISamplePipelineXRRight";
+            xrPipelineDesc.m_rootPassTemplate = "LowEndPipelineXRRightTemplate";
+            RPI::RenderPipelinePtr renderPipelineRight = RPI::RenderPipeline::CreateRenderPipelineForWindow(xrPipelineDesc, *m_windowContext.get(), AZ::RPI::ViewType::XrRight);
+
+            //Add both the pipelines to the scene
+            m_rpiScene->AddRenderPipeline(renderPipelineLeft);
+            m_rpiScene->AddRenderPipeline(renderPipelineRight);
+
+            renderPipelineLeft->SetDefaultStereoscopicViewFromEntity(m_cameraEntity->GetId(), RPI::ViewType::XrLeft); //Left eye
+            renderPipelineRight->SetDefaultStereoscopicViewFromEntity(m_cameraEntity->GetId(), RPI::ViewType::XrRight); //Right eye
+
+            //Cache the pipelines in case we want to enable/disable them
+            m_xrPipelines.push_back(renderPipelineLeft);
+            m_xrPipelines.push_back(renderPipelineRight);
+
+            // Disable XR pipelines by default
+            EnableXrPipelines(false);
+        }
 
         // As part of our initialization we need to create the BRDF texture generation pipeline
         AZ::RPI::RenderPipelineDescriptor brdfPipelineDesc;
@@ -1638,7 +1788,7 @@ namespace AtomSampleViewer
         RPI::RenderPipelinePtr brdfTexturePipeline = AZ::RPI::RenderPipeline::CreateRenderPipeline(brdfPipelineDesc);
         m_rpiScene->AddRenderPipeline(brdfTexturePipeline);
         
-        // Save a reference to the generated BRDF texture so it doesn't get deleted if all the passes refering to it get deleted and it's ref count goes to zero
+        // Save a reference to the generated BRDF texture so it doesn't get deleted if all the passes referring to it get deleted and it's ref count goes to zero
         if (!m_brdfTexture)
         {
             const AZStd::shared_ptr<const RPI::PassTemplate> brdfTextureTemplate = RPI::PassSystemInterface::Get()->GetPassTemplate(Name("BRDFTextureTemplate"));
@@ -1667,6 +1817,7 @@ namespace AtomSampleViewer
             [[maybe_unused]] bool result = scene->UnsetSubsystem(m_rpiScene);
             AZ_Assert(result, "SampleComponentManager failed to unregister its RPI scene from the general scene.");
             
+            m_xrPipelines.clear();
             m_rpiScene = nullptr;
         }
     }
@@ -1686,5 +1837,13 @@ namespace AtomSampleViewer
                 ActivateInternal();
             });
     }
+    
+    void SampleComponentManager::SetRHISamplePass(BasicRHIComponent* sampleComponent)
+    {
+        for (AZ::RPI::Ptr<RHISamplePass> samplePass : m_rhiSamplePasses)
+        {
+            samplePass->SetRHISample(sampleComponent);
+        }
+    }
 
 } // namespace AtomSampleViewer

+ 9 - 2
Gem/Code/Source/SampleComponentManager.h

@@ -148,6 +148,7 @@ namespace AtomSampleViewer
         void SampleChange();
         void CameraReset();
         void ShutdownActiveSample();
+        void SetRHISamplePass(BasicRHIComponent* sampleComponent);
 
         // SampleComponentManagerRequestBus overrides...
         void Reset() override;
@@ -160,6 +161,8 @@ namespace AtomSampleViewer
         void ResetNumMSAASamples() override;
         void ResetRPIScene() override;
         void ClearRPIScene() override;
+        void EnableRenderPipeline(bool value) override;
+        void EnableXrPipelines(bool value) override;
 
         // FrameCaptureNotificationBus overrides...
         void OnFrameCaptureFinished(AZ::Render::FrameCaptureResult result, const AZStd::string& info) override;
@@ -192,7 +195,7 @@ namespace AtomSampleViewer
         // Entity to hold only example component. It doesn't need an entity context.
         AZ::Entity* m_exampleEntity = nullptr;
 
-        AZ::Component* m_activeSample = nullptr;
+        AZStd::vector<AZ::Component*> m_activeSamples;
 
         AZ::Entity* m_cameraEntity = nullptr;
 
@@ -260,12 +263,16 @@ namespace AtomSampleViewer
 
         // Scene and some variables for RHI samples
         AZ::RPI::ScenePtr m_rhiScene;
-        AZ::RPI::Ptr<RHISamplePass> m_rhiSamplePass = nullptr;
+        AZStd::vector<AZ::RPI::Ptr<RHISamplePass>> m_rhiSamplePasses;
 
         // Scene and some variables for RPI samples
         AZ::RPI::ScenePtr m_rpiScene;
 
         // number of MSAA samples, initialized in Activate() and can vary by platform
         int m_numMSAASamples = 0;
+
+        // Cache PC and XR pipelines
+        AZ::RPI::RenderPipelinePtr m_renderPipeline = nullptr;
+        AZStd::vector<AZ::RPI::RenderPipelinePtr> m_xrPipelines;
     };
 } // namespace AtomSampleViewer

+ 6 - 0
Gem/Code/Source/SampleComponentManagerBus.h

@@ -51,6 +51,12 @@ namespace AtomSampleViewer
 
         //! Clear the RPI scene
         virtual void ClearRPIScene() = 0;
+
+        //! Enables or disables the default render pipeline.
+        virtual void EnableRenderPipeline(bool value) = 0;
+
+        //! Enables or disables the XR pipelines.
+        virtual void EnableXrPipelines(bool value) = 0;
     };
     using SampleComponentManagerRequestBus = AZ::EBus<SampleComponentManagerRequests>;
 

+ 296 - 0
Gem/Code/Source/XRRPIExampleComponent.cpp

@@ -0,0 +1,296 @@
+/*
+ * 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 <XRRPIExampleComponent.h>
+#include <Atom/Component/DebugCamera/NoClipControllerComponent.h>
+#include <Atom/RPI.Public/Scene.h>
+#include <Atom/RPI.Public/RPISystem.h>
+#include <Atom/RPI.Public/RPISystemInterface.h>
+#include <Atom/RPI.Public/Pass/PassFilter.h>
+#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
+#include <Atom/RPI.Reflect/Model/ModelAsset.h>
+#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
+#include <Automation/ScriptableImGui.h>
+#include <Automation/ScriptRunnerBus.h>
+#include <Utils/Utils.h>
+
+#include <SSRExampleComponent_Traits_Platform.h>
+
+
+namespace AtomSampleViewer
+{
+    static const float ControllerOffsetScale = 2.0f;
+    static const float ViewOrientationScale = 10.0f;
+    static const float PixelToDegree = 1.0 / 360.0f;
+
+    void XRRPIExampleComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<XRRPIExampleComponent, AZ::Component>()
+                ->Version(0);
+        }
+    }
+
+    void XRRPIExampleComponent::Activate()
+    {
+        if (m_xrSystem = AZ::RPI::RPISystemInterface::Get()->GetXRSystem();
+            m_xrSystem == nullptr)
+        {
+            return;
+        }
+
+        AZ::TickBus::Handler::BusConnect();
+
+        // setup the camera
+        Camera::CameraRequestBus::EventResult(m_originalFarClipDistance, GetCameraEntityId(), &Camera::CameraRequestBus::Events::GetFarClipDistance);
+        Camera::CameraRequestBus::Event(GetCameraEntityId(), &Camera::CameraRequestBus::Events::SetFarClipDistance, 180.f);
+
+        m_numXrViews = m_xrSystem->GetNumViews();
+
+        // create scene
+        CreateModels();
+        CreateGroundPlane();
+
+        InitLightingPresets(true);
+    }
+
+    void XRRPIExampleComponent::Deactivate()
+    {
+        if (!m_xrSystem)
+        {
+            return;
+        }
+
+        ShutdownLightingPresets();
+
+        GetMeshFeatureProcessor()->ReleaseMesh(m_statueMeshHandle);
+        GetMeshFeatureProcessor()->ReleaseMesh(m_boxMeshHandle);
+        GetMeshFeatureProcessor()->ReleaseMesh(m_shaderBallMeshHandle);
+        GetMeshFeatureProcessor()->ReleaseMesh(m_groundMeshHandle);
+        GetMeshFeatureProcessor()->ReleaseMesh(m_leftControllerMeshHandle);
+        GetMeshFeatureProcessor()->ReleaseMesh(m_rightControllerMeshHandle);
+
+        Camera::CameraRequestBus::Event(GetCameraEntityId(), &Camera::CameraRequestBus::Events::SetFarClipDistance, m_originalFarClipDistance);
+        AZ::Debug::CameraControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::CameraControllerRequestBus::Events::Disable);
+
+        AZ::TickBus::Handler::BusDisconnect();
+    }
+
+    void XRRPIExampleComponent::CreateModels()
+    {
+        GetMeshFeatureProcessor();
+
+        // statue
+        {
+            AZ::Data::Asset<AZ::RPI::MaterialAsset> materialAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::MaterialAsset>("objects/hermanubis/hermanubis_stone.azmaterial", AZ::RPI::AssetUtils::TraceLevel::Assert);
+            AZ::Data::Asset<AZ::RPI::ModelAsset> modelAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>(ATOMSAMPLEVIEWER_TRAIT_SSR_SAMPLE_HERMANUBIS_MODEL_NAME, AZ::RPI::AssetUtils::TraceLevel::Assert);
+            AZ::Transform transform = AZ::Transform::CreateIdentity();
+            transform.SetTranslation(0.0f, 0.0f, -0.05f);
+
+            m_statueMeshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor{ modelAsset }, AZ::RPI::Material::FindOrCreate(materialAsset));
+            GetMeshFeatureProcessor()->SetTransform(m_statueMeshHandle, transform);
+        }
+
+        // cube
+        {
+            AZ::Data::Asset<AZ::RPI::MaterialAsset> materialAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::MaterialAsset>("materials/ssrexample/cube.azmaterial", AZ::RPI::AssetUtils::TraceLevel::Assert);
+            AZ::Data::Asset<AZ::RPI::ModelAsset> modelAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/cube.azmodel", AZ::RPI::AssetUtils::TraceLevel::Assert);
+            AZ::Transform transform = AZ::Transform::CreateIdentity();
+            transform.SetTranslation(-4.5f, 0.0f, 0.49f);
+
+            m_boxMeshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor{ modelAsset }, AZ::RPI::Material::FindOrCreate(materialAsset));
+            GetMeshFeatureProcessor()->SetTransform(m_boxMeshHandle, transform);
+        }
+
+        // shader ball
+        {
+            AZ::Data::Asset<AZ::RPI::MaterialAsset> materialAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::MaterialAsset>("Materials/Presets/PBR/default_grid.azmaterial", AZ::RPI::AssetUtils::TraceLevel::Assert);
+            AZ::Data::Asset<AZ::RPI::ModelAsset> modelAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/ShaderBall_simple.azmodel", AZ::RPI::AssetUtils::TraceLevel::Assert);
+            AZ::Transform transform = AZ::Transform::CreateIdentity();
+            transform *= AZ::Transform::CreateRotationZ(AZ::Constants::Pi);
+            transform.SetTranslation(4.5f, 0.0f, 0.89f);
+
+            m_shaderBallMeshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor{ modelAsset }, AZ::RPI::Material::FindOrCreate(materialAsset));
+            GetMeshFeatureProcessor()->SetTransform(m_shaderBallMeshHandle, transform);
+        }
+
+        // controller meshes
+        {
+            AZ::Data::Asset<AZ::RPI::MaterialAsset> materialAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::MaterialAsset>("Materials/XR/XR_Hand_Controller_ControlerMAT.azmaterial", AZ::RPI::AssetUtils::TraceLevel::Assert);
+            AZ::Data::Asset<AZ::RPI::ModelAsset> modelAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/left_hand_controller.azmodel", AZ::RPI::AssetUtils::TraceLevel::Assert);
+            AZ::Transform transform = AZ::Transform::CreateIdentity();
+
+            m_leftControllerMeshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor{ modelAsset }, AZ::RPI::Material::FindOrCreate(materialAsset));
+            m_rightControllerMeshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor{ modelAsset }, AZ::RPI::Material::FindOrCreate(materialAsset));
+            GetMeshFeatureProcessor()->SetTransform(m_leftControllerMeshHandle, transform);
+            GetMeshFeatureProcessor()->SetTransform(m_rightControllerMeshHandle, transform);
+        }
+    }
+
+    void XRRPIExampleComponent::CreateGroundPlane()
+    {
+        AZ::Render::MeshFeatureProcessorInterface* meshFeatureProcessor = GetMeshFeatureProcessor();
+        if (m_groundMeshHandle.IsValid())
+        {
+            meshFeatureProcessor->ReleaseMesh(m_groundMeshHandle);
+        }
+
+        // load material
+        AZStd::string materialName;
+        switch (m_groundPlaneMaterial)
+        {
+        case 0:
+            materialName = "materials/ssrexample/groundplanechrome.azmaterial";
+            break;
+        case 1:
+            materialName = "materials/ssrexample/groundplanealuminum.azmaterial";
+            break;
+        case 2:
+            materialName = "materials/presets/pbr/default_grid.azmaterial";
+            break;
+        default:
+            materialName = "materials/ssrexample/groundplanemirror.azmaterial";
+            break;
+        }
+
+        AZ::Data::AssetId groundMaterialAssetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(materialName.c_str(), AZ::RPI::AssetUtils::TraceLevel::Error);
+        m_groundMaterialAsset.Create(groundMaterialAssetId);
+
+        // load mesh
+        AZ::Data::Asset<AZ::RPI::ModelAsset> planeModel = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>("objects/plane.azmodel", AZ::RPI::AssetUtils::TraceLevel::Error);
+        m_groundMeshHandle = GetMeshFeatureProcessor()->AcquireMesh(AZ::Render::MeshHandleDescriptor{ planeModel }, AZ::RPI::Material::FindOrCreate(m_groundMaterialAsset));
+
+        AZ::Transform transform = AZ::Transform::CreateIdentity();
+        const AZ::Vector3 nonUniformScale(15.0f, 15.0f, 1.0f);
+        GetMeshFeatureProcessor()->SetTransform(m_groundMeshHandle, transform, nonUniformScale);
+    }
+
+    void XRRPIExampleComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint timePoint)
+    {
+        if (m_resetCamera)
+        {
+            AZ::Debug::CameraControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::CameraControllerRequestBus::Events::Reset);
+            AZ::TransformBus::Event(GetCameraEntityId(), &AZ::TransformBus::Events::SetWorldTranslation, AZ::Vector3(7.5f, -10.5f, 3.0f));
+            AZ::Debug::CameraControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::CameraControllerRequestBus::Events::Enable, azrtti_typeid<AZ::Debug::NoClipControllerComponent>());
+            AZ::Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::NoClipControllerRequests::SetHeading, AZ::DegToRad(22.5f));
+            AZ::Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::NoClipControllerRequests::SetPitch, AZ::DegToRad(-10.0f));
+            m_resetCamera = false;
+        }
+
+        if (m_xrSystem && m_xrSystem->ShouldRender())
+        {
+            AZ::RPI::PoseData frontPoseData;
+            AZ::RHI::ResultCode resultCode = m_xrSystem->GetViewLocalPose(frontPoseData);
+            for (AZ::u32 i = 0; i < m_numXrViews; i++)
+            {
+                //Pose data for the controller
+                AZ::RPI::PoseData controllerPose;
+                resultCode = m_xrSystem->GetControllerPose(i, controllerPose);
+
+                if(resultCode == AZ::RHI::ResultCode::Success && !controllerPose.m_orientation.IsZero())
+                {
+                    AZ::Vector3 camPosition;
+                    AZ::TransformBus::EventResult(camPosition, GetCameraEntityId(), &AZ::TransformBus::Events::GetWorldTranslation);
+                    AZ::Vector3 controllerPositionOffset = controllerPose.m_position * ControllerOffsetScale;
+                    AZ::Vector3 newControllerPos = camPosition + AZ::Vector3(controllerPositionOffset.GetX(), -controllerPositionOffset.GetZ(), controllerPositionOffset.GetY());
+
+                    // Go from y up to z up as a right handed coord system
+                    AZ::Quaternion controllerOrientation = controllerPose.m_orientation;
+                    controllerOrientation.SetX(controllerPose.m_orientation.GetX());
+                    controllerOrientation.SetY(-controllerPose.m_orientation.GetZ());
+                    controllerOrientation.SetZ(controllerPose.m_orientation.GetY());
+
+                    //Apply a Rotation of 90 deg around X axis in order to orient the model to face away from you as default pose
+                    AZ::Transform controllerTransform = AZ::Transform::CreateFromQuaternionAndTranslation(
+                                controllerOrientation * AZ::Quaternion::CreateRotationX(-AZ::Constants::Pi / 2), AZ::Vector3(newControllerPos.GetX(), newControllerPos.GetY(),newControllerPos.GetZ()));
+
+                    AZ::Render::MeshFeatureProcessorInterface::MeshHandle* controllerMeshhandle = &m_leftControllerMeshHandle;
+                    if (i == 1)
+                    {
+                        controllerMeshhandle = &m_rightControllerMeshHandle;
+                    }
+                    GetMeshFeatureProcessor()->SetTransform(*controllerMeshhandle, controllerTransform, AZ::Vector3(m_xrSystem->GetControllerScale(i)));
+                }
+            }
+
+            //Update Camera movement (left, right forward, back) based on left JoyStick controller
+            float m_xJoyStickValue = m_xrSystem->GetXJoyStickState(0);
+            float m_yJoyStickValue = m_xrSystem->GetYJoyStickState(0);
+            AZ::Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::NoClipControllerRequests::SetCameraStateForward, m_yJoyStickValue);
+            AZ::Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::NoClipControllerRequests::SetCameraStateBack, m_yJoyStickValue);
+            AZ::Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::NoClipControllerRequests::SetCameraStateLeft, m_xJoyStickValue);
+            AZ::Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::NoClipControllerRequests::SetCameraStateRight, m_xJoyStickValue);
+
+            //Update Camera movement (Up, Down) based on X,Y button presses on the left controller
+            float yButtonState = m_xrSystem->GetYButtonState();
+            float xButtonState = m_xrSystem->GetXButtonState();
+            AZ::Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::NoClipControllerRequests::SetCameraStateUp, yButtonState);
+            AZ::Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::NoClipControllerRequests::SetCameraStateDown, xButtonState);
+
+            // Switch to updating the view using right joystick controller if the Trigger button on the right controller is pressed
+            m_rightTriggerButtonPressed = (m_xrSystem->GetTriggerState(1) > 0.1f);
+            if (m_rightTriggerButtonPressed)
+            {
+                //Update Camera view based on right JoyStick controller
+                float m_xRightJoyStickValue = m_xrSystem->GetXJoyStickState(1);
+                float m_yRightJoyStickValue = m_xrSystem->GetYJoyStickState(1);
+                float heading, pitch = 0.0f;
+                AZ::Debug::NoClipControllerRequestBus::EventResult(heading, GetCameraEntityId(), &AZ::Debug::NoClipControllerRequests::GetHeading);
+                AZ::Debug::NoClipControllerRequestBus::EventResult(pitch, GetCameraEntityId(), &AZ::Debug::NoClipControllerRequests::GetPitch);
+                heading -= m_xRightJoyStickValue * PixelToDegree * ViewOrientationScale;
+                pitch += m_yRightJoyStickValue * PixelToDegree * ViewOrientationScale;
+                pitch = AZStd::max(pitch, -AZ::Constants::HalfPi);
+                pitch = AZStd::min(pitch, AZ::Constants::HalfPi);
+                AZ::Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::NoClipControllerRequests::SetHeading, heading);
+                AZ::Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &AZ::Debug::NoClipControllerRequests::SetPitch, pitch);
+            }
+            else
+            {
+                //Convert to O3de's coordinate system and update the camera orientation for the correct eye view
+                AZ::Quaternion viewLocalPoseOrientation = frontPoseData.m_orientation;
+                viewLocalPoseOrientation.SetX(-frontPoseData.m_orientation.GetX());
+                viewLocalPoseOrientation.SetY(frontPoseData.m_orientation.GetZ());
+                viewLocalPoseOrientation.SetZ(-frontPoseData.m_orientation.GetY());
+                for (AZ::u32 i = 0; i < m_numXrViews; i++)
+                {
+                    Camera::CameraRequestBus::Event(GetCameraEntityId(), &Camera::CameraRequestBus::Events::SetXRViewQuaternion, viewLocalPoseOrientation, i);
+                }
+            }
+
+            // Switch to the next lighting preset using the B-button
+            if (m_xrSystem->GetBButtonState() > 0.0f)
+            {
+                if (!m_bButtonPressed)
+                {
+                    m_bButtonPressed = true;
+                    IterateToNextLightingPreset();
+                }
+            }
+            else
+            {
+                m_bButtonPressed = false;
+            }
+
+            // Switch to the next ground floor using the A-button
+            if (m_xrSystem->GetAButtonState() > 0.0f)
+            {
+                if (!m_aButtonPressed)
+                {
+                    m_aButtonPressed = true;
+                    m_groundPlaneMaterial = (m_groundPlaneMaterial + 1) % 4;
+                    CreateGroundPlane();
+                }
+            }
+            else
+            {
+                m_aButtonPressed = false;
+            }
+        }
+    }
+}

+ 86 - 0
Gem/Code/Source/XRRPIExampleComponent.h

@@ -0,0 +1,86 @@
+/*
+ * 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/TickBus.h>
+#include <Atom/Component/DebugCamera/CameraComponent.h>
+#include <Atom/Feature/Mesh/MeshFeatureProcessorInterface.h>
+#include <Atom/Feature/ImGui/ImGuiUtils.h>
+#include <CommonSampleComponentBase.h>
+#include <Utils/ImGuiSidebar.h>
+#include <Utils/Utils.h>
+
+
+namespace AtomSampleViewer
+{
+    //!
+    //! This component creates a simple scene that tests VR using a special multi-view VR pipeline. We setup two pipelines, one for each eye and use stereoscopic view for this pipeline.
+    //! This sample supports Quest 2 controllers to fly around the world. It also has support to use button presses for specific functionality 
+    //! in the scene. The schema for each controller is below
+    //! Left controller
+    //!         Joystick - Camera movement, Button X - Camera Up (View space y-axis), Button Y - Camera Down (View space Y axis), Squeeze - Scales Controller model     
+     //! Right controller
+    //!         Joystick - View Orientation if Trigger button is pressed, otherwise it will use device for view tracking, 
+    //!         Button B - Iterate through lighting preset, Button B - Iterate through ground plane material, Squeeze - Scales Controller model     
+    //!
+    class XRRPIExampleComponent final
+        : public CommonSampleComponentBase
+        , public AZ::TickBus::Handler
+    {
+    public:
+        AZ_COMPONENT(AtomSampleViewer::XRRPIExampleComponent, "{3122B48E-2553-4568-8B8B-532C105CB83B}", CommonSampleComponentBase);
+
+        static void Reflect(AZ::ReflectContext* context);
+
+        XRRPIExampleComponent() = default;
+        ~XRRPIExampleComponent() override = default;
+
+        void Activate() override;
+        void Deactivate() override;
+
+    private:
+        AZ_DISABLE_COPY_MOVE(XRRPIExampleComponent);
+
+        void CreateModels();
+        void CreateGroundPlane();
+        
+        // AZ::TickBus::Handler
+        void OnTick(float deltaTime, AZ::ScriptTimePoint timePoint) override;
+
+        void OnModelReady(AZ::Data::Instance<AZ::RPI::Model> model);
+
+        // meshes
+        AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_statueMeshHandle;
+        AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_boxMeshHandle;
+        AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_shaderBallMeshHandle;
+        AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_groundMeshHandle;
+        AZ::Data::Asset<AZ::RPI::MaterialAsset> m_groundMaterialAsset;
+        AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_leftControllerMeshHandle;
+        AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_rightControllerMeshHandle;
+
+        // ground plane default material setting index
+        int m_groundPlaneMaterial = 2;
+
+        // IBL and skybox
+        Utils::DefaultIBL m_defaultIbl;
+        AZ::Data::Asset<AZ::RPI::StreamingImageAsset> m_skyboxImageAsset;
+
+        bool m_resetCamera = true;
+        float m_originalFarClipDistance = 0.0f;
+
+        bool m_xButtonPressed = false;
+        bool m_yButtonPressed = false;
+        bool m_aButtonPressed = false;
+        bool m_bButtonPressed = false;
+        bool m_rightTriggerButtonPressed = false;
+
+        AZ::RPI::XRRenderingInterface* m_xrSystem = nullptr;
+        AZ::u32 m_numXrViews = 0;
+    };
+} // namespace AtomSampleViewer

+ 0 - 7
Gem/Code/Tools/AtomSampleViewerToolsSystemComponent.cpp

@@ -7,8 +7,6 @@
  */
 
 #include <Tools/AtomSampleViewerToolsSystemComponent.h>
-#include <MaterialFunctors/StacksShaderCollectionFunctorSourceData.h>
-#include <MaterialFunctors/StacksShaderInputFunctorSourceData.h>
 
 #include <Atom/RPI.Edit/Material/MaterialFunctorSourceDataRegistration.h>
 
@@ -28,15 +26,10 @@ namespace AtomSampleViewer
                     ->Version(0)
                 ;
             }
-
-            StacksShaderCollectionFunctorSourceData::Reflect(context);
-            StacksShaderInputFunctorSourceData::Reflect(context);
         }
 
         void AtomSampleViewerToolsSystemComponent::Activate()
         {
-            AZ::RPI::MaterialFunctorSourceDataRegistration::Get()->RegisterMaterialFunctor("StacksShaderCollection", azrtti_typeid<StacksShaderCollectionFunctorSourceData>());
-            AZ::RPI::MaterialFunctorSourceDataRegistration::Get()->RegisterMaterialFunctor("StacksShaderInput",      azrtti_typeid<StacksShaderInputFunctorSourceData>());
         }
 
         void AtomSampleViewerToolsSystemComponent::Deactivate()

+ 0 - 43
Gem/Code/Tools/MaterialFunctors/StacksShaderCollectionFunctorSourceData.cpp

@@ -1,43 +0,0 @@
-/*
- * 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 <MaterialFunctors/StacksShaderCollectionFunctorSourceData.h>
-#include <Atom/RPI.Reflect/Shader/ShaderOptionGroupLayout.h>
-#include <AzCore/Serialization/SerializeContext.h>
-
-namespace AtomSampleViewer
-{
-    void StacksShaderCollectionFunctorSourceData::Reflect(AZ::ReflectContext* context)
-    {
-        if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
-        {
-            serializeContext->Class<StacksShaderCollectionFunctorSourceData>()
-                ->Version(2)
-                ;
-        }
-    }
-
-    AZ::RPI::MaterialFunctorSourceData::FunctorResult StacksShaderCollectionFunctorSourceData::CreateFunctor(const RuntimeContext& context) const
-    {
-        using namespace AZ;
-        using namespace AZ::RPI;
-
-        Ptr<StacksShaderCollectionFunctor> functor = aznew StacksShaderCollectionFunctor;
-
-        functor->m_stackCountProperty = context.FindMaterialPropertyIndex(Name("stacks.stackCount"));
-        functor->m_highlightLastStackProperty = context.FindMaterialPropertyIndex(Name("stacks.highlightLastStack"));
-        AddMaterialPropertyDependency(functor, functor->m_stackCountProperty);
-        AddMaterialPropertyDependency(functor, functor->m_highlightLastStackProperty);
-
-        // StacksShaderCollectionFunctorSourceData directly corresponds to Comprehensive.material, which uses the same ShaderAsset for all passes, 
-        // so we can just use the ShaderOptionGroupLayout from the first ShaderAsset.
-        functor->m_highlightLastStackOption = context.FindShaderOptionIndex(0, AZ::Name{"o_highlighted2"});
-
-        return Success(Ptr<MaterialFunctor>(functor));
-    }
-}

+ 0 - 32
Gem/Code/Tools/MaterialFunctors/StacksShaderCollectionFunctorSourceData.h

@@ -1,32 +0,0 @@
-/*
- * 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 <MaterialFunctors/StacksShaderCollectionFunctor.h>
-#include <Atom/RPI.Edit/Material/MaterialFunctorSourceData.h>
-
-namespace AtomSampleViewer
-{
-    class StacksShaderCollectionFunctor;
-
-    //! This is an example of a MaterialFunctorSourceData subclass which creates a 
-    //! MaterialFunctor during asset processing. 
-    //! This is used with comprehensive.material to enable/disable variants of the "stacks" shader.
-    class StacksShaderCollectionFunctorSourceData final
-        : public AZ::RPI::MaterialFunctorSourceData
-    {
-    public:
-        AZ_RTTI(StacksShaderCollectionFunctorSourceData, "{2B4678D5-5C1B-4BEE-99E5-9EC9FB871D37}", AZ::RPI::MaterialFunctorSourceData);
-
-        static void Reflect(AZ::ReflectContext* context);
-        
-        using AZ::RPI::MaterialFunctorSourceData::CreateFunctor;
-        FunctorResult CreateFunctor(const RuntimeContext& context) const override;
-    };
-} // namespace AtomSampleViewer

+ 0 - 35
Gem/Code/Tools/MaterialFunctors/StacksShaderInputFunctorSourceData.cpp

@@ -1,35 +0,0 @@
-/*
- * 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 <MaterialFunctors/StacksShaderInputFunctorSourceData.h>
-#include <Atom/RHI.Reflect/ShaderResourceGroupLayout.h>
-#include <AzCore/Serialization/SerializeContext.h>
-
-namespace AtomSampleViewer
-{
-    void StacksShaderInputFunctorSourceData::Reflect(AZ::ReflectContext* context)
-    {
-        if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
-        {
-            serializeContext->Class<StacksShaderInputFunctorSourceData>()
-                ->Version(2)
-                ;
-        }
-    }
-
-    AZ::RPI::MaterialFunctorSourceData::FunctorResult StacksShaderInputFunctorSourceData::CreateFunctor(const RuntimeContext& context) const
-    {
-        AZ::RPI::Ptr<StacksShaderInputFunctor> functor = aznew StacksShaderInputFunctor;
-        functor->m_azimuthDegreesIndex = context.FindMaterialPropertyIndex(AZ::Name{ "light.azimuthDegrees" });
-        functor->m_elevationDegreesIndex = context.FindMaterialPropertyIndex(AZ::Name{ "light.elevationDegrees" });
-        AddMaterialPropertyDependency(functor, functor->m_azimuthDegreesIndex);
-        AddMaterialPropertyDependency(functor, functor->m_elevationDegreesIndex);
-        functor->m_lightDirectionIndex = context.GetShaderResourceGroupLayout()->FindShaderInputConstantIndex(AZ::Name{ "m_lightDir" });
-        return AZ::Success(AZ::RPI::Ptr<AZ::RPI::MaterialFunctor>(functor));
-    }
-}

+ 0 - 31
Gem/Code/Tools/MaterialFunctors/StacksShaderInputFunctorSourceData.h

@@ -1,31 +0,0 @@
-/*
- * 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 <MaterialFunctors/StacksShaderInputFunctor.h>
-#include <Atom/RPI.Edit/Material/MaterialFunctorSourceData.h>
-
-namespace AtomSampleViewer
-{
-    //! This is an example of a ShaderInputFunctorSourceData subclass which creates a MaterialFunctor during asset processing. 
-    //! This is used with comprehensive.materialtype to transform angle values into a light direction vector.
-    class StacksShaderInputFunctorSourceData final
-        : public AZ::RPI::MaterialFunctorSourceData
-    {
-    public:
-        AZ_RTTI(StacksShaderInputFunctorSourceData, "{6952C7F4-88F7-4840-8A29-4A3AE57F099C}", AZ::RPI::MaterialFunctorSourceData);
-
-        static void Reflect(AZ::ReflectContext* context);
-
-    private:
-        using AZ::RPI::MaterialFunctorSourceData::CreateFunctor;
-        FunctorResult CreateFunctor(const RuntimeContext& context) const override;
-    };
-
-} // namespace AtomSampleViewer

+ 0 - 4
Gem/Code/atomsampleviewergem_lib_files.cmake

@@ -7,8 +7,4 @@
 #
 
 set(FILES
-    Lib/MaterialFunctors/StacksShaderCollectionFunctor.h
-    Lib/MaterialFunctors/StacksShaderCollectionFunctor.cpp
-    Lib/MaterialFunctors/StacksShaderInputFunctor.h
-    Lib/MaterialFunctors/StacksShaderInputFunctor.cpp
 )

+ 4 - 0
Gem/Code/atomsampleviewergem_private_files.cmake

@@ -85,6 +85,8 @@ set(FILES
     Source/RHI/RayTracingExampleComponent.h
     Source/RHI/MatrixAlignmentTestExampleComponent.cpp
     Source/RHI/MatrixAlignmentTestExampleComponent.h
+    Source/RHI/XRExampleComponent.cpp
+    Source/RHI/XRExampleComponent.h
     Source/RHI/VariableRateShadingExampleComponent.cpp
     Source/RHI/VariableRateShadingExampleComponent.h
     Source/Performance/HighInstanceExampleComponent.cpp
@@ -196,4 +198,6 @@ set(FILES
     Source/Utils/Utils.h
     Source/Utils/ImGuiProgressList.cpp
     Source/Utils/ImGuiProgressList.h
+    Source/XRRPIExampleComponent.cpp
+    Source/XRRPIExampleComponent.h
 )

+ 0 - 4
Gem/Code/atomsampleviewergem_tools_files.cmake

@@ -9,8 +9,4 @@
 set(FILES
     Tools/AtomSampleViewerToolsSystemComponent.h
     Tools/AtomSampleViewerToolsSystemComponent.cpp
-    Tools/MaterialFunctors/StacksShaderCollectionFunctorSourceData.h
-    Tools/MaterialFunctors/StacksShaderCollectionFunctorSourceData.cpp
-    Tools/MaterialFunctors/StacksShaderInputFunctorSourceData.h
-    Tools/MaterialFunctors/StacksShaderInputFunctorSourceData.cpp
 )

+ 2 - 0
Gem/Code/enabled_gems.cmake

@@ -24,4 +24,6 @@ set(ENABLED_GEMS
     UiBasics
     StreamerProfiler
     DiffuseProbeGrid
+    XR
+    OpenXRVk
 )

+ 26 - 0
Materials/XR/XR_Hand_Controller_ControlerMAT.material

@@ -0,0 +1,26 @@
+{
+    "materialType": "Materials/Types/StandardPBR.materialtype",
+    "materialTypeVersion": 5,
+    "propertyValues": {
+        "baseColor.color": [
+            0.800000011920929,
+            0.800000011920929,
+            0.800000011920929,
+            1.0
+        ],
+        "baseColor.textureBlendMode": "Lerp",
+        "baseColor.textureMap": "Materials/XR/Textures/ControlerMAT_BaseColor.png",
+        "emissive.color": [
+            0.0,
+            0.9666590094566345,
+            1.0,
+            1.0
+        ],
+        "emissive.enable": true,
+        "emissive.textureMap": "Materials/XR/Textures/ControlerMAT_Emissive.png",
+        "irradiance.irradianceColorSource": "BaseColor",
+        "opacity.factor": 1.0,
+        "roughness.textureMap": "Materials/XR/Textures/ControlerMAT_Roughness.png",
+        "specularF0.factor": 0.10000000149011612
+    }
+}

+ 3 - 0
Materials/XR/textures/ControlerMAT_BaseColor.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:95251161fa2692d790da835acb658eb7ce4c0712b10cb50bcbb04c11f37d827d
+size 8919

+ 3 - 0
Materials/XR/textures/ControlerMAT_Emissive.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c4192d49b4da2c9d35b533148cbc86860686d38b287c69a8b1a5868ce4194d70
+size 4533

+ 3 - 0
Materials/XR/textures/ControlerMAT_Roughness.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:00bfda11447ae3523ebcf38ccaa8b6b857c1c9143e0586a33c55349794131a7f
+size 4799

+ 3 - 0
Objects/Left_Hand_Controller.fbx

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cdbe3cac006e138534fe7d77a61e5fe40fc3de77472f51b76031e4ef98051549
+size 1957820

+ 3 - 0
Objects/Right_Hand_Controller.fbx

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:df8523b3c8e2e194f3949937cc10a87ba2120ed0a3dad613910deaccf841101a
+size 1977484

+ 8 - 0
Passes/ASV/PassTemplates.azasset

@@ -36,6 +36,14 @@
                 "Name": "RHISamplePipelineTemplate",
                 "Path": "Passes/RHISamplePipeline.pass"
             },
+            {
+                "Name": "RHISamplePipelineXRLeftTemplate",
+                "Path": "Passes/RHISamplePipelineXRLeft.pass"
+            },
+            {
+                "Name": "RHISamplePipelineXRRightTemplate",
+                "Path": "Passes/RHISamplePipelineXRRight.pass"
+            },
             {
                 "Name": "SsaoPipeline",
                 "Path": "Passes/SsaoPipeline.pass"

+ 44 - 0
Passes/MainRenderPipeline.azasset

@@ -0,0 +1,44 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "RenderPipelineDescriptor",
+    "ClassData": {
+        "Name": "MainPipeline",
+        "MainViewTag": "MainCamera",
+        "RootPassTemplate": "MainPipeline",
+        "AllowModification": true,
+        "RenderSettings": {
+            "MultisampleState": {
+                "samples": 2,
+                "customPositionsCount": 16,
+                "customPositions": [
+                    // First sample is dead center of the pixel for accurate resolve -> non-MSAA depth
+                    // When we resolve MSAA depth to non-MSAA depth, we just pick this first sample
+                    // Being at the center of the pixel makes SSAO and world space reprojection more accurate
+                    { "x":  8,   "y":  8 },
+
+                    // Second sample is top-left corner so that samples from neighboring pixels can be used for
+                    // Quincux anti-aliasing techniques
+                    { "x":  0,   "y":  0 },
+                    
+                    { "x":  7,   "y": 15 },
+                    { "x": 15,   "y":  7 },
+                    
+                    { "x":  8,   "y":  0 },
+                    { "x":  0,   "y":  8 },
+                    { "x":  4,   "y": 11 },
+                    { "x": 11,   "y":  4 },
+                    
+                    { "x":  0,   "y": 15 },
+                    { "x": 15,   "y":  0 },
+                    { "x": 15,   "y": 15 },
+                    { "x":  3,   "y":  3 },
+                    { "x":  4,   "y":  7 },
+                    { "x":  7,   "y":  4 },
+                    { "x": 10,   "y": 13 },
+                    { "x": 13,   "y": 10 }
+                ]
+            }
+        }
+    }
+}

+ 71 - 0
Passes/RHISamplePipelineXRLeft.pass

@@ -0,0 +1,71 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "PassAsset",
+    "ClassData": {
+        "PassTemplate": {
+            "Name": "RHISamplePipelineXRLeftTemplate",
+            "PassClass": "ParentPass",
+            "Slots": [
+                {
+                    "Name": "PipelineOutput",
+                    "SlotType": "InputOutput"
+                }
+            ],
+            "PassData": {
+                "$type": "PassData",
+                "PipelineGlobalConnections": [
+                    {
+                        "GlobalName": "PipelineOutput",
+                        "Slot": "PipelineOutput"
+                    }
+                ]
+            },
+            "PassRequests": [
+                {
+                    "Name": "RHISamplePass",
+                    "TemplateName": "RHISamplePassTemplate",
+                    "Enabled": true
+                },
+                {
+                    "Name": "ImGuiPass",
+                    "TemplateName": "ImGuiPassTemplate",
+                    "Enabled": true,
+                    "Connections": [
+                        {
+                            "LocalSlot": "InputOutput",
+                            "AttachmentRef": {
+                                "Pass": "RHISamplePass",
+                                "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"
+                            }
+                        }
+                    ]
+                }
+            ]
+        }
+    }
+}

+ 71 - 0
Passes/RHISamplePipelineXRRight.pass

@@ -0,0 +1,71 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "PassAsset",
+    "ClassData": {
+        "PassTemplate": {
+            "Name": "RHISamplePipelineXRRightTemplate",
+            "PassClass": "ParentPass",
+            "Slots": [
+                {
+                    "Name": "PipelineOutput",
+                    "SlotType": "InputOutput"
+                }
+            ],
+            "PassData": {
+                "$type": "PassData",
+                "PipelineGlobalConnections": [
+                    {
+                        "GlobalName": "PipelineOutput",
+                        "Slot": "PipelineOutput"
+                    }
+                ]
+            },
+            "PassRequests": [
+                {
+                    "Name": "RHISamplePass",
+                    "TemplateName": "RHISamplePassTemplate",
+                    "Enabled": true
+                },
+                {
+                    "Name": "ImGuiPass",
+                    "TemplateName": "ImGuiPassTemplate",
+                    "Enabled": true,
+                    "Connections": [
+                        {
+                            "LocalSlot": "InputOutput",
+                            "AttachmentRef": {
+                                "Pass": "RHISamplePass",
+                                "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"
+                            }
+                        }
+                    ]
+                }
+            ]
+        }
+    }
+}

+ 1 - 0
Scripts/MaterialScreenshotTests.bv.lua

@@ -44,6 +44,7 @@ function GenerateMaterialScreenshot(imageComparisonThresholdLevel, materialName,
         options.showGroundPlane = false
     end
 
+    Print("Generating material screenshot for " .. materialName)
     Print("MODEL: " .. options.model)
 
     materialName = string.lower(materialName)

+ 0 - 3
Shaders/ComprehensiveTestMaterial/Comprehensive.material

@@ -1,3 +0,0 @@
-{
-    "materialType": "Comprehensive.materialtype"
-}

+ 0 - 183
Shaders/ComprehensiveTestMaterial/Comprehensive.materialtype

@@ -1,183 +0,0 @@
-{
-    "description": "This material tests as many material system features as possible in one place. It splits an object into several stacks of slices, with each stack oscillating independently. This is achieved by rendering a separate pass for each stack, demonstrating multi-pass rendering. The stacks/passes can be disabled independently, demonstrating the ShaderCollectionFunctor feature. There is also a light source, which can be moved using angle values, and these values are converted to a direction vector before being sent to the shader, which demonstrates the use of a ShaderInputFunctor.",
-    "version": 1,
-    "propertyLayout": {
-        "groups": [
-            {
-                "id": "stacks",
-                "displayName": "Stack Settings",
-                "description": "Settings for the oscillating stack of slices that form the shell."
-            },
-            {
-                "id": "light",
-                "displayName": "Light Settings",
-                "description": "Controls the direction of the light."
-            }
-        ],
-        "properties": {
-            "stacks": [
-                {
-                    "id": "color",
-                    "displayName": "Stacks Color",
-                    "description": "The color for the stacks.",
-                    "type": "color",
-                    "defaultValue": [ 0.9, 0.9, 0.9 ],
-                    "connection": {
-                        "type": "shaderInput",
-                        "id": "m_color"
-                    }
-                },
-                {
-                    "id": "stackCount",
-                    "displayName": "Stack Count",
-                    "description": "The number of independent stacks. This demonstrates a shader option connection that applies to all shaders in the list, because it doesn't specify a shaderIndex.",
-                    "type": "uint",
-                    "defaultValue": 4,
-                    "min": 2,
-                    "max": 4,
-                    "step": 1,
-                    "connection": {
-                        "type": "shaderOption",
-                        "id": "o_stackCount"
-                    }
-                },
-                {
-                    "id": "sliceThickness",
-                    "displayName": "Slice Thickness",
-                    "description": "Sets how thick each slice should be. Thicker slices will reduce the number of slices composing the object.",
-                    "type": "float",
-                    "defaultValue": 0.1,
-                    "min": 0.01,
-                    "max": 1.0,
-                    "step": 0.001,
-                    "connection": {
-                        "type": "shaderInput",
-                        "id": "m_sliceThickness"
-                    }
-                },
-                {
-                    "id": "animate1",
-                    "displayName": "Animate Stack 1",
-                    "description": "Whether the first stack of slices should be animated. This demonstrates a shader option connection to a specific shaders in the list, because it specifies a shaderIndex.",
-                    "type": "bool",
-                    "defaultValue": true,
-                    "connection": {
-                        "type": "shaderOption",
-                        "id": "o_animated",
-                        "shaderIndex": 0
-                    }
-                },
-                {
-                    "id": "animate2",
-                    "displayName": "Animate Stack 2",
-                    "description": "Whether the second stack of slices should be animated. This demonstrates a shader option connection to a specific shaders in the list, because it specifies a shaderIndex.",
-                    "type": "bool",
-                    "defaultValue": true,
-                    "connection": {
-                        "type": "shaderOption",
-                        "id": "o_animated",
-                        "shaderIndex": 1
-                    }
-                },
-                {
-                    "id": "animate3",
-                    "displayName": "Animate Stack 3",
-                    "description": "Whether the third stack of slices should be animated. This demonstrates a shader option connection to a specific shaders in the list, because it specifies a shaderIndex.",
-                    "type": "bool",
-                    "defaultValue": true,
-                    "connection": {
-                        "type": "shaderOption",
-                        "id": "o_animated",
-                        "shaderIndex": 2
-                    }
-                },
-                {
-                    "id": "animate4",
-                    "displayName": "Animate Stack 4",
-                    "description": "Whether the fourth stack of slices should be animated. This demonstrates a shader option connection to a specific shaders in the list, because it specifies a shaderIndex.",
-                    "type": "bool",
-                    "defaultValue": true,
-                    "connection": {
-                        "type": "shaderOption",
-                        "id": "o_animated",
-                        "shaderIndex": 3
-                    }
-                },
-                {
-                    "id": "highlightMainSlices",
-                    "displayName": "Highlight Permanent Slices",
-                    "description": "Highlight the first two stacks of slices, which are always present. This demonstrates a property that is connected to multiple shader settings.",
-                    "type": "bool",
-                    "defaultValue": false,
-                    "connection": [
-                        {
-                            "type": "shaderOption",
-                            "id": "o_highlighted",
-                            "shaderIndex": 0
-                        },
-                        {
-                            "type": "shaderOption",
-                            "id": "o_highlighted",
-                            "shaderIndex": 1
-                        }
-                    ]
-                },
-                {
-                    "id": "highlightLastStack",
-                    "displayName": "Highlight Last Stack",
-                    "description": "Highlight the last stack of slices. This demonstrates a shader option that is applied by a functor rather than a direct connection. See StacksShaderCollectionFunctor and 'o_highlighted2'.",
-                    "type": "bool",
-                    "defaultValue": false
-                }
-            ],
-            "light": [
-                {
-                    "id": "azimuthDegrees",
-                    "displayName": "Azimuth (degrees)",
-                    "type": "float",
-                    "min": 0.0,
-                    "max": 360.0,
-                    "step": 1.0
-                },
-                {
-                    "id": "elevationDegrees",
-                    "displayName": "Elevation (degrees)",
-                    "type": "float",
-                    "min": -90.0,
-                    "max": 90.0,
-                    "step": 1.0
-                }
-            ]
-        }
-    },
-    "shaders": [
-        {
-            "file": "Stacks.shader",
-            "options": {
-                "o_wobbleDirection": "Direction::Left",
-                "o_stackIndex": 0
-            }
-        },
-        {
-            "file": "Stacks.shader",
-            "options": {
-                "o_wobbleDirection": "Direction::Right",
-                "o_stackIndex": 1
-            }
-        },
-        {
-            "file": "Stacks.shader",
-            "options": {
-                "o_wobbleDirection": "Direction::Up",
-                "o_stackIndex": 2
-            }
-        },
-        {
-            "file": "Stacks.shader",
-            "options": {
-                "o_wobbleDirection": "Direction::Down",
-                "o_stackIndex": 3
-            }
-        }
-    ]
-}

+ 0 - 10
Shaders/ComprehensiveTestMaterial/Comprehensive_variant.material

@@ -1,10 +0,0 @@
-{
-    "materialType": "Comprehensive.materialtype",
-    "materialTypeVersion": 1,
-    "propertyValues": {
-        "light.azimuthDegrees": 45.0,
-        "light.elevationDegrees": -45.0,
-        "stacks.sliceThickness": 0.02500000037252903,
-        "stacks.stackCount": 2
-    }
-}

+ 0 - 152
Shaders/ComprehensiveTestMaterial/Stacks.azsl

@@ -1,152 +0,0 @@
-/*
- * 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 <scenesrg.srgi>
-#include <viewsrg.srgi>
-#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
-#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>
-
-// This shader draws a model with most of the surface clipped away, only drawing regularly spaced slices of it.
-// The idea is to run a model through several variants of this same shader, so the combined appearance is a shell
-// broken into multiple slices that are oscillating in different directions.
-// This demonstrates a material's ability to run multiple shader variants, over multiple passes.
-
-// Indicates which direction the stack of slices will animate/wobble.
-// This demonstrates the use of a ShaderOption that is an enum, and is controlled through a ShaderCollectionFunctor.
-option enum class Direction { None, Left, Right, Up, Down } o_wobbleDirection;
-
-// Indicates the number of independent slice stacks, which will animate together, and the index of the current slice stack.
-// Running the shader for each of the available stacks will render enough slices to cover the entire model.
-[range(2, 4)]
-option uint o_stackCount;
-[range(0, 3)]
-option int o_stackIndex;
-
-// Indicates whether the current stack's wobble animation should be enabled.
-option bool o_animated;
-
-// Indicates whether the current stack's color should be highlighted.
-option bool o_highlighted;
-
-// Indicates whether the current stack's color should be highlighted (using an alternate color).
-option bool o_highlighted2;
-
-ShaderResourceGroup MaterialSrg : SRG_PerMaterial
-{
-    float3 m_color;
-    float3 m_lightDir;
-    float m_sliceThickness;
-};
-
-struct VertexInput
-{
-    float3 m_position : POSITION;
-    float3 m_normal : NORMAL;
-};
-
-struct VertexOutput
-{
-    float4 m_position : SV_Position;
-    float3 m_normal : NORMAL;
-    float4 m_localPos : UV1;
-};
-
-// Oscillates a value between min and max.
-float CalcShift(float speed, float min, float max)
-{
-    float t = sin(SceneSrg::m_time * speed) * 0.5 + 0.5;
-
-    // Make it snap to min a bit
-    t = saturate(t * 2 - 1);
-
-    float shift = lerp(min, max, t); 
-    return shift;
-}
-
-VertexOutput MainVS(VertexInput input)
-{
-    const float4x4 objectToWorldMatrix = ObjectSrg::GetWorldMatrix();
-
-    const float wobbleSize = 0.05;
-    const float wobbleSpeed = 5;
-
-    float3 wobbleDir;
-    switch(o_wobbleDirection)
-    {
-    case Direction::Left:  wobbleDir = float3(-1, 0, 0); break;
-    case Direction::Right: wobbleDir = float3( 1, 0, 0); break;
-    case Direction::Up:    wobbleDir = float3(0, 0,  1); break;
-    case Direction::Down:  wobbleDir = float3(0, 0, -1); break;
-    default:               wobbleDir = float3(0, 0, 0);
-    }
-
-    float3 shift;
-    if (o_animated)
-    {
-        shift = wobbleDir * CalcShift(wobbleSpeed, 0, wobbleSize);
-    }
-    else
-    {
-        shift = wobbleDir * wobbleSize;
-    }
-
-    const float4 position = float4(input.m_position, 1);
-
-    VertexOutput output;
-    output.m_localPos = position;
-    float3 worldPosition = mul(objectToWorldMatrix, position + float4(shift,0)).xyz;
-    output.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0));
-    output.m_normal = mul(ObjectSrg::GetWorldMatrixInverseTranspose(), input.m_normal);
-    output.m_normal = normalize(output.m_normal);
-
-    return output;
-}
-
-struct PixelOutput
-{
-    float4 m_color : SV_Target0;
-};
-
-PixelOutput MainPS(VertexOutput input)
-{
-    PixelOutput output;
-    
-    float sliceThickness = MaterialSrg::m_sliceThickness;
-    float sliceStride = sliceThickness * o_stackCount;
-
-    float arbitraryBigNumber = 1000; // Used to make the slices not weird around 0
-    float stackOffset = abs(fmod(input.m_localPos.y + arbitraryBigNumber, sliceStride)) - sliceThickness * o_stackIndex;
-
-    // Discard if stackOffset < 0
-    clip(stackOffset);
-
-    // Discard if stackOffset > sliceThickness 
-    clip(sliceThickness - stackOffset);
-
-    // fakeLighting is peaks at 0.9, so we can leave room for the o_highlighted option
-    float3 normal = normalize(input.m_normal);
-    float fakeLighting = dot(normal, MaterialSrg::m_lightDir) * 0.6 + 0.3;
-
-    output.m_color.rgb = fakeLighting * MaterialSrg::m_color;
-
-    const float highlightAmount = 0.1;
-
-    if (o_highlighted)
-    {
-        output.m_color.rgb += highlightAmount * float3(1.0, 0.5, 0.5);
-    }
-
-    if (o_highlighted2)
-    {
-        output.m_color.rgb += highlightAmount * float3(0.5, 0.5, 1.0);
-    }
-
-    output.m_color.a = 1;
-
-    return output;
-}

+ 0 - 26
Shaders/ComprehensiveTestMaterial/Stacks.shader

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

+ 0 - 37
Shaders/ComprehensiveTestMaterial/Stacks.shadervariantlist

@@ -1,37 +0,0 @@
-{
-    "Shader": "Shaders/ComprehensiveTestMaterial/Stacks.shader",
-
-    // Note that we don't include pre-compiled ProgramVariants for some shader option values like o_stackCount, to demonstrate the variant fallback feature.
-
-    "Variants": [
-        {
-            "StableId": 1,
-            "Options": {
-                "o_wobbleDirection": "Direction::Left",
-                "o_stackIndex": 0
-            }
-        },
-        {
-            "StableId": 2,
-            "Options": {
-                "o_wobbleDirection": "Direction::Right",
-                "o_stackIndex": 1
-            }
-        },
-        {
-            "StableId": 3,
-            "Options": {
-                "o_wobbleDirection": "Direction::Up",
-                "o_stackIndex": 2
-            }
-        },
-        {
-            "StableId": 4,
-            "Options": {
-                "o_wobbleDirection": "Direction::Down",
-                "o_stackIndex": 3
-            }
-        }
-    ]
-}
-

+ 17 - 9
Shaders/RHI/BindlessPrototype.azsl

@@ -7,10 +7,11 @@
  */
 
 // GlobalSrg::m_floatBuffer
-// PerSceneSrg::m_textureArray
 // PerSceneSrg::m_sampler
 #include "BindlessPrototypeSrg.azsli"
 
+#include <Atom/Features/Bindless.azsli>
+
 struct BindlessMaterial0
 {
     uint4 materialIndex;
@@ -44,7 +45,7 @@ struct PerObject
 
 ShaderResourceGroupSemantic PerSubMesh
 {
-    FrequencyId = 1;
+    FrequencyId = 4;
 };
 
 // [TODO ATOM-2769] When the inline feature is complete, use InlineConstant instead of sending the data
@@ -131,21 +132,28 @@ PixelOutput MainPS(VertexOutput psInput)
         BindlessMaterial1 bindlessMaterial1; 
         ReadFromFloatBuffer(bindlessMaterial1.m_diffuseColor, HandleSrg::m_materialHandle, offset);
         ReadFromFloatBuffer(bindlessMaterial1.m_diffuseTextureIndex, HandleSrg::m_materialHandle, offset);
-
-        Texture2D texture = ImageSrg::m_textureArray[bindlessMaterial1.m_diffuseTextureIndex % 8]; // % 8 for wrap-around texture index as specified in ImageSrg.m_textureArray
-        OUT.m_color = texture.Sample(ImageSrg::m_sampler, psInput.m_uv);
+        
+#ifdef UB_DIRECTBINDING_NOTSUPPPORTED 
+        Texture2D texture = Bindless::GetTexture2D(
+            IndirectionBufferSrg::m_indirectionBuffer.Load(bindlessMaterial1.m_diffuseTextureIndex * 4));
+#else   
+        // % 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);
     }
     else if(materialIndex.x == 2) // Shaded material
     {
-        float4 color;
+        float4 color; 
         BindlessMaterial2 bindlessMaterial2; 
         ReadFromFloatBuffer(bindlessMaterial2.m_diffuseColor, HandleSrg::m_materialHandle, offset);
         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 = ImageSrg::m_textureArray[bindlessMaterial2.m_diffuseTextureIndex % 8]; // % 8 for wrap-around texture index as specified in ImageSrg.m_textureArray
-        color = texture.Sample(ImageSrg::m_sampler, psInput.m_uv);
+        Texture2D texture = Bindless::GetTexture2D(
+            IndirectionBufferSrg::m_indirectionBuffer.Load(bindlessMaterial2.m_diffuseTextureIndex * 4));
+        color = texture.Sample(SamplerSrg::m_sampler, psInput.m_uv);
 
         float3 lightDir;
         uint lightOffset = 0;
@@ -161,4 +169,4 @@ PixelOutput MainPS(VertexOutput psInput)
     }
 
     return OUT;
-}
+}

+ 4 - 2
Shaders/RHI/BindlessPrototype.shader

@@ -7,6 +7,9 @@
 
     "DrawList" : "forward",
 
+    "AddBuildArguments"
+        : { "dxc" : ["-fspv-target-env=vulkan1.2"] },
+
     "ProgramSettings":
     {
       "EntryPoints":
@@ -20,6 +23,5 @@
           "type": "Fragment"
         }
       ]
-    },
-    "DisabledRHIBackends": ["metal"]
+    }
 }

+ 23 - 5
Shaders/RHI/BindlessPrototypeSrg.azsli

@@ -17,12 +17,22 @@ struct FloatBuffer
 // Listed on update frequency
 ShaderResourceGroupSemantic FrequencyPerScene 
 {
-    FrequencyId = 6;
+    FrequencyId = 0;
 };
 
 ShaderResourceGroupSemantic FloatBufferSemanticId
 {
-    FrequencyId = 7;
+    FrequencyId = 1;
+};
+
+ShaderResourceGroupSemantic IndirectionBufferSemanticId
+{
+    FrequencyId = 2;
+};
+
+ShaderResourceGroupSemantic NonBindlessTextureSemanticId
+{
+    FrequencyId = 3;
 };
 
 ShaderResourceGroup FloatBufferSrg : FloatBufferSemanticId
@@ -30,7 +40,12 @@ ShaderResourceGroup FloatBufferSrg : FloatBufferSemanticId
     StructuredBuffer<FloatBuffer> m_floatBuffer;
 };
 
-ShaderResourceGroup ImageSrg : FrequencyPerScene
+ShaderResourceGroup IndirectionBufferSrg : IndirectionBufferSemanticId
+{
+    ByteAddressBuffer m_indirectionBuffer;
+}; 
+
+ShaderResourceGroup SamplerSrg : FrequencyPerScene
 {
     Sampler m_sampler
     {
@@ -39,10 +54,13 @@ ShaderResourceGroup ImageSrg : FrequencyPerScene
         AddressV = Wrap;
         AddressW = Wrap;
     };
+};
 
+ShaderResourceGroup ImageSrg : NonBindlessTextureSemanticId
+{
     // Array of textures
-    Texture2D m_textureArray[];
-}
+    Texture2D m_textureArray[UNBOUNDED_SIZE];
+};
 
 // Helper functions to read data from the FloatBuffer. The FloatBuffer is accessed with an offset and an index.
 // The offset holds the initial offset within the FloatBuffer, and the index is a sub-index, which increments with each property that is being read.

+ 49 - 0
Shaders/RHI/OpenXrSample.azsl

@@ -0,0 +1,49 @@
+/*
+ * 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 OpenXrSrg : SRG_PerObject
+{
+    row_major float4x4 m_worldMatrix;
+    row_major float4x4 m_viewProjMatrix;
+}
+
+struct VSInput
+{
+    float3 m_position : POSITION;
+    float4 m_color : COLOR0;
+};
+
+struct VSOutput
+{
+    float4 m_position : SV_Position;
+    float4 m_color : COLOR0;
+};
+
+VSOutput MainVS(VSInput vsInput)
+{
+    VSOutput OUT;
+    
+    OUT.m_position = mul(OpenXrSrg::m_worldMatrix, float4(vsInput.m_position, 1.0));
+    OUT.m_position = mul(OpenXrSrg::m_viewProjMatrix, OUT.m_position);
+    OUT.m_color = vsInput.m_color;
+    return OUT;
+}
+
+struct PSOutput
+{
+    float4 m_color : SV_Target0;
+};
+
+PSOutput MainPS(VSOutput vsOutput)
+{
+    PSOutput OUT;
+    OUT.m_color = vsOutput.m_color;
+    return OUT;
+}

+ 23 - 0
Shaders/RHI/OpenXrSample.shader

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

+ 3 - 0
Textures/ControlerMAT_BaseColor.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:95251161fa2692d790da835acb658eb7ce4c0712b10cb50bcbb04c11f37d827d
+size 8919

+ 3 - 0
Textures/ControlerMAT_Emissive.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c4192d49b4da2c9d35b533148cbc86860686d38b287c69a8b1a5868ce4194d70
+size 4533

+ 3 - 0
Textures/ControlerMAT_Roughness.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:00bfda11447ae3523ebcf38ccaa8b6b857c1c9143e0586a33c55349794131a7f
+size 4799

+ 0 - 3
atomsampleviewer_asset_files.cmake

@@ -61,9 +61,6 @@ set(FILES
     Shaders/DebugVertexNormals.materialtype
     Shaders/DebugVertexNormals.shader
     Shaders/Instanced.azsl
-    Shaders/ComprehensiveTestMaterial/Comprehensive.materialtype
-    Shaders/ComprehensiveTestMaterial/Stacks.azsl
-    Shaders/ComprehensiveTestMaterial/Stacks.shader
     Shaders/DynamicDraw/DynamicDrawExample.azsl
     Shaders/DynamicDraw/DynamicDrawExample.shader
     Shaders/OptimizationTests/DummyTransformColor.azsl

+ 11 - 1
project.json

@@ -13,5 +13,15 @@
     ],
     "icon_path": "preview.png",
     "engine": "o3de",
-    "external_subdirectories": []
+    "external_subdirectories": [],
+    "gem_names": [
+        {
+            "name": "OpenXRVk",
+            "optional": true
+        },
+        {
+            "name": "XR",
+            "optional": true
+        }
+    ]
 }