Selaa lähdekoodia

Integrating through commit 90f050496

alexpete 4 vuotta sitten
vanhempi
commit
05dbcd9d8e
99 muutettua tiedostoa jossa 1640 lisäystä ja 326 poistoa
  1. 21 7
      Gem/Code/Source/Automation/ScriptReporter.cpp
  2. 4 4
      Gem/Code/Source/BloomExampleComponent.cpp
  3. 4 4
      Gem/Code/Source/BloomExampleComponent.h
  4. 38 9
      Gem/Code/Source/CommonSampleComponentBase.cpp
  5. 2 1
      Gem/Code/Source/CommonSampleComponentBase.h
  6. 10 2
      Gem/Code/Source/DecalExampleComponent.cpp
  7. 2 3
      Gem/Code/Source/MaterialHotReloadTestComponent.cpp
  8. 23 24
      Gem/Code/Source/MeshExampleComponent.cpp
  9. 3 1
      Gem/Code/Source/MeshExampleComponent.h
  10. 8 13
      Gem/Code/Source/MultiSceneExampleComponent.cpp
  11. 273 0
      Gem/Code/Source/Passes/RayTracingAmbientOcclusionPass.cpp
  12. 88 0
      Gem/Code/Source/Passes/RayTracingAmbientOcclusionPass.h
  13. 21 14
      Gem/Code/Source/RHI/AlphaToCoverageExampleComponent.cpp
  14. 3 1
      Gem/Code/Source/RHI/AlphaToCoverageExampleComponent.h
  15. 10 21
      Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.cpp
  16. 4 4
      Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.h
  17. 8 16
      Gem/Code/Source/RHI/InputAssemblyExampleComponent.cpp
  18. 4 0
      Gem/Code/Source/RHI/MSAAExampleComponent.cpp
  19. 1 1
      Gem/Code/Source/RHI/RayTracingExampleComponent.cpp
  20. 10 7
      Gem/Code/Source/RHI/TrianglesConstantBufferExampleComponent.cpp
  21. 34 30
      Gem/Code/Source/RootConstantsExampleComponent.cpp
  22. 9 1
      Gem/Code/Source/SampleComponentManager.cpp
  23. 7 3
      Gem/Code/Source/SkinnedMeshContainer.cpp
  24. 124 35
      Gem/Code/Source/SsaoExampleComponent.cpp
  25. 20 0
      Gem/Code/Source/SsaoExampleComponent.h
  26. 7 4
      Gem/Code/atomsampleviewergem_private_files.cmake
  27. 1 0
      Gem/Code/runtime_dependencies.cmake
  28. 2 0
      Gem/Code/tool_dependencies.cmake
  29. 9 1
      Passes/PassTemplates.azasset
  30. 72 0
      Passes/RayTracingAmbientOcclusion.pass
  31. 28 0
      Passes/SelectorPass.pass
  32. 258 3
      Passes/SsaoPipeline.pass
  33. 7 0
      Registry/editorpreferences.setreg
  34. 1 1
      Scripts/DiffuseGITest.bv.lua
  35. 1 1
      Scripts/DynamicMaterialTest.bv.lua
  36. 1 1
      Scripts/ExpectedScreenshots/AreaLights/disk_double_sided_vary_rough_metal.ppm
  37. 1 1
      Scripts/ExpectedScreenshots/AreaLights/polygon_double_sided_vary_rough_metal.ppm
  38. 1 1
      Scripts/ExpectedScreenshots/AreaLights/polygon_vary_rough_metal.ppm
  39. 1 1
      Scripts/ExpectedScreenshots/AreaLights/quad_double_sided_vary_rough_metal.ppm
  40. 1 1
      Scripts/ExpectedScreenshots/AutoBrick/brick.ppm
  41. 1 1
      Scripts/ExpectedScreenshots/Decals/screenshot_decals.ppm
  42. 1 1
      Scripts/ExpectedScreenshots/DynamicMaterialTest/01_defaultsetup_A.ppm
  43. 1 1
      Scripts/ExpectedScreenshots/DynamicMaterialTest/01_defaultsetup_B.ppm
  44. 1 1
      Scripts/ExpectedScreenshots/DynamicMaterialTest/02_manyentities_A.ppm
  45. 1 1
      Scripts/ExpectedScreenshots/DynamicMaterialTest/02_manyentities_B.ppm
  46. 1 1
      Scripts/ExpectedScreenshots/MinimalPBR/MinimalPBR_BlueMetal.ppm
  47. 1 1
      Scripts/ExpectedScreenshots/MinimalPBR/MinimalPBR_Default.ppm
  48. 1 1
      Scripts/ExpectedScreenshots/MinimalPBR/MinimalPBR_RedDielectric.ppm
  49. 1 1
      Scripts/ExpectedScreenshots/MultiRenderPipeline/IBL_Skybox_Spot_Dir_DOF_window1.ppm
  50. 1 1
      Scripts/ExpectedScreenshots/MultiRenderPipeline/IBL_Skybox_Spot_Dir_DOF_window2.ppm
  51. 1 1
      Scripts/ExpectedScreenshots/MultiRenderPipeline/IBL_Skybox_Spot_Dir_window2.ppm
  52. 1 1
      Scripts/ExpectedScreenshots/MultiRenderPipeline/IBL_Skybox_Spot_window2.ppm
  53. 1 1
      Scripts/ExpectedScreenshots/MultiRenderPipeline/IBL_Skybox_window2.ppm
  54. 1 1
      Scripts/ExpectedScreenshots/MultiRenderPipeline/NoDOF_window2.ppm
  55. 1 1
      Scripts/ExpectedScreenshots/MultiRenderPipeline/Start_window1.ppm
  56. 1 1
      Scripts/ExpectedScreenshots/MultiRenderPipeline/Start_window2.ppm
  57. 1 1
      Scripts/ExpectedScreenshots/MultiRenderPipeline/TwoCameras_window1.ppm
  58. 1 1
      Scripts/ExpectedScreenshots/MultiRenderPipeline/TwoCameras_window2.ppm
  59. 1 1
      Scripts/ExpectedScreenshots/MultiRenderPipeline/WithDOF_window1.ppm
  60. 1 1
      Scripts/ExpectedScreenshots/MultiRenderPipeline/WithDOF_window2.ppm
  61. 1 1
      Scripts/ExpectedScreenshots/PassTree/specularResolved.ppm
  62. 1 1
      Scripts/ExpectedScreenshots/SceneReloadSoakTest/screenshot.ppm
  63. 1 1
      Scripts/ExpectedScreenshots/Skin/001_lucy_regression_test.ppm
  64. 1 1
      Scripts/ExpectedScreenshots/Skin/002_wrinkle_regression_test.ppm
  65. 1 1
      Scripts/ExpectedScreenshots/StandardPBR/001_defaultwhite.ppm
  66. 1 1
      Scripts/ExpectedScreenshots/StandardPBR/003_metalmatte.ppm
  67. 1 1
      Scripts/ExpectedScreenshots/StandardPBR/003_metalpolished.ppm
  68. 1 1
      Scripts/ExpectedScreenshots/StandardPBR/006_specularf0map.ppm
  69. 1 1
      Scripts/ExpectedScreenshots/StandardPBR/007_multiscatteringcompensationoff.ppm
  70. 1 1
      Scripts/ExpectedScreenshots/StandardPBR/007_multiscatteringcompensationon.ppm
  71. 1 1
      Scripts/ExpectedScreenshots/StandardPBR/101_detailmaps_lucybasenodetailmaps.ppm
  72. 1 1
      Scripts/ExpectedScreenshots/StandardPBR/102_detailmaps_all.ppm
  73. 1 1
      Scripts/ExpectedScreenshots/StandardPBR/104_detailmaps_normal.ppm
  74. 1 1
      Scripts/ExpectedScreenshots/StandardPBR/104_detailmaps_normalwithmask.ppm
  75. 1 1
      Scripts/ExpectedScreenshots/StandardPBR/105_detailmaps_blendmaskusingdetailuvs.ppm
  76. 1 1
      Scripts/ExpectedScreenshots/StreamingImage/HotReloading.ppm
  77. 1 1
      Scripts/ExpectedScreenshots/StreamingImage/Streaming2dImages.ppm
  78. 1 1
      Scripts/ExpectedScreenshots/StreamingImage/Streaming3dImage.ppm
  79. 1 1
      Scripts/MSAA_RPI_Test.bv.lua
  80. 12 12
      Scripts/MaterialScreenshotTests.bv.lua
  81. 1 1
      Scripts/StreamingImageTest.bv.lua
  82. 21 0
      Shaders/RayTracing/RTAOClosestHit.azsl
  83. 19 0
      Shaders/RayTracing/RTAOClosestHit.shader
  84. 16 0
      Shaders/RayTracing/RTAODefines.azsli
  85. 140 0
      Shaders/RayTracing/RTAOGeneration.azsl
  86. 19 0
      Shaders/RayTracing/RTAOGeneration.shader
  87. 23 0
      Shaders/RayTracing/RTAOMiss.azsl
  88. 19 0
      Shaders/RayTracing/RTAOMiss.shader
  89. 10 0
      Standalone/PythonTests/Automated/test_suites/main/__init__.py
  90. 74 0
      Standalone/PythonTests/Automated/test_suites/main/test_AtomSampleViewer_main_dx12.py
  91. 10 0
      Standalone/PythonTests/Automated/test_suites/periodic/__init__.py
  92. 48 0
      Standalone/PythonTests/Automated/test_suites/periodic/test_AtomSampleViewer_main_vulkan.py
  93. 2 21
      Standalone/PythonTests/Automated/test_suites/periodic/test_AtomSampleViewer_periodic_dx12.py
  94. 2 21
      Standalone/PythonTests/Automated/test_suites/periodic/test_AtomSampleViewer_periodic_vulkan.py
  95. 10 0
      Standalone/PythonTests/Automated/test_suites/smoke/__init__.py
  96. 12 0
      Standalone/PythonTests/Automated/test_suites/smoke/test_AtomSampleViewer_smoke_dx12.py
  97. 12 0
      Standalone/PythonTests/Automated/test_suites/smoke/test_AtomSampleViewer_smoke_vulkan.py
  98. 18 3
      Standalone/PythonTests/CMakeLists.txt
  99. 11 13
      Standalone/PythonTests/conftest.py

+ 21 - 7
Gem/Code/Source/Automation/ScriptReporter.cpp

@@ -81,12 +81,25 @@ namespace AtomSampleViewer
 
 
         AZStd::string GetOfficialBaseline(const AZStd::string& forScreenshotFile)
         AZStd::string GetOfficialBaseline(const AZStd::string& forScreenshotFile)
         {
         {
-            AZStd::string newPath = forScreenshotFile;
-            if (!AzFramework::StringFunc::Replace(newPath, "user/scripts/screenshots", "atomsampleviewer/scripts/expectedscreenshots"))
+            AZStd::string path = forScreenshotFile;
+            const AZStd::string userPath = Utils::ResolvePath("@user@");
+
+            // make the path relative to the user folder
+            if (!AzFramework::StringFunc::Replace(path, userPath.c_str(), ""))
             {
             {
-                newPath = "";
+                return "";
             }
             }
-            return newPath;
+
+            // After replacing "screenshots" with "expectedscreenshots", the path should be a valid asset path, relative to asset root.
+            if (!AzFramework::StringFunc::Replace(path, "scripts/screenshots", "scripts/expectedscreenshots"))
+            {
+                return "";
+            }
+
+            // Turn it back into a full path
+            path = Utils::ResolvePath("@assets@" + path);
+
+            return path;
         }
         }
     }
     }
 
 
@@ -898,9 +911,8 @@ namespace AtomSampleViewer
         // Get source folder
         // Get source folder
         if (m_officialBaselineSourceFolder.empty())
         if (m_officialBaselineSourceFolder.empty())
         {
         {
-            AZStd::string appRoot;
-            AzFramework::ApplicationRequests::Bus::BroadcastResult(appRoot, &AzFramework::ApplicationRequests::GetAppRoot);
-            AzFramework::StringFunc::Path::Join(appRoot.c_str(), "AtomSampleViewer/Scripts/ExpectedScreenshots", m_officialBaselineSourceFolder);
+            AZStd::string projectPath = AZ::Utils::GetProjectPath();
+            AzFramework::StringFunc::Path::Join(projectPath.c_str(), "Scripts/ExpectedScreenshots", m_officialBaselineSourceFolder);
 
 
             if (!io->Exists(m_officialBaselineSourceFolder.c_str()))
             if (!io->Exists(m_officialBaselineSourceFolder.c_str()))
             {
             {
@@ -1046,6 +1058,8 @@ namespace AtomSampleViewer
                 }
                 }
                 else
                 else
                 {
                 {
+                    // Be aware there is an automation test script that looks for the "Screenshot check failed. Diff score" string text to report failures.
+                    // If you change this message, be sure to update the associated tests as well located here: "C:/path/to/Lumberyard/AtomSampleViewer/Standalone/PythonTests"
                     ReportScreenshotComparisonIssue(
                     ReportScreenshotComparisonIssue(
                         AZStd::string::format("Screenshot check failed. Diff score %f exceeds threshold of %f ('%s').",
                         AZStd::string::format("Screenshot check failed. Diff score %f exceeds threshold of %f ('%s').",
                             screenshotTestInfo.m_officialComparisonResult.m_finalDiffScore, toleranceLevel->m_threshold, toleranceLevel->m_name.c_str()),
                             screenshotTestInfo.m_officialComparisonResult.m_finalDiffScore, toleranceLevel->m_threshold, toleranceLevel->m_name.c_str()),

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

@@ -360,10 +360,10 @@ namespace AtomSampleViewer
             CreatePipeline("Shaders/tonemappingexample/renderimage.azshader", "Shaders/tonemappingexample/renderimage_renderimagesrg.azsrg", m_srgAsset, m_pipelineState, m_drawListTag);
             CreatePipeline("Shaders/tonemappingexample/renderimage.azshader", "Shaders/tonemappingexample/renderimage_renderimagesrg.azsrg", m_srgAsset, m_pipelineState, m_drawListTag);
 
 
             // Set the input indices
             // Set the input indices
-            m_imageInputIndex = m_srgAsset->GetLayout()->FindShaderInputImageIndex(Name("m_texture"));
-            m_positionInputIndex = m_srgAsset->GetLayout()->FindShaderInputConstantIndex(Name("m_position"));
-            m_sizeInputIndex = m_srgAsset->GetLayout()->FindShaderInputConstantIndex(Name("m_size"));
-            m_colorSpaceIndex = m_srgAsset->GetLayout()->FindShaderInputConstantIndex(Name("m_colorSpace"));
+            m_imageInputIndex.Reset();
+            m_positionInputIndex.Reset();
+            m_sizeInputIndex.Reset();
+            m_colorSpaceIndex.Reset();
         }
         }
 
 
         m_drawImage.m_srg = RPI::ShaderResourceGroup::Create(m_srgAsset);
         m_drawImage.m_srg = RPI::ShaderResourceGroup::Create(m_srgAsset);

+ 4 - 4
Gem/Code/Source/BloomExampleComponent.h

@@ -103,10 +103,10 @@ namespace AtomSampleViewer
         AZ::Data::Asset<AZ::RPI::ShaderResourceGroupAsset> m_srgAsset;
         AZ::Data::Asset<AZ::RPI::ShaderResourceGroupAsset> m_srgAsset;
 
 
         // shader input indices
         // shader input indices
-        AZ::RHI::ShaderInputImageIndex m_imageInputIndex;
-        AZ::RHI::ShaderInputConstantIndex m_positionInputIndex;
-        AZ::RHI::ShaderInputConstantIndex m_sizeInputIndex;
-        AZ::RHI::ShaderInputConstantIndex m_colorSpaceIndex;
+        AZ::RHI::ShaderInputNameIndex m_imageInputIndex = "m_texture";
+        AZ::RHI::ShaderInputNameIndex m_positionInputIndex = "m_position";
+        AZ::RHI::ShaderInputNameIndex m_sizeInputIndex = "m_size";
+        AZ::RHI::ShaderInputNameIndex m_colorSpaceIndex = "m_colorSpace";
 
 
         // post processing feature processor
         // post processing feature processor
         AZ::Render::PostProcessFeatureProcessorInterface* m_postProcessFeatureProcessor = nullptr;
         AZ::Render::PostProcessFeatureProcessorInterface* m_postProcessFeatureProcessor = nullptr;

+ 38 - 9
Gem/Code/Source/CommonSampleComponentBase.cpp

@@ -141,7 +141,7 @@ namespace AtomSampleViewer
             if (!m_lightingPresets.empty() && m_currentLightingPresetIndex == InvalidLightingPresetIndex)
             if (!m_lightingPresets.empty() && m_currentLightingPresetIndex == InvalidLightingPresetIndex)
             {
             {
                 m_currentLightingPresetIndex = 0;
                 m_currentLightingPresetIndex = 0;
-                OnLightingPresetSelected(m_lightingPresets[0]);
+                OnLightingPresetSelected(m_lightingPresets[0], m_useAlternateSkybox);
             }
             }
         }
         }
         else
         else
@@ -153,6 +153,7 @@ namespace AtomSampleViewer
     void CommonSampleComponentBase::ClearLightingPresets()
     void CommonSampleComponentBase::ClearLightingPresets()
     {
     {
         m_currentLightingPresetIndex = InvalidLightingPresetIndex;
         m_currentLightingPresetIndex = InvalidLightingPresetIndex;
+        m_useAlternateSkybox = false;
         m_lightingPresets.clear();
         m_lightingPresets.clear();
         m_lightingPresetsDirty = true;
         m_lightingPresetsDirty = true;
     }
     }
@@ -167,19 +168,44 @@ namespace AtomSampleViewer
             AZ_Warning("Lighting Preset", false, "Lighting presets must be loaded before calling ImGui.");
             AZ_Warning("Lighting Preset", false, "Lighting presets must be loaded before calling ImGui.");
             return;
             return;
         }
         }
-
-        AZStd::vector<const char*> items;
-        for (const auto& lightingPreset : m_lightingPresets)
+        
+        AZStd::string selectedPresetLabel = m_lightingPresets[m_currentLightingPresetIndex].m_displayName;
+        if (m_useAlternateSkybox)
         {
         {
-            items.push_back(lightingPreset.m_displayName.c_str());
+            selectedPresetLabel += " (Alt)";
         }
         }
-        if (ScriptableImGui::Combo("Lighting Preset##SampleBase", &m_currentLightingPresetIndex, items.begin(), aznumeric_cast<int>(items.size())))
+
+        // Note that before we were using ScriptableImGui::Combo but there were issues (at least on some systems) when the number of items
+        // exceeded the max visible item count (which defaults to 8 in ImGui). In this case you'd expect to see a scroll bar in the combo box,
+        // but the combo box just never popped up. This only happened in profile, not debug builds. It seems to be a bug in ImGui. So by using
+        // BeginCombo with ImGuiComboFlags_HeightLargest, we just make sure the combo box is always big enough for all the items.
+        if (ScriptableImGui::BeginCombo("Lighting Preset##SampleBase", selectedPresetLabel.c_str(), ImGuiComboFlags_HeightLargest))
         {
         {
-            OnLightingPresetSelected(m_lightingPresets[m_currentLightingPresetIndex]);
+            for (uint32_t i = 0; i < m_lightingPresets.size(); ++i)
+            {
+                AZStd::string& name = m_lightingPresets[i].m_displayName;
+                AZStd::string nameForAlternateSkybox = name + " (Alt)";
+
+                // Each LightingPreset can have an alternate skybox (usually a blurred version of the primary skybox), so we expose both here as separate items in the UI.
+
+                bool useThisPresetWithNormalSkybox = (i == m_currentLightingPresetIndex) && !m_useAlternateSkybox;
+                bool useThisPresetWithAlternateSkybox = (i == m_currentLightingPresetIndex) && m_useAlternateSkybox;
+
+                useThisPresetWithNormalSkybox = ScriptableImGui::Selectable(name.c_str(), useThisPresetWithNormalSkybox);
+                useThisPresetWithAlternateSkybox = ScriptableImGui::Selectable(nameForAlternateSkybox.c_str(), useThisPresetWithAlternateSkybox);
+
+                if (useThisPresetWithNormalSkybox || useThisPresetWithAlternateSkybox)
+                {
+                    m_currentLightingPresetIndex = i;
+                    m_useAlternateSkybox = useThisPresetWithAlternateSkybox;
+                    OnLightingPresetSelected(m_lightingPresets[i], m_useAlternateSkybox);
+                }
+            }
+            ScriptableImGui::EndCombo();
         }
         }
     }
     }
 
 
-    void CommonSampleComponentBase::OnLightingPresetSelected(const AZ::Render::LightingPreset& preset)
+    void CommonSampleComponentBase::OnLightingPresetSelected(const AZ::Render::LightingPreset& preset, bool useAlternateSkybox)
     {
     {
         AZ::Render::SkyBoxFeatureProcessorInterface* skyboxFeatureProcessor = AZ::RPI::Scene::GetFeatureProcessorForEntityContextId<AZ::Render::SkyBoxFeatureProcessorInterface>(m_entityContextId);
         AZ::Render::SkyBoxFeatureProcessorInterface* skyboxFeatureProcessor = AZ::RPI::Scene::GetFeatureProcessorForEntityContextId<AZ::Render::SkyBoxFeatureProcessorInterface>(m_entityContextId);
         AZ::Render::PostProcessFeatureProcessorInterface* postProcessFeatureProcessor = AZ::RPI::Scene::GetFeatureProcessorForEntityContextId<AZ::Render::PostProcessFeatureProcessorInterface>(m_entityContextId);
         AZ::Render::PostProcessFeatureProcessorInterface* postProcessFeatureProcessor = AZ::RPI::Scene::GetFeatureProcessorForEntityContextId<AZ::Render::PostProcessFeatureProcessorInterface>(m_entityContextId);
@@ -197,7 +223,10 @@ namespace AtomSampleViewer
             exposureControlSettingInterface,
             exposureControlSettingInterface,
             directionalLightFeatureProcessor,
             directionalLightFeatureProcessor,
             cameraConfig,
             cameraConfig,
-            m_lightHandles);
+            m_lightHandles,
+            nullptr,
+            AZ::RPI::MaterialPropertyIndex{},
+            useAlternateSkybox);
     }
     }
 
 
     void CommonSampleComponentBase::OnTransformChanged(const AZ::Transform&, const AZ::Transform&)
     void CommonSampleComponentBase::OnTransformChanged(const AZ::Transform&, const AZ::Transform&)

+ 2 - 1
Gem/Code/Source/CommonSampleComponentBase.h

@@ -64,7 +64,7 @@ namespace AtomSampleViewer
 
 
         //! Apply lighting presets to the scene.
         //! Apply lighting presets to the scene.
         //! Derived samples can override this function to have custom behaviors.
         //! Derived samples can override this function to have custom behaviors.
-        virtual void OnLightingPresetSelected(const AZ::Render::LightingPreset& preset);
+        virtual void OnLightingPresetSelected(const AZ::Render::LightingPreset& preset, bool useAltSkybox);
 
 
         //! Return the AtomSampleViewer EntityContextId, retrieved from the ComponentConfig
         //! Return the AtomSampleViewer EntityContextId, retrieved from the ComponentConfig
         AzFramework::EntityContextId GetEntityContextId() const;
         AzFramework::EntityContextId GetEntityContextId() const;
@@ -117,6 +117,7 @@ namespace AtomSampleViewer
         //! Current active lighting preset.
         //! Current active lighting preset.
         constexpr static int32_t InvalidLightingPresetIndex = -1;
         constexpr static int32_t InvalidLightingPresetIndex = -1;
         int32_t m_currentLightingPresetIndex = InvalidLightingPresetIndex;
         int32_t m_currentLightingPresetIndex = InvalidLightingPresetIndex;
+        bool m_useAlternateSkybox = false; //!< LightingPresets have an alternate skybox that can be used, when this is true. This is usually a blurred version of the primary skybox.
     };
     };
 
 
 } // namespace AtomSampleViewer
 } // namespace AtomSampleViewer

+ 10 - 2
Gem/Code/Source/DecalExampleComponent.cpp

@@ -28,8 +28,15 @@
 
 
 #include <RHI/BasicRHIComponent.h>
 #include <RHI/BasicRHIComponent.h>
 
 
+
 namespace AtomSampleViewer
 namespace AtomSampleViewer
 {
 {
+    namespace
+    {
+        static constexpr char* TargetMeshName = "objects/plane.azmodel";
+        static constexpr char* TargetMaterialName = "materials/defaultpbr.azmaterial";
+    }
+
     void DecalExampleComponent::Reflect(AZ::ReflectContext* context)
     void DecalExampleComponent::Reflect(AZ::ReflectContext* context)
     {
     {
         if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
         if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
@@ -72,8 +79,9 @@ namespace AtomSampleViewer
 
 
     void DecalExampleComponent::CreatePlaneObject()
     void DecalExampleComponent::CreatePlaneObject()
     {
     {
-        auto meshAsset = m_assetLoadManager.GetAsset<AZ::RPI::ModelAsset>("objects/plane.azmodel");
-        m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(meshAsset);
+        const auto meshAsset = m_assetLoadManager.GetAsset<AZ::RPI::ModelAsset>(TargetMeshName);
+        const auto materialAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::MaterialAsset>(TargetMaterialName, AZ::RPI::AssetUtils::TraceLevel::Assert);
+        m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(meshAsset, AZ::RPI::Material::FindOrCreate(materialAsset));
         ScaleObjectToFitDecals();
         ScaleObjectToFitDecals();
     }
     }
 
 

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

@@ -124,10 +124,9 @@ namespace AtomSampleViewer
     {
     {
         auto io = AZ::IO::LocalFileIO::GetInstance();
         auto io = AZ::IO::LocalFileIO::GetInstance();
 
 
-        AZStd::string appRoot;
-        AzFramework::ApplicationRequests::Bus::BroadcastResult(appRoot, &AzFramework::ApplicationRequests::GetAppRoot);
+        auto projectPath = AZ::Utils::GetProjectPath();
         AZStd::string mainTestFolder;
         AZStd::string mainTestFolder;
-        AzFramework::StringFunc::Path::Join(appRoot.c_str(), "AtomSampleViewer/Materials/HotReloadTest/", mainTestFolder);
+        AzFramework::StringFunc::Path::Join(projectPath.c_str(), "Materials/HotReloadTest/", mainTestFolder);
         AzFramework::StringFunc::Path::Join(mainTestFolder.c_str(), "TestData/", m_testDataFolder);
         AzFramework::StringFunc::Path::Join(mainTestFolder.c_str(), "TestData/", m_testDataFolder);
         if (!io->Exists(m_testDataFolder.c_str()))
         if (!io->Exists(m_testDataFolder.c_str()))
         {
         {

+ 23 - 24
Gem/Code/Source/MeshExampleComponent.cpp

@@ -127,6 +127,8 @@ namespace AtomSampleViewer
 
 
         GetMeshFeatureProcessor()->ReleaseMesh(m_meshHandle);
         GetMeshFeatureProcessor()->ReleaseMesh(m_meshHandle);
 
 
+        m_modelAsset = {};
+
         m_materialOverrideInstance = nullptr;
         m_materialOverrideInstance = nullptr;
 
 
         ShutdownLightingPresets();
         ShutdownLightingPresets();
@@ -234,10 +236,10 @@ namespace AtomSampleViewer
 
 
     void MeshExampleComponent::ModelChange()
     void MeshExampleComponent::ModelChange()
     {
     {
-        GetMeshFeatureProcessor()->ReleaseMesh(m_meshHandle);
-
         if (!m_modelBrowser.GetSelectedAssetId().IsValid())
         if (!m_modelBrowser.GetSelectedAssetId().IsValid())
         {
         {
+            m_modelAsset = {};
+            GetMeshFeatureProcessor()->ReleaseMesh(m_meshHandle);
             return;
             return;
         }
         }
 
 
@@ -255,30 +257,33 @@ namespace AtomSampleViewer
             }
             }
         }
         }
 
 
-        AZ::Render::MaterialAssignmentMap materialMap;
         if (m_enableMaterialOverride && selectedMaterialAssetId.IsValid())
         if (m_enableMaterialOverride && selectedMaterialAssetId.IsValid())
         {
         {
             AZ::Data::Asset<AZ::RPI::MaterialAsset> materialAsset;
             AZ::Data::Asset<AZ::RPI::MaterialAsset> materialAsset;
             materialAsset.Create(selectedMaterialAssetId);
             materialAsset.Create(selectedMaterialAssetId);
-
             m_materialOverrideInstance = AZ::RPI::Material::FindOrCreate(materialAsset);
             m_materialOverrideInstance = AZ::RPI::Material::FindOrCreate(materialAsset);
-
-            materialMap[AZ::Render::DefaultMaterialAssignmentId].m_materialAsset = materialAsset;
-            materialMap[AZ::Render::DefaultMaterialAssignmentId].m_materialInstance = m_materialOverrideInstance;
         }
         }
         else
         else
         {
         {
             m_materialOverrideInstance = nullptr;
             m_materialOverrideInstance = nullptr;
         }
         }
 
 
-        ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::PauseScript);
 
 
-        AZ::Data::Asset<AZ::RPI::ModelAsset> modelAsset;
-        modelAsset.Create(m_modelBrowser.GetSelectedAssetId());
-        m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(modelAsset, materialMap);
-        GetMeshFeatureProcessor()->SetTransform(m_meshHandle, AZ::Transform::CreateIdentity());
-        GetMeshFeatureProcessor()->ConnectModelChangeEventHandler(m_meshHandle, m_changedHandler);
-        GetMeshFeatureProcessor()->SetLodOverride(m_meshHandle, m_lodOverride);
+        if (m_modelAsset.GetId() != m_modelBrowser.GetSelectedAssetId())
+        {
+            ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::PauseScript);
+
+            m_modelAsset.Create(m_modelBrowser.GetSelectedAssetId());
+            GetMeshFeatureProcessor()->ReleaseMesh(m_meshHandle);
+            m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(m_modelAsset, m_materialOverrideInstance);
+            GetMeshFeatureProcessor()->SetTransform(m_meshHandle, AZ::Transform::CreateIdentity());
+            GetMeshFeatureProcessor()->ConnectModelChangeEventHandler(m_meshHandle, m_changedHandler);
+            GetMeshFeatureProcessor()->SetLodOverride(m_meshHandle, m_lodOverride);
+        }
+        else
+        {
+            GetMeshFeatureProcessor()->SetMaterialAssignmentMap(m_meshHandle, m_materialOverrideInstance);
+        }
     }
     }
 
 
     void MeshExampleComponent::OnEntityDestruction(const AZ::EntityId& entityId)
     void MeshExampleComponent::OnEntityDestruction(const AZ::EntityId& entityId)
@@ -306,24 +311,18 @@ namespace AtomSampleViewer
 
 
     void MeshExampleComponent::SetArcBallControllerParams()
     void MeshExampleComponent::SetArcBallControllerParams()
     {
     {
-        if (!m_modelBrowser.GetSelectedAssetId().IsValid())
+        if (!m_modelBrowser.GetSelectedAssetId().IsValid() || !m_modelAsset.IsReady())
         {
         {
             return;
             return;
         }
         }
 
 
         // Adjust the arc-ball controller so that it has bounds that make sense for the current model
         // Adjust the arc-ball controller so that it has bounds that make sense for the current model
-        AZ::Data::Asset<AZ::RPI::ModelAsset> asset = AZ::Data::AssetManager::Instance().GetAsset(m_modelBrowser.GetSelectedAssetId(), azrtti_typeid<AZ::RPI::ModelAsset>(),
-            AZ::Data::AssetLoadBehavior::PreLoad);
-        asset.BlockUntilLoadComplete();
-
-        AZ::RPI::ModelAsset* modelAsset = asset.Get();
-        const AZ::Aabb& aabb = modelAsset->GetAabb();
-
+        
         AZ::Vector3 center;
         AZ::Vector3 center;
         float radius;
         float radius;
-        aabb.GetAsSphere(center, radius);
+        m_modelAsset->GetAabb().GetAsSphere(center, radius);
 
 
-        const float startingDistance = radius;
+        const float startingDistance = radius * ArcballRadiusDefaultModifier;
         const float minDistance = radius * ArcballRadiusMinModifier;
         const float minDistance = radius * ArcballRadiusMinModifier;
         const float maxDistance = radius * ArcballRadiusMaxModifier;
         const float maxDistance = radius * ArcballRadiusMaxModifier;
 
 

+ 3 - 1
Gem/Code/Source/MeshExampleComponent.h

@@ -73,9 +73,10 @@ namespace AtomSampleViewer
         AZ::Component* m_cameraControlComponent = nullptr;
         AZ::Component* m_cameraControlComponent = nullptr;
 
 
         AZ::Render::MeshFeatureProcessorInterface::ModelChangedEvent::Handler m_changedHandler;
         AZ::Render::MeshFeatureProcessorInterface::ModelChangedEvent::Handler m_changedHandler;
-
+        
         static constexpr float ArcballRadiusMinModifier = 0.01f;
         static constexpr float ArcballRadiusMinModifier = 0.01f;
         static constexpr float ArcballRadiusMaxModifier = 4.0f;
         static constexpr float ArcballRadiusMaxModifier = 4.0f;
+        static constexpr float ArcballRadiusDefaultModifier = 2.0f;
         
         
         AZ::RPI::Cullable::LodOverride m_lodOverride = AZ::RPI::Cullable::NoLodOverride;
         AZ::RPI::Cullable::LodOverride m_lodOverride = AZ::RPI::Cullable::NoLodOverride;
 
 
@@ -90,6 +91,7 @@ namespace AtomSampleViewer
 
 
         AZ::Data::Instance<AZ::RPI::Material> m_materialOverrideInstance; //< Holds a copy of the material instance being used when m_enableMaterialOverride is true.
         AZ::Data::Instance<AZ::RPI::Material> m_materialOverrideInstance; //< Holds a copy of the material instance being used when m_enableMaterialOverride is true.
         AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_meshHandle;
         AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_meshHandle;
+        AZ::Data::Asset<AZ::RPI::ModelAsset> m_modelAsset;
 
 
         ImGuiSidebar m_imguiSidebar;
         ImGuiSidebar m_imguiSidebar;
         ImGuiMaterialDetails m_imguiMaterialDetails;
         ImGuiMaterialDetails m_imguiMaterialDetails;

+ 8 - 13
Gem/Code/Source/MultiSceneExampleComponent.cpp

@@ -93,19 +93,14 @@ namespace AtomSampleViewer
             {
             {
                 return;
                 return;
             }
             }
-            bool needCompile = false;
-            RHI::ShaderInputConstantIndex timeIndex = srg->FindShaderInputConstantIndex(Name{ "m_time" });
-            if (timeIndex.IsValid())
-            {
-                srg->SetConstant(timeIndex, aznumeric_cast<float>(m_simulateTime));
-                needCompile = true;
-            }
-            RHI::ShaderInputConstantIndex deltaTimeIndex = srg->FindShaderInputConstantIndex(Name{ "m_deltaTime" });
-            if (deltaTimeIndex.IsValid())
-            {
-                srg->SetConstant(deltaTimeIndex, m_deltaTime);
-                needCompile = true;
-            }
+
+            RHI::ShaderInputNameIndex timeIndex = "m_time";
+            RHI::ShaderInputNameIndex deltaTimeIndex = "m_deltaTime";
+
+            srg->SetConstant(timeIndex, aznumeric_cast<float>(m_simulateTime));
+            srg->SetConstant(deltaTimeIndex, m_deltaTime);
+
+            bool needCompile = timeIndex.IsValid() || deltaTimeIndex.IsValid();
 
 
             if (needCompile)
             if (needCompile)
             {
             {

+ 273 - 0
Gem/Code/Source/Passes/RayTracingAmbientOcclusionPass.cpp

@@ -0,0 +1,273 @@
+/*
+* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
+* its licensors.
+*
+* For complete copyright and license terms please see the LICENSE at the root of this
+* distribution (the "License"). All use of this software is governed by the License,
+* or, if provided, by the license below or the license accompanying this file. Do not
+* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*
+*/
+
+#include <Passes/RayTracingAmbientOcclusionPass.h>
+#include <Atom/RHI/CommandList.h>
+#include <Atom/RHI/DispatchRaysItem.h>
+#include <Atom/RHI/Factory.h>
+#include <Atom/RHI/FrameScheduler.h>
+#include <Atom/RHI/RHISystemInterface.h>
+#include <Atom/RHI/ScopeProducerFunction.h>
+#include <Atom/RPI.Public/Buffer/BufferSystemInterface.h>
+#include <Atom/RPI.Public/Buffer/Buffer.h>
+#include <Atom/RPI.Public/RenderPipeline.h>
+#include <Atom/RPI.Public/Scene.h>
+#include <Atom/RPI.Public/Pass/PassUtils.h>
+#include <Atom/RPI.Public/RPIUtils.h>
+#include <Atom/RPI.Public/View.h>
+#include <Atom/Feature/TransformService/TransformServiceFeatureProcessor.h>
+
+namespace AZ
+{
+    namespace Render
+    {
+        RPI::Ptr<RayTracingAmbientOcclusionPass> RayTracingAmbientOcclusionPass::Create(const RPI::PassDescriptor& descriptor)
+        {
+            RPI::Ptr<RayTracingAmbientOcclusionPass> pass = aznew RayTracingAmbientOcclusionPass(descriptor);
+            return AZStd::move(pass);
+        }
+
+        RayTracingAmbientOcclusionPass::RayTracingAmbientOcclusionPass(const RPI::PassDescriptor& descriptor)
+            : RPI::RenderPass(descriptor)
+        {
+            RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
+            if (device->GetFeatures().m_rayTracing == false)
+            {
+                // ray tracing is not supported on this platform
+                SetEnabled(false);
+            }
+        }
+
+        RayTracingAmbientOcclusionPass::~RayTracingAmbientOcclusionPass()
+        {
+        }
+
+        void RayTracingAmbientOcclusionPass::CreateRayTracingPipelineState()
+        {
+            RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
+
+            // load ray generation shader
+            const char* rayGenerationShaderFilePath = "Shaders/RayTracing/RTAOGeneration.azshader";
+            m_rayGenerationShader = RPI::LoadShader(rayGenerationShaderFilePath);
+            AZ_Assert(m_rayGenerationShader, "Failed to load ray generation shader");
+
+            auto rayGenerationShaderVariant = m_rayGenerationShader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
+            RHI::PipelineStateDescriptorForRayTracing rayGenerationShaderDescriptor;
+            rayGenerationShaderVariant.ConfigurePipelineState(rayGenerationShaderDescriptor);
+
+            // load miss shader
+            const char* missShaderFilePath = "Shaders/RayTracing/RTAOMiss.azshader";
+            m_missShader = RPI::LoadShader(missShaderFilePath);
+            AZ_Assert(m_missShader, "Failed to load miss shader");
+
+            auto missShaderVariant = m_missShader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
+            RHI::PipelineStateDescriptorForRayTracing missShaderDescriptor;
+            missShaderVariant.ConfigurePipelineState(missShaderDescriptor);
+                       
+            // Load closest hit shader
+            // This can be removed when the following issue is fixed.
+            // [ATOM-15087] RayTracingShaderTable shouldn't report an error if there is no hit group in the descriptor
+            const char* hitShaderFilePath = "Shaders/RayTracing/RTAOClosestHit.azshader";
+            m_hitShader = RPI::LoadShader(hitShaderFilePath);
+            AZ_Assert(m_hitShader, "Failed to load closest hit shader");
+
+            auto hitShaderVariant = m_hitShader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
+            RHI::PipelineStateDescriptorForRayTracing hitShaderDescriptor;
+            hitShaderVariant.ConfigurePipelineState(hitShaderDescriptor);
+
+            // global pipeline state
+            m_globalPipelineState = m_rayGenerationShader->AcquirePipelineState(rayGenerationShaderDescriptor);
+            AZ_Assert(m_globalPipelineState, "Failed to acquire ray tracing global pipeline state");
+
+            //Get pass srg
+            auto srgAsset = m_rayGenerationShader->FindShaderResourceGroupAsset(Name{"RayTracingGlobalSrg"});
+            AZ_Error("RayTracingAmbientOcclusionPass", srgAsset.GetId().IsValid(), "Failed to find PassSrg asset for shader [%s]", rayGenerationShaderFilePath);
+            AZ_Error("RayTracingAmbientOcclusionPass", srgAsset.IsReady(), "RayTracingGlobalSrg asset is not loaded for shader [%s]", rayGenerationShaderFilePath);
+
+            m_shaderResourceGroup = RPI::ShaderResourceGroup::Create(srgAsset);
+            AZ_Assert(m_shaderResourceGroup, "[RayTracingAmbientOcclusionPass '%s']: Failed to create SRG from shader asset '%s'",
+                GetPathName().GetCStr(), rayGenerationShaderFilePath);
+                        
+            RHI::RayTracingPipelineStateDescriptor descriptor;
+            descriptor.Build()
+                ->PipelineState(m_globalPipelineState.get())
+                ->ShaderLibrary(rayGenerationShaderDescriptor)
+                ->RayGenerationShaderName(AZ::Name("AoRayGen"))
+                ->ShaderLibrary(missShaderDescriptor)
+                ->MissShaderName(AZ::Name("AoMiss"))
+                ->ShaderLibrary(hitShaderDescriptor)
+                ->ClosestHitShaderName(AZ::Name("AoClosestHit"))
+                ->HitGroup(AZ::Name("ClosestHitGroup"))
+                ->ClosestHitShaderName(AZ::Name("AoClosestHit"))
+                ;
+
+            // create the ray tracing pipeline state object
+            m_rayTracingPipelineState = RHI::Factory::Get().CreateRayTracingPipelineState();
+            m_rayTracingPipelineState->Init(*device.get(), &descriptor);
+        }
+
+        void RayTracingAmbientOcclusionPass::FrameBeginInternal(FramePrepareParams params)
+        {
+            if (!m_flags.m_initialized)
+            {
+                RPI::Scene* scene = m_pipeline->GetScene();
+                m_rayTracingFeatureProcessor = scene->GetFeatureProcessor<RayTracingFeatureProcessor>();
+
+                CreateRayTracingPipelineState();
+                m_flags.m_initialized = true;
+            }
+
+            if (!m_rayTracingShaderTable)
+            {
+                // Build shader table once. Since we are not using local srg so we don't need to rebuild it even when scene changed 
+                m_rayTracingShaderTable = RHI::Factory::Get().CreateRayTracingShaderTable();
+                RHI::RayTracingShaderTableDescriptor descriptor;
+                descriptor.Build(AZ::Name("RayTracingAOShaderTable"), m_rayTracingPipelineState)
+                    ->RayGenerationRecord(AZ::Name("AoRayGen"))
+                    ->MissRecord(AZ::Name("AoMiss"))
+                    ->HitGroupRecord(AZ::Name("ClosestHitGroup"))
+                    ;
+
+                RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
+                RHI::RayTracingBufferPools& rayTracingBufferPools = m_rayTracingFeatureProcessor->GetBufferPools();
+
+                m_rayTracingShaderTable->Init(*device.get(), &descriptor, rayTracingBufferPools);
+            }
+
+            RenderPass::FrameBeginInternal(params);
+        }        
+
+        void RayTracingAmbientOcclusionPass::SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph)
+        {
+            RenderPass::SetupFrameGraphDependencies(frameGraph);
+            frameGraph.SetEstimatedItemCount(1);
+        }
+
+        void RayTracingAmbientOcclusionPass::CompileResources(const RHI::FrameGraphCompileContext& context)
+        {
+            if (!m_shaderResourceGroup)
+            {
+                return;
+            }
+
+            // Bind pass attachments to global srg 
+            BindPassSrg(context, m_shaderResourceGroup);
+
+            // Bind others for global srg
+            const RHI::ShaderResourceGroupLayout* srgLayout = m_shaderResourceGroup->GetLayout();
+            RHI::ShaderInputImageIndex imageIndex;
+            RHI::ShaderInputBufferIndex bufferIndex;
+            RHI::ShaderInputConstantIndex constantIndex;
+
+            // Bind scene TLAS buffer
+            const RHI::Ptr<RHI::Buffer> tlasBuffer = m_rayTracingFeatureProcessor->GetTlas()->GetTlasBuffer();
+            if (tlasBuffer)
+            {
+                // TLAS
+                uint32_t tlasBufferByteCount = aznumeric_cast<uint32_t>(tlasBuffer->GetDescriptor().m_byteCount);
+                RHI::BufferViewDescriptor bufferViewDescriptor = RHI::BufferViewDescriptor::CreateRayTracingTLAS(tlasBufferByteCount);
+
+                bufferIndex = srgLayout->FindShaderInputBufferIndex(AZ::Name("m_scene"));
+                m_shaderResourceGroup->SetBufferView(bufferIndex, tlasBuffer->GetBufferView(bufferViewDescriptor).get());
+            }
+
+            // Bind constants
+            // float m_aoRadius
+            constantIndex = srgLayout->FindShaderInputConstantIndex(AZ::Name("m_aoRadius"));
+            m_shaderResourceGroup->SetConstant(constantIndex, m_rayMaxT);
+
+            // uint m_frameCount
+            constantIndex = srgLayout->FindShaderInputConstantIndex(AZ::Name("m_frameCount"));
+            m_shaderResourceGroup->SetConstant(constantIndex, m_frameCount++);
+
+            // float m_rayMinT
+            constantIndex = srgLayout->FindShaderInputConstantIndex(AZ::Name("m_rayMinT"));
+            m_shaderResourceGroup->SetConstant(constantIndex, m_rayMinT);
+
+            // uint m_numRays
+            constantIndex = srgLayout->FindShaderInputConstantIndex(AZ::Name("m_numRays"));
+            m_shaderResourceGroup->SetConstant(constantIndex, m_rayNumber);
+
+            // Matrix4x4 m_viewProjectionInverseMatrix. This is the copy of same constant from ViewSrg.
+            // Although we don't have access to ViewSrg in ray tracing shader at this moment
+            constantIndex = srgLayout->FindShaderInputConstantIndex(AZ::Name("m_viewProjectionInverseMatrix"));
+            const AZStd::vector<RPI::ViewPtr>& views = m_pipeline->GetViews(RPI::PipelineViewTag{"MainCamera"});
+            Matrix4x4 clipToWorld = views[0]->GetWorldToClipMatrix();
+            clipToWorld.InvertFull();
+            m_shaderResourceGroup->SetConstant(constantIndex, clipToWorld);
+
+            m_shaderResourceGroup->Compile();
+        }
+    
+        void RayTracingAmbientOcclusionPass::BuildCommandListInternal([[maybe_unused]] const RHI::FrameGraphExecuteContext& context)
+        {
+            RPI::Scene* scene = m_pipeline->GetScene();
+            RayTracingFeatureProcessor* rayTracingFeatureProcessor = scene->GetFeatureProcessor<RayTracingFeatureProcessor>();
+            AZ_Assert(rayTracingFeatureProcessor, "RayTracingAmbientOcclusionPass requires the RayTracingFeatureProcessor");
+
+            if (!rayTracingFeatureProcessor->GetSubMeshCount())
+            {
+                return;
+            }
+
+            if (!m_rayTracingShaderTable)
+            {
+                return;
+            }
+
+            RPI::PassAttachment* outputAttachment = GetOutputBinding(0).m_attachment.get();
+            RHI::Size targetImageSize = outputAttachment->m_descriptor.m_image.m_size;
+
+            RHI::DispatchRaysItem dispatchRaysItem;
+            dispatchRaysItem.m_width = targetImageSize.m_width;
+            dispatchRaysItem.m_height = targetImageSize.m_height;
+            dispatchRaysItem.m_depth = 1;
+            dispatchRaysItem.m_rayTracingPipelineState = m_rayTracingPipelineState.get();
+            dispatchRaysItem.m_rayTracingShaderTable = m_rayTracingShaderTable.get();
+            dispatchRaysItem.m_globalSrg = m_shaderResourceGroup->GetRHIShaderResourceGroup();
+            dispatchRaysItem.m_globalPipelineState = m_globalPipelineState.get();
+
+            // submit the DispatchRays item
+            context.GetCommandList()->Submit(dispatchRaysItem);
+        }
+
+        uint32_t RayTracingAmbientOcclusionPass::GetRayNumberPerPixel()
+        {
+            return m_rayNumber;
+        }
+
+        void RayTracingAmbientOcclusionPass::SetRayNumberPerPixel(uint32_t rayNumber)
+        {
+            m_rayNumber = rayNumber;
+        }
+
+        float RayTracingAmbientOcclusionPass::GetRayExtentMin()
+        {
+            return m_rayMinT;
+        }
+
+        void RayTracingAmbientOcclusionPass::SetRayExtentMin(float minT)
+        {
+            m_rayMinT = minT;
+        }
+
+        float RayTracingAmbientOcclusionPass::GetRayExtentMax()
+        {
+            return m_rayMaxT;
+        }
+
+        void RayTracingAmbientOcclusionPass::SetRayExtentMax(float maxT)
+        {
+            m_rayMaxT = maxT;
+        }
+    }   // namespace RPI
+}   // namespace AZ

+ 88 - 0
Gem/Code/Source/Passes/RayTracingAmbientOcclusionPass.h

@@ -0,0 +1,88 @@
+/*
+* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
+* its licensors.
+*
+* For complete copyright and license terms please see the LICENSE at the root of this
+* distribution (the "License"). All use of this software is governed by the License,
+* or, if provided, by the license below or the license accompanying this file. Do not
+* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*
+*/
+#pragma once
+
+#include <Atom/RHI/ScopeProducer.h>
+#include <Atom/RPI.Public/Pass/RenderPass.h>
+#include <Atom/RPI.Public/Buffer/Buffer.h>
+#include <Atom/RHI/RayTracingBufferPools.h>
+#include <Atom/RHI/RayTracingPipelineState.h>
+#include <Atom/RHI/RayTracingShaderTable.h>
+#include <Atom/RPI.Public/RPIUtils.h>
+#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
+
+#include <RayTracing/RayTracingFeatureProcessor.h>
+
+namespace AZ
+{
+    namespace Render
+    {
+        //! A pass to generate rays g-buffer for ambient occlusion.
+        class RayTracingAmbientOcclusionPass final
+            : public RPI::RenderPass
+        {
+        public:
+            AZ_RPI_PASS(RayTracingAmbientOcclusionPass);
+
+            AZ_RTTI(RayTracingAmbientOcclusionPass, "{4E8F814F-F7C4-4788-B793-D7F118992819}", RPI::RenderPass);
+            AZ_CLASS_ALLOCATOR(RayTracingAmbientOcclusionPass, SystemAllocator, 0);
+
+            virtual ~RayTracingAmbientOcclusionPass() override;
+
+            //! Creates a RayTracingAmbientOcclusionPass
+            static RPI::Ptr<RayTracingAmbientOcclusionPass> Create(const RPI::PassDescriptor& descriptor);
+
+            //! Get/Set some raytracing ao settings
+            uint32_t GetRayNumberPerPixel();
+            void SetRayNumberPerPixel(uint32_t rayNumber);
+            float GetRayExtentMin();
+            void SetRayExtentMin(float minT);
+            float GetRayExtentMax();
+            void SetRayExtentMax(float maxT);
+
+        private:
+            explicit RayTracingAmbientOcclusionPass(const RPI::PassDescriptor& descriptor);
+
+            void CreateRayTracingPipelineState();
+
+            // Scope producer functions
+            void SetupFrameGraphDependencies(RHI::FrameGraphInterface frameGraph) override;
+            void CompileResources(const RHI::FrameGraphCompileContext& context) override;
+            void BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) override;
+
+            // Pass overrides
+            void FrameBeginInternal(FramePrepareParams params) override;
+
+            // ray tracing shader and pipeline state
+            Data::Instance<RPI::Shader> m_rayGenerationShader;
+            Data::Instance<RPI::Shader> m_missShader;
+            Data::Instance<RPI::Shader> m_hitShader;
+            RHI::Ptr<RHI::RayTracingPipelineState> m_rayTracingPipelineState;
+
+            // ray tracing shader table
+            RHI::Ptr<RHI::RayTracingShaderTable> m_rayTracingShaderTable;
+
+            // ray tracing global shader resource group asset and pipeline state
+            Data::Asset<RPI::ShaderResourceGroupAsset> m_globalSrgAsset;
+            RHI::ConstPtr<RHI::PipelineState> m_globalPipelineState;
+
+            Render::RayTracingFeatureProcessor* m_rayTracingFeatureProcessor = nullptr;
+
+            uint32_t m_frameCount = 0;
+
+            // ray tracing ambient occlusion parameters
+            float m_rayMaxT = 0.4f;          // The ray's far distance
+            float m_rayMinT = 0.01f;        // The ray's near distance
+            uint32_t m_rayNumber = 8;      // Ray casted per pixel
+        };
+    }   // namespace RPI
+}   // namespace AZ

+ 21 - 14
Gem/Code/Source/RHI/AlphaToCoverageExampleComponent.cpp

@@ -121,10 +121,17 @@ namespace AtomSampleViewer
             const auto worldMatrix1 = translate * rotate1 * scale;
             const auto worldMatrix1 = translate * rotate1 * scale;
             auto& srg0 = m_shaderResourceGroups[blendIndex][0];
             auto& srg0 = m_shaderResourceGroups[blendIndex][0];
             auto& srg1 = m_shaderResourceGroups[blendIndex][1];
             auto& srg1 = m_shaderResourceGroups[blendIndex][1];
-            srg0->SetConstant(m_worldMatrixIndex, worldMatrix0);
-            srg1->SetConstant(m_worldMatrixIndex, worldMatrix1);
-            srg0->Compile();
-            srg1->Compile();
+            if(srg0)
+            {
+                srg0->SetConstant(m_worldMatrixIndex, worldMatrix0);
+                srg0->Compile();
+            }
+
+            if (srg1)
+            {
+                srg1->SetConstant(m_worldMatrixIndex, worldMatrix1);
+                srg1->Compile();
+            }
         }
         }
 
 
         BasicRHIComponent::OnFramePrepare(frameGraphBuilder);
         BasicRHIComponent::OnFramePrepare(frameGraphBuilder);
@@ -186,15 +193,10 @@ namespace AtomSampleViewer
                 m_shaderResourceGroups[blendIndex][rectIndex] = CreateShaderResourceGroup(m_shader, "TexturedSurfaceSrg", AlphaToCoverage::SampleName);
                 m_shaderResourceGroups[blendIndex][rectIndex] = CreateShaderResourceGroup(m_shader, "TexturedSurfaceSrg", AlphaToCoverage::SampleName);
             }
             }
         }
         }
-        const Name worldMatrixId{"m_worldMatrix"};
-        const Name viewProjectionMatrixId{"m_viewProjectionMatrix"};
-        const Name alphaRefValueId{ "m_alphaTestRefValue" };
-        AZ::RHI::ShaderInputConstantIndex viewProjectionMatrixIndex;
-        AZ::RHI::ShaderInputConstantIndex alphaTestRefValueIndex;
 
 
-        FindShaderInputIndex(&m_worldMatrixIndex, m_shaderResourceGroups[0][0], worldMatrixId, AlphaToCoverage::SampleName);
-        FindShaderInputIndex(&viewProjectionMatrixIndex, m_shaderResourceGroups[0][0], viewProjectionMatrixId, AlphaToCoverage::SampleName);
-        FindShaderInputIndex(&alphaTestRefValueIndex, m_shaderResourceGroups[0][0], alphaRefValueId, AlphaToCoverage::SampleName);
+        m_worldMatrixIndex.Reset();
+        m_viewProjectionMatrixIndex.Reset();
+        m_alphaTestRefValueIndex.Reset();
 
 
         const AZ::Vector3 upVector{0.f, 1.f, 0.f};
         const AZ::Vector3 upVector{0.f, 1.f, 0.f};
         const AZ::Vector3 lookAtPos{0.f, 0.f, 0.f};
         const AZ::Vector3 lookAtPos{0.f, 0.f, 0.f};
@@ -227,12 +229,12 @@ namespace AtomSampleViewer
                     AZ_Error(AlphaToCoverage::SampleName, false, "Failed to set image into shader resource group.");
                     AZ_Error(AlphaToCoverage::SampleName, false, "Failed to set image into shader resource group.");
                     return;
                     return;
                 }
                 }
-                if (!m_shaderResourceGroups[blendIndex][rectIndex]->SetConstant(viewProjectionMatrixIndex, viewProjectionMatrix))
+                if (!m_shaderResourceGroups[blendIndex][rectIndex]->SetConstant(m_viewProjectionMatrixIndex, viewProjectionMatrix))
                 {
                 {
                     AZ_Error(AlphaToCoverage::SampleName, false, "Failed to set view projection matrix into shader resource group.");
                     AZ_Error(AlphaToCoverage::SampleName, false, "Failed to set view projection matrix into shader resource group.");
                     return;
                     return;
                 }
                 }
-                if (!m_shaderResourceGroups[blendIndex][rectIndex]->SetConstant(alphaTestRefValueIndex, alphaRefValue))
+                if (!m_shaderResourceGroups[blendIndex][rectIndex]->SetConstant(m_alphaTestRefValueIndex, alphaRefValue))
                 {
                 {
                     AZ_Error(AlphaToCoverage::SampleName, false, "Failed to set alpha test reference value into shader resource group.");
                     AZ_Error(AlphaToCoverage::SampleName, false, "Failed to set alpha test reference value into shader resource group.");
                     return;
                     return;
@@ -244,6 +246,11 @@ namespace AtomSampleViewer
      void AlphaToCoverageExampleComponent::CreateScopeResources(BlendType type)
      void AlphaToCoverageExampleComponent::CreateScopeResources(BlendType type)
     {
     {
         using namespace AZ;
         using namespace AZ;
+
+        if(!m_shader)
+        {
+            return;
+        }
         const bool useDepth = (type == BlendType::AlphaTest) || (type == BlendType::A2C_MSAA);
         const bool useDepth = (type == BlendType::AlphaTest) || (type == BlendType::A2C_MSAA);
         const bool useResolve = (type == BlendType::A2C_MSAA);
         const bool useResolve = (type == BlendType::A2C_MSAA);
 
 

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

@@ -101,7 +101,9 @@ namespace AtomSampleViewer
         AZ::RHI::InputStreamLayout m_rectangleInputStreamLayout;
         AZ::RHI::InputStreamLayout m_rectangleInputStreamLayout;
 
 
         // Shader Resource
         // Shader Resource
-        AZ::RHI::ShaderInputConstantIndex m_worldMatrixIndex;
+        AZ::RHI::ShaderInputNameIndex m_worldMatrixIndex = "m_worldMatrix";
+        AZ::RHI::ShaderInputNameIndex m_viewProjectionMatrixIndex = "m_viewProjectionMatrix";
+        AZ::RHI::ShaderInputNameIndex m_alphaTestRefValueIndex = "m_alphaTestRefValue";
         AZ::RHI::AttachmentId m_depthImageAttachmentId, m_multisamleDepthImageAttachmentId;
         AZ::RHI::AttachmentId m_depthImageAttachmentId, m_multisamleDepthImageAttachmentId;
         AZ::RHI::AttachmentId m_resolveImageAttachmentId;
         AZ::RHI::AttachmentId m_resolveImageAttachmentId;
         AZStd::array<AZStd::array<AZ::Data::Instance<AZ::RPI::ShaderResourceGroup>, s_numRectangles>, s_numBlendTypes> m_shaderResourceGroups;
         AZStd::array<AZStd::array<AZ::Data::Instance<AZ::RPI::ShaderResourceGroup>, s_numRectangles>, s_numBlendTypes> m_shaderResourceGroups;

+ 10 - 21
Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.cpp

@@ -272,16 +272,7 @@ namespace AtomSampleViewer
             SubMeshInstance& subMeshInstance = m_subMeshInstanceArray.back();
             SubMeshInstance& subMeshInstance = m_subMeshInstanceArray.back();
 
 
             subMeshInstance.m_perSubMeshSrg = CreateShaderResourceGroup(m_shader, "HandleSrg", InternalBP::SampleName);
             subMeshInstance.m_perSubMeshSrg = CreateShaderResourceGroup(m_shader, "HandleSrg", InternalBP::SampleName);
-
-            subMeshInstance.m_viewHandleIndex = subMeshInstance.m_perSubMeshSrg->FindShaderInputConstantIndex(PerViewHandleId);
-            subMeshInstance.m_objecHandleIndex = subMeshInstance.m_perSubMeshSrg->FindShaderInputConstantIndex(PerObjectHandleId);
-            subMeshInstance.m_materialHandleIndex = subMeshInstance.m_perSubMeshSrg->FindShaderInputConstantIndex(MaterialHandleId);
-            subMeshInstance.m_lightHandleIndex = subMeshInstance.m_perSubMeshSrg->FindShaderInputConstantIndex(LightHandleId);
             subMeshInstance.m_mesh = &mesh;
             subMeshInstance.m_mesh = &mesh;
-            AZ_Assert(subMeshInstance.m_viewHandleIndex.IsValid(), "Invalid view index.");
-            AZ_Assert(subMeshInstance.m_objecHandleIndex.IsValid(), "Invalid object index.");
-            AZ_Assert(subMeshInstance.m_materialHandleIndex.IsValid(), "Invalid material index.");
-            AZ_Assert(subMeshInstance.m_lightHandleIndex.IsValid(), "Invalid light index.");
 
 
             // Set the buffer stream
             // Set the buffer stream
             RHI::InputStreamLayout layout;
             RHI::InputStreamLayout layout;
@@ -545,7 +536,7 @@ namespace AtomSampleViewer
                     for (uint32_t subMeshIdx = objectInterval.m_min; subMeshIdx < objectInterval.m_max; subMeshIdx++)
                     for (uint32_t subMeshIdx = objectInterval.m_min; subMeshIdx < objectInterval.m_max; subMeshIdx++)
                     {
                     {
                         // Update the constant data
                         // Update the constant data
-                        const SubMeshInstance& subMesh = m_subMeshInstanceArray[subMeshIdx];
+                        SubMeshInstance& subMesh = m_subMeshInstanceArray[subMeshIdx];
                         // Set the view handle
                         // Set the view handle
                         bool set = subMesh.m_perSubMeshSrg->SetConstant(subMesh.m_viewHandleIndex, m_worldToClipHandle);
                         bool set = subMesh.m_perSubMeshSrg->SetConstant(subMesh.m_viewHandleIndex, m_worldToClipHandle);
                         AZ_Assert(set, "Failed to set the view constant");
                         AZ_Assert(set, "Failed to set the view constant");
@@ -688,12 +679,10 @@ namespace AtomSampleViewer
         // Update the worldToClipMatrix 
         // Update the worldToClipMatrix 
         Matrix4x4 worldToClipMatrix = m_viewToClipMatrix * worldToViewMatrix;
         Matrix4x4 worldToClipMatrix = m_viewToClipMatrix * worldToViewMatrix;
         bool set = m_floatBuffer->AllocateOrUpdateBuffer(m_worldToClipHandle, static_cast<void*>(&worldToClipMatrix), static_cast<uint32_t>(sizeof(Matrix4x4)));
         bool set = m_floatBuffer->AllocateOrUpdateBuffer(m_worldToClipHandle, static_cast<void*>(&worldToClipMatrix), static_cast<uint32_t>(sizeof(Matrix4x4)));
-        AZ_Assert(set, "Failed to set the perspective matrix constant");
-
+        
         // Update the light direction
         // Update the light direction
         set = m_floatBuffer->AllocateOrUpdateBuffer(m_lightDirectionHandle, static_cast<void*>(&m_lightDir), static_cast<uint32_t>(sizeof(Vector3)));
         set = m_floatBuffer->AllocateOrUpdateBuffer(m_lightDirectionHandle, static_cast<void*>(&m_lightDir), static_cast<uint32_t>(sizeof(Vector3)));
-        AZ_Assert(set, "Failed to set the perspective matrix constant");
-
+       
         BasicRHIComponent::OnFramePrepare(frameGraphBuilder);
         BasicRHIComponent::OnFramePrepare(frameGraphBuilder);
     }
     }
 
 
@@ -816,15 +805,15 @@ namespace AtomSampleViewer
     {
     {
         RHI::BufferMapResponse response;
         RHI::BufferMapResponse response;
         RHI::ResultCode result = m_bufferPool->MapBuffer(mapRequest, response);
         RHI::ResultCode result = m_bufferPool->MapBuffer(mapRequest, response);
-        if (result != RHI::ResultCode::Success || !response.m_data)
+        // ResultCode::Unimplemented is used by Null Renderer and hence is a valid use case
+        AZ_Assert(result == RHI::ResultCode::Success || result == RHI::ResultCode::Unimplemented, "Failed to map object buffer]");
+        if (response.m_data)
         {
         {
-            AZ_Assert(response.m_data, "Failed to map object buffer");
-            return false;
+            memcpy(response.m_data, data, mapRequest.m_byteCount);
+            m_bufferPool->UnmapBuffer(*m_buffer);
+            return true;
         }
         }
 
 
-        memcpy(response.m_data, data, mapRequest.m_byteCount);
-        m_bufferPool->UnmapBuffer(*m_buffer);
-
-        return true;
+        return false;
     }
     }
 }; // namespace AtomSampleViewer
 }; // namespace AtomSampleViewer

+ 4 - 4
Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.h

@@ -125,10 +125,10 @@ namespace AtomSampleViewer
         // Simple intermediate structure that represents a submesh instance
         // Simple intermediate structure that represents a submesh instance
         struct SubMeshInstance
         struct SubMeshInstance
         {
         {
-            AZ::RHI::ShaderInputConstantIndex m_viewHandleIndex;
-            AZ::RHI::ShaderInputConstantIndex m_objecHandleIndex;
-            AZ::RHI::ShaderInputConstantIndex m_materialHandleIndex;
-            AZ::RHI::ShaderInputConstantIndex m_lightHandleIndex;
+            AZ::RHI::ShaderInputNameIndex m_viewHandleIndex = "m_perViewHandle";
+            AZ::RHI::ShaderInputNameIndex m_objecHandleIndex = "m_perObjectHandle";
+            AZ::RHI::ShaderInputNameIndex m_materialHandleIndex = "m_materialHandle";
+            AZ::RHI::ShaderInputNameIndex m_lightHandleIndex = "m_lightHandle";
 
 
             AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> m_perSubMeshSrg;
             AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> m_perSubMeshSrg;
 
 

+ 8 - 16
Gem/Code/Source/RHI/InputAssemblyExampleComponent.cpp

@@ -370,30 +370,22 @@ namespace AtomSampleViewer
             AZ_UNUSED(scopeData);
             AZ_UNUSED(scopeData);
             {
             {
                 const RHI::BufferView* inputAssemblyBufferView = context.GetBufferView(RHI::AttachmentId{ InputAssembly::InputAssemblyBufferAttachmentId });
                 const RHI::BufferView* inputAssemblyBufferView = context.GetBufferView(RHI::AttachmentId{ InputAssembly::InputAssemblyBufferAttachmentId });
-
-                m_streamBufferView[0] =
+                if (inputAssemblyBufferView)
                 {
                 {
-                    inputAssemblyBufferView->GetBuffer(),
-                    0,
-                    sizeof(BufferData),
-                    sizeof(BufferData::value_type)
-                };
+                    m_streamBufferView[0] = {inputAssemblyBufferView->GetBuffer(), 0, sizeof(BufferData), sizeof(BufferData::value_type)};
 
 
-                RHI::ValidateStreamBufferViews(m_inputStreamLayout, AZStd::array_view<RHI::StreamBufferView>(&m_streamBufferView[0], 1));
+                    RHI::ValidateStreamBufferViews(m_inputStreamLayout, AZStd::array_view<RHI::StreamBufferView>(&m_streamBufferView[0], 1));
+                }
             }
             }
 
 
             {
             {
                 const RHI::BufferView* inputAssemblyBufferView = context.GetBufferView(RHI::AttachmentId{ InputAssembly::ImportedInputAssemblyBufferAttachmentId });
                 const RHI::BufferView* inputAssemblyBufferView = context.GetBufferView(RHI::AttachmentId{ InputAssembly::ImportedInputAssemblyBufferAttachmentId });
-
-                m_streamBufferView[1] =
+                if (inputAssemblyBufferView)
                 {
                 {
-                    inputAssemblyBufferView->GetBuffer(),
-                    0,
-                    sizeof(BufferData),
-                    sizeof(BufferData::value_type)
-                };
+                    m_streamBufferView[1] = {inputAssemblyBufferView->GetBuffer(), 0, sizeof(BufferData), sizeof(BufferData::value_type)};
 
 
-                RHI::ValidateStreamBufferViews(m_inputStreamLayout, AZStd::array_view<RHI::StreamBufferView>(&m_streamBufferView[1], 1));
+                    RHI::ValidateStreamBufferViews(m_inputStreamLayout, AZStd::array_view<RHI::StreamBufferView>(&m_streamBufferView[1], 1));
+                }
             }
             }
         };
         };
 
 

+ 4 - 0
Gem/Code/Source/RHI/MSAAExampleComponent.cpp

@@ -392,6 +392,10 @@ namespace AtomSampleViewer
         AZ::RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
         AZ::RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
         pipelineStateDescriptor.m_inputStreamLayout = m_quadInputStreamLayout;
         pipelineStateDescriptor.m_inputStreamLayout = m_quadInputStreamLayout;
 
 
+        if (!m_customMSAAResolveShader)
+        {
+            return;
+        }
         auto shaderVariant = m_customMSAAResolveShader->GetVariant(AZ::RPI::ShaderAsset::RootShaderVariantStableId);
         auto shaderVariant = m_customMSAAResolveShader->GetVariant(AZ::RPI::ShaderAsset::RootShaderVariantStableId);
         shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
         shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
 
 

+ 1 - 1
Gem/Code/Source/RHI/RayTracingExampleComponent.cpp

@@ -245,7 +245,7 @@ namespace AtomSampleViewer
         // load miss shader
         // load miss shader
         const char* missShaderFilePath = "Shaders/RHI/RayTracingMiss.azshader";
         const char* missShaderFilePath = "Shaders/RHI/RayTracingMiss.azshader";
         m_missShader = LoadShader(missShaderFilePath, RayTracingExampleName);
         m_missShader = LoadShader(missShaderFilePath, RayTracingExampleName);
-        AZ_Assert(m_rayGenerationShader, "Failed to load miss shader");
+        AZ_Assert(m_missShader, "Failed to load miss shader");
 
 
         auto missShaderVariant = m_missShader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
         auto missShaderVariant = m_missShader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
         RHI::PipelineStateDescriptorForRayTracing missShaderDescriptor;
         RHI::PipelineStateDescriptorForRayTracing missShaderDescriptor;

+ 10 - 7
Gem/Code/Source/RHI/TrianglesConstantBufferExampleComponent.cpp

@@ -123,10 +123,11 @@ namespace AtomSampleViewer
 
 
             AZ::RHI::BufferMapResponse mapResponse;
             AZ::RHI::BufferMapResponse mapResponse;
             AZ::RHI::ResultCode resultCode = m_constantBufferPool->MapBuffer(mapRequest, mapResponse);
             AZ::RHI::ResultCode resultCode = m_constantBufferPool->MapBuffer(mapRequest, mapResponse);
-            AZ_Assert(resultCode == AZ::RHI::ResultCode::Success, "Failed to map constant buffer");
-
-            memcpy(mapResponse.m_data, data + triangleIdx, sizeof(InstanceInfo));
-            m_constantBufferPool->UnmapBuffer(*mapRequest.m_buffer);
+            if (mapResponse.m_data)
+            {
+                memcpy(mapResponse.m_data, data + triangleIdx, sizeof(InstanceInfo));
+                m_constantBufferPool->UnmapBuffer(*mapRequest.m_buffer);
+            }
         }
         }
     }
     }
 
 
@@ -142,10 +143,12 @@ namespace AtomSampleViewer
 
 
             AZ::RHI::BufferMapResponse mapResponse;
             AZ::RHI::BufferMapResponse mapResponse;
             AZ::RHI::ResultCode resultCode = m_constantBufferPool->MapBuffer(mapRequest, mapResponse);
             AZ::RHI::ResultCode resultCode = m_constantBufferPool->MapBuffer(mapRequest, mapResponse);
-            AZ_Assert(resultCode == AZ::RHI::ResultCode::Success, "Failed to map constant buffer");
 
 
-            memcpy(mapResponse.m_data, data + triangleIdx, sizeof(InstanceInfo));
-            m_constantBufferPool->UnmapBuffer(*mapRequest.m_buffer);
+            if(mapResponse.m_data)
+            {
+                memcpy(mapResponse.m_data, data + triangleIdx, sizeof(InstanceInfo));
+                m_constantBufferPool->UnmapBuffer(*mapRequest.m_buffer);
+            }
         }
         }
     }
     }
 
 

+ 34 - 30
Gem/Code/Source/RootConstantsExampleComponent.cpp

@@ -176,16 +176,17 @@ namespace AtomSampleViewer
             for (uint32_t i = 0; i < m_models.size(); ++i)
             for (uint32_t i = 0; i < m_models.size(); ++i)
             {
             {
                 auto model = m_models[i];
                 auto model = m_models[i];
-                Data::Instance<AZ::RPI::ModelLod> modelLod = model->GetLods()[0];
-                m_modelStreamBufferViews[i].resize(modelLod->GetMeshes().size());
-
-                for (uint32_t j = 0; j < m_modelStreamBufferViews[i].size(); ++j)
+                if (model)
                 {
                 {
-                    modelLod->GetStreamsForMesh(
-                        pipelineStateDescriptor.m_inputStreamLayout,
-                        m_modelStreamBufferViews[i][j],
-                        shaderVariant.GetInputContract(),
-                        j);
+                    Data::Instance<AZ::RPI::ModelLod> modelLod = model->GetLods()[0];
+                    m_modelStreamBufferViews[i].resize(modelLod->GetMeshes().size());
+
+                    for (uint32_t j = 0; j < m_modelStreamBufferViews[i].size(); ++j)
+                    {
+                        modelLod->GetStreamsForMesh(
+                            pipelineStateDescriptor.m_inputStreamLayout, m_modelStreamBufferViews[i][j], shaderVariant.GetInputContract(),
+                            j);
+                    }
                 }
                 }
             }            
             }            
         }
         }
@@ -300,28 +301,31 @@ namespace AtomSampleViewer
         m_rootConstantData.SetConstant(m_modelMatrixInputIndex, m_modelMatrices[modelIndex]);
         m_rootConstantData.SetConstant(m_modelMatrixInputIndex, m_modelMatrices[modelIndex]);
 
 
         const auto& model = m_models[modelIndex];
         const auto& model = m_models[modelIndex];
-        const auto& meshes = model->GetLods()[0]->GetMeshes();
-        for (uint32_t i = 0; i < meshes.size(); ++i)
+        if (model)
         {
         {
-            auto const& mesh = meshes[i];
-
-            // Build draw packet and set the values of the inline constants.
-            RHI::DrawPacketBuilder drawPacketBuilder;
-            drawPacketBuilder.Begin(nullptr);
-            drawPacketBuilder.SetDrawArguments(mesh.m_drawArguments);
-            drawPacketBuilder.SetIndexBufferView(mesh.m_indexBufferView);
-            drawPacketBuilder.SetRootConstants(m_rootConstantData.GetConstantData());
-            drawPacketBuilder.AddShaderResourceGroup(m_srg->GetRHIShaderResourceGroup());
-
-            RHI::DrawPacketBuilder::DrawRequest drawRequest;
-            drawRequest.m_listTag = m_drawListTag;
-            drawRequest.m_pipelineState = m_pipelineState.get();
-            drawRequest.m_streamBufferViews = m_modelStreamBufferViews[modelIndex][i];
-            drawRequest.m_sortKey = 0;
-            drawPacketBuilder.AddDrawItem(drawRequest);
-
-            AZStd::unique_ptr<const RHI::DrawPacket> drawPacket(drawPacketBuilder.End());
-            m_dynamicDraw->AddDrawPacket(RPI::RPISystemInterface::Get()->GetDefaultScene().get(), AZStd::move(drawPacket));
+            const auto& meshes = model->GetLods()[0]->GetMeshes();
+            for (uint32_t i = 0; i < meshes.size(); ++i)
+            {
+                auto const& mesh = meshes[i];
+
+                // Build draw packet and set the values of the inline constants.
+                RHI::DrawPacketBuilder drawPacketBuilder;
+                drawPacketBuilder.Begin(nullptr);
+                drawPacketBuilder.SetDrawArguments(mesh.m_drawArguments);
+                drawPacketBuilder.SetIndexBufferView(mesh.m_indexBufferView);
+                drawPacketBuilder.SetRootConstants(m_rootConstantData.GetConstantData());
+                drawPacketBuilder.AddShaderResourceGroup(m_srg->GetRHIShaderResourceGroup());
+
+                RHI::DrawPacketBuilder::DrawRequest drawRequest;
+                drawRequest.m_listTag = m_drawListTag;
+                drawRequest.m_pipelineState = m_pipelineState.get();
+                drawRequest.m_streamBufferViews = m_modelStreamBufferViews[modelIndex][i];
+                drawRequest.m_sortKey = 0;
+                drawPacketBuilder.AddDrawItem(drawRequest);
+
+                AZStd::unique_ptr<const RHI::DrawPacket> drawPacket(drawPacketBuilder.End());
+                m_dynamicDraw->AddDrawPacket(RPI::RPISystemInterface::Get()->GetDefaultScene().get(), AZStd::move(drawPacket));
+            }
         }
         }
     }
     }
 }
 }

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

@@ -116,6 +116,8 @@
 #include <AzFramework/Scene/Scene.h>
 #include <AzFramework/Scene/Scene.h>
 #include <AzFramework/Scene/SceneSystemBus.h>
 #include <AzFramework/Scene/SceneSystemBus.h>
 
 
+#include <Passes/RayTracingAmbientOcclusionPass.h>
+
 #include <Utils/Utils.h>
 #include <Utils/Utils.h>
 
 
 #include "ExampleComponentBus.h"
 #include "ExampleComponentBus.h"
@@ -271,7 +273,7 @@ namespace AtomSampleViewer
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRPISample( "RPI/Shading", azrtti_typeid<ShadingExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRPISample( "RPI/Shading", azrtti_typeid<ShadingExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRPISample( "RPI/StreamingImage", azrtti_typeid<StreamingImageExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRPISample( "RPI/StreamingImage", azrtti_typeid<StreamingImageExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRPISample( "Features/AreaLight", azrtti_typeid<AreaLightExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRPISample( "Features/AreaLight", azrtti_typeid<AreaLightExampleComponent>() ));
-        SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRPISample("Features/Bloom", azrtti_typeid<BloomExampleComponent>() ));
+        SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRPISample( "Features/Bloom", azrtti_typeid<BloomExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRPISample( "Features/Checkerboard", azrtti_typeid<CheckerboardExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRPISample( "Features/Checkerboard", azrtti_typeid<CheckerboardExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRPISample( "Features/DepthOfField", azrtti_typeid<DepthOfFieldExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRPISample( "Features/DepthOfField", azrtti_typeid<DepthOfFieldExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRPISample( "Features/DiffuseGI", azrtti_typeid<DiffuseGIExampleComponent>(), []() {return Utils::GetRHIDevice()->GetFeatures().m_rayTracing; }));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRPISample( "Features/DiffuseGI", azrtti_typeid<DiffuseGIExampleComponent>(), []() {return Utils::GetRHIDevice()->GetFeatures().m_rayTracing; }));
@@ -295,6 +297,12 @@ namespace AtomSampleViewer
         // We can only initialize this component after the asset catalog has been loaded.
         // We can only initialize this component after the asset catalog has been loaded.
         AzFramework::AssetCatalogEventBus::Handler::BusConnect();
         AzFramework::AssetCatalogEventBus::Handler::BusConnect();
         AZ::Render::ImGuiSystemNotificationBus::Handler::BusConnect();
         AZ::Render::ImGuiSystemNotificationBus::Handler::BusConnect();
+
+        auto* passSystem = AZ::RPI::PassSystemInterface::Get();
+        AZ_Assert(passSystem, "Cannot get the pass system.");
+
+        passSystem->AddPassCreator(Name("RayTracingAmbientOcclusionPass"), &AZ::Render::RayTracingAmbientOcclusionPass::Create);
+ 
     }
     }
 
 
     void SampleComponentManager::ActivateInternal()
     void SampleComponentManager::ActivateInternal()

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

@@ -176,9 +176,13 @@ namespace AtomSampleViewer
             materialMap[AZ::Render::DefaultMaterialAssignmentId].m_materialAsset = materialAsset;
             materialMap[AZ::Render::DefaultMaterialAssignmentId].m_materialAsset = materialAsset;
             materialMap[AZ::Render::DefaultMaterialAssignmentId].m_materialInstance = materialOverrideInstance;
             materialMap[AZ::Render::DefaultMaterialAssignmentId].m_materialInstance = materialOverrideInstance;
             bool isSkinnedMeshWithMotion = true;
             bool isSkinnedMeshWithMotion = true;
-            renderData.m_meshHandle = AZStd::make_shared<AZ::Render::MeshFeatureProcessorInterface::MeshHandle>(m_meshFeatureProcessor->AcquireMesh(renderData.m_skinnedMeshInstance->m_model->GetModelAsset(), materialMap, isSkinnedMeshWithMotion));
-            m_meshFeatureProcessor->SetTransform(*renderData.m_meshHandle, renderData.m_rootTransform);
-
+            if (renderData.m_skinnedMeshInstance->m_model)
+            {
+                renderData.m_meshHandle =
+                    AZStd::make_shared<AZ::Render::MeshFeatureProcessorInterface::MeshHandle>(m_meshFeatureProcessor->AcquireMesh(
+                        renderData.m_skinnedMeshInstance->m_model->GetModelAsset(), materialMap, isSkinnedMeshWithMotion));
+                m_meshFeatureProcessor->SetTransform(*renderData.m_meshHandle, renderData.m_rootTransform);
+            }
             // If render proxies already exist, they will be auto-freed
             // If render proxies already exist, they will be auto-freed
             AZ::Render::SkinnedMeshShaderOptions defaultShaderOptions;
             AZ::Render::SkinnedMeshShaderOptions defaultShaderOptions;
             AZ::Render::SkinnedMeshFeatureProcessorInterface::SkinnedMeshRenderProxyDesc desc{ skinnedMesh.m_skinnedMeshInputBuffers, renderData.m_skinnedMeshInstance, renderData.m_meshHandle, renderData.m_boneTransformBuffer, defaultShaderOptions };
             AZ::Render::SkinnedMeshFeatureProcessorInterface::SkinnedMeshRenderProxyDesc desc{ skinnedMesh.m_skinnedMeshInputBuffers, renderData.m_skinnedMeshInstance, renderData.m_meshHandle, renderData.m_boneTransformBuffer, defaultShaderOptions };

+ 124 - 35
Gem/Code/Source/SsaoExampleComponent.cpp

@@ -56,6 +56,9 @@ namespace AtomSampleViewer
 
 
     void SsaoExampleComponent::Activate()
     void SsaoExampleComponent::Activate()
     {
     {
+        RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
+        m_rayTracingEnabled = device->GetFeatures().m_rayTracing;
+
         AZ::TickBus::Handler::BusConnect();
         AZ::TickBus::Handler::BusConnect();
         AZ::Render::Bootstrap::DefaultWindowNotificationBus::Handler::BusConnect();
         AZ::Render::Bootstrap::DefaultWindowNotificationBus::Handler::BusConnect();
         ActivateSsaoPipeline();
         ActivateSsaoPipeline();
@@ -63,6 +66,8 @@ namespace AtomSampleViewer
         ActivateModel();
         ActivateModel();
         ActivatePostProcessSettings();
         ActivatePostProcessSettings();
         m_imguiSidebar.Activate();
         m_imguiSidebar.Activate();
+
+        SwitchAOType();
     }
     }
 
 
     void SsaoExampleComponent::Deactivate()
     void SsaoExampleComponent::Deactivate()
@@ -123,6 +128,29 @@ namespace AtomSampleViewer
         ssaoPipelineDesc.m_rootPassTemplate = "SsaoPipeline";
         ssaoPipelineDesc.m_rootPassTemplate = "SsaoPipeline";
 
 
         m_ssaoPipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(ssaoPipelineDesc, *m_windowContext);
         m_ssaoPipeline = AZ::RPI::RenderPipeline::CreateRenderPipelineForWindow(ssaoPipelineDesc, *m_windowContext);
+
+        if (m_rayTracingEnabled)
+        {
+            RPI::PassHierarchyFilter passFilter({ssaoPipelineDesc.m_name, "RayTracingAmbientOcclusionPass"});
+            const AZStd::vector<RPI::Pass*>& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter);
+            if (!passes.empty())
+            {
+                m_RTAOPass = azrtti_cast<Render::RayTracingAmbientOcclusionPass*>(passes.front());
+                AZ_Assert(m_RTAOPass, "Couldn't find the RayTracingAmbientOcclusionPass from the SsaoPipeline");
+            }
+        }
+        else
+        {
+            m_aoType = AmbientOcclusionType::SSAO;
+        }
+
+        RPI::PassHierarchyFilter passFilter({ssaoPipelineDesc.m_name, "SelectorPass"});
+        const AZStd::vector<RPI::Pass*>& passes = RPI::PassSystemInterface::Get()->FindPasses(passFilter);
+        if (!passes.empty())
+        {
+            m_selector = azrtti_cast<RPI::SelectorPass*>(passes.front());
+            AZ_Assert(m_selector, "Couldn't find the SelectorPass from the SsaoPipeline");
+        }
     }
     }
 
 
     void SsaoExampleComponent::DestroySsaoPipeline()
     void SsaoExampleComponent::DestroySsaoPipeline()
@@ -220,55 +248,116 @@ namespace AtomSampleViewer
     {
     {
         ScriptableImGui::ScopedNameContext context{ "SSAO" };
         ScriptableImGui::ScopedNameContext context{ "SSAO" };
 
 
-        ImGui::Text("SSAO Params");
-
-        bool enabled = m_ssaoSettings->GetEnabled();
-        if (ScriptableImGui::Checkbox("Enable", &enabled))
+        // only enable selecting AO type if ray tracing is enabled
+        if (m_rayTracingEnabled)
         {
         {
-            m_ssaoSettings->SetEnabled(enabled);
-            m_ssaoSettings->OnConfigChanged();
-        }
+            ImGui::Text("Ambient Occlusion");
+            bool aoTypeChanged = false;
+            aoTypeChanged = ScriptableImGui::RadioButton("Screen space AO", &m_aoType, AmbientOcclusionType::SSAO);
+            aoTypeChanged = aoTypeChanged | ScriptableImGui::RadioButton("Ray tracing AO", &m_aoType, AmbientOcclusionType::RTAO);
 
 
-        float strength = m_ssaoSettings->GetStrength();
-        if (ScriptableImGui::SliderFloat("SSAO Strength", &strength, 0.0f, 2.0f))
-        {
-            m_ssaoSettings->SetStrength(strength);
-            m_ssaoSettings->OnConfigChanged();
+            if (aoTypeChanged)
+            {
+                SwitchAOType();
+            }
+
+            ImGui::NewLine();
         }
         }
 
 
-        bool blurEnabled = m_ssaoSettings->GetEnableBlur();
-        if (ScriptableImGui::Checkbox("Enable Blur", &blurEnabled))
+        if (m_aoType == AmbientOcclusionType::SSAO)
         {
         {
-            m_ssaoSettings->SetEnableBlur(blurEnabled);
-            m_ssaoSettings->OnConfigChanged();
-        }
+            ImGui::Text("SSAO Params");
+
+            bool enabled = m_ssaoSettings->GetEnabled();
+            if (ScriptableImGui::Checkbox("Enable", &enabled))
+            {
+                m_ssaoSettings->SetEnabled(enabled);
+                m_ssaoSettings->OnConfigChanged();
+            }
+
+            float strength = m_ssaoSettings->GetStrength();
+            if (ScriptableImGui::SliderFloat("SSAO Strength", &strength, 0.0f, 2.0f))
+            {
+                m_ssaoSettings->SetStrength(strength);
+                m_ssaoSettings->OnConfigChanged();
+            }
 
 
-        float blurConstFalloff = m_ssaoSettings->GetBlurConstFalloff();
-        if (ScriptableImGui::SliderFloat("Blur Strength", &blurConstFalloff, 0.0f, 0.95f))
+            bool blurEnabled = m_ssaoSettings->GetEnableBlur();
+            if (ScriptableImGui::Checkbox("Enable Blur", &blurEnabled))
+            {
+                m_ssaoSettings->SetEnableBlur(blurEnabled);
+                m_ssaoSettings->OnConfigChanged();
+            }
+
+            float blurConstFalloff = m_ssaoSettings->GetBlurConstFalloff();
+            if (ScriptableImGui::SliderFloat("Blur Strength", &blurConstFalloff, 0.0f, 0.95f))
+            {
+                m_ssaoSettings->SetBlurConstFalloff(blurConstFalloff);
+                m_ssaoSettings->OnConfigChanged();
+            }
+
+            float blurDepthFalloffStrength = m_ssaoSettings->GetBlurDepthFalloffStrength();
+            if (ScriptableImGui::SliderFloat("Blur Sharpness", &blurDepthFalloffStrength, 0.0f, 400.0f))
+            {
+                m_ssaoSettings->SetBlurDepthFalloffStrength(blurDepthFalloffStrength);
+                m_ssaoSettings->OnConfigChanged();
+            }
+
+            float blurDepthFalloffThreshold = m_ssaoSettings->GetBlurDepthFalloffThreshold();
+            if (ScriptableImGui::SliderFloat("Blur Edge Threshold", &blurDepthFalloffThreshold, 0.0f, 1.0f))
+            {
+                m_ssaoSettings->SetBlurDepthFalloffThreshold(blurDepthFalloffThreshold);
+                m_ssaoSettings->OnConfigChanged();
+            }
+
+            bool downsampleEnabled = m_ssaoSettings->GetEnableDownsample();
+            if (ScriptableImGui::Checkbox("Enable Downsample", &downsampleEnabled))
+            {
+                m_ssaoSettings->SetEnableDownsample(downsampleEnabled);
+                m_ssaoSettings->OnConfigChanged();
+            }
+        }
+        else if (m_aoType == AmbientOcclusionType::RTAO)
         {
         {
-            m_ssaoSettings->SetBlurConstFalloff(blurConstFalloff);
-            m_ssaoSettings->OnConfigChanged();
+            ImGui::Text("RTAO Params");
+            float rayNear = m_RTAOPass->GetRayExtentMin();
+            if (ScriptableImGui::SliderFloat("Ray near distance", &rayNear, 0.0f, 0.5f))
+            {
+                m_RTAOPass->SetRayExtentMin(rayNear);
+            }
+            float rayFar = m_RTAOPass->GetRayExtentMax();
+            if (ScriptableImGui::SliderFloat("Ray far distance", &rayFar, 0.0f, 1.0f))
+            {
+                if (rayFar < rayNear)
+                {
+                    rayFar = rayNear + 0.1f;
+                }
+                m_RTAOPass->SetRayExtentMax(rayFar);
+            }
+            int32_t maxNumberRays = m_RTAOPass->GetRayNumberPerPixel();
+            if (ScriptableImGui::SliderInt("Number of rays", &maxNumberRays, 1, 30))
+            {
+                m_RTAOPass->SetRayNumberPerPixel(maxNumberRays);
+            }
         }
         }
-
-        float blurDepthFalloffStrength = m_ssaoSettings->GetBlurDepthFalloffStrength();
-        if (ScriptableImGui::SliderFloat("Blur Sharpness", &blurDepthFalloffStrength, 0.0f, 400.0f))
+    }
+    
+    void SsaoExampleComponent::SwitchAOType()
+    {
+        if (m_aoType == AmbientOcclusionType::SSAO)
         {
         {
-            m_ssaoSettings->SetBlurDepthFalloffStrength(blurDepthFalloffStrength);
-            m_ssaoSettings->OnConfigChanged();
+            m_selector->Connect(1, 0);
         }
         }
-
-        float blurDepthFalloffThreshold = m_ssaoSettings->GetBlurDepthFalloffThreshold();
-        if (ScriptableImGui::SliderFloat("Blur Edge Threshold", &blurDepthFalloffThreshold, 0.0f, 1.0f))
+        else if (m_aoType == AmbientOcclusionType::RTAO)
         {
         {
-            m_ssaoSettings->SetBlurDepthFalloffThreshold(blurDepthFalloffThreshold);
-            m_ssaoSettings->OnConfigChanged();
+            m_selector->Connect(0, 0);
         }
         }
 
 
-        bool downsampleEnabled = m_ssaoSettings->GetEnableDownsample();
-        if (ScriptableImGui::Checkbox("Enable Downsample", &downsampleEnabled))
+        m_ssaoSettings->SetEnabled(m_aoType == AmbientOcclusionType::SSAO);
+        m_ssaoSettings->OnConfigChanged();
+        if (m_RTAOPass)
         {
         {
-            m_ssaoSettings->SetEnableDownsample(downsampleEnabled);
-            m_ssaoSettings->OnConfigChanged();
+            m_RTAOPass->SetEnabled(m_aoType == AmbientOcclusionType::RTAO);
         }
         }
     }
     }
 
 

+ 20 - 0
Gem/Code/Source/SsaoExampleComponent.h

@@ -18,6 +18,8 @@
 #include <Atom/Feature/ImGui/ImGuiUtils.h>
 #include <Atom/Feature/ImGui/ImGuiUtils.h>
 #include <AzFramework/Input/Events/InputChannelEventListener.h>
 #include <AzFramework/Input/Events/InputChannelEventListener.h>
 #include <Atom/Bootstrap/DefaultWindowBus.h>
 #include <Atom/Bootstrap/DefaultWindowBus.h>
+#include <Atom/RPI.Public/Pass/Specific/SelectorPass.h>
+#include <Passes/RayTracingAmbientOcclusionPass.h>
 #include <Utils/ImGuiSidebar.h>
 #include <Utils/ImGuiSidebar.h>
 #include <Utils/Utils.h>
 #include <Utils/Utils.h>
 #include <ExampleComponentBus.h>
 #include <ExampleComponentBus.h>
@@ -71,6 +73,8 @@ namespace AtomSampleViewer
         void DrawImGUI();
         void DrawImGUI();
         void DrawSidebar();
         void DrawSidebar();
 
 
+        void SwitchAOType();
+
         AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_meshHandle;
         AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_meshHandle;
         AZ::Render::MeshFeatureProcessorInterface::ModelChangedEvent::Handler m_meshChangedHandler
         AZ::Render::MeshFeatureProcessorInterface::ModelChangedEvent::Handler m_meshChangedHandler
         {
         {
@@ -93,5 +97,21 @@ namespace AtomSampleViewer
         AZ::Entity* m_ssaoEntity = nullptr;
         AZ::Entity* m_ssaoEntity = nullptr;
 
 
         AZ::Render::ImGuiActiveContextScope m_imguiScope;
         AZ::Render::ImGuiActiveContextScope m_imguiScope;
+
+        enum AmbientOcclusionType
+        {
+            SSAO = 0,
+            RTAO
+        };
+
+        // Ambient Occlusion type. 0: SSAO; 1: RTAO
+        int m_aoType = AmbientOcclusionType::SSAO;
+
+        // Ray tracing ambient occlusion
+        bool m_rayTracingEnabled = false;
+        AZ::RPI::Ptr<AZ::Render::RayTracingAmbientOcclusionPass> m_RTAOPass;
+
+        // To swtich between outputs
+        AZ::RPI::Ptr<AZ::RPI::SelectorPass> m_selector;
     };
     };
 } // namespace AtomSampleViewer
 } // namespace AtomSampleViewer

+ 7 - 4
Gem/Code/atomsampleviewergem_private_files.cmake

@@ -107,6 +107,8 @@ set(FILES
     Source/DecalContainer.h
     Source/DecalContainer.h
     Source/DepthOfFieldExampleComponent.h
     Source/DepthOfFieldExampleComponent.h
     Source/DepthOfFieldExampleComponent.cpp
     Source/DepthOfFieldExampleComponent.cpp
+    Source/DiffuseGIExampleComponent.cpp
+    Source/DiffuseGIExampleComponent.h
     Source/DynamicDrawExampleComponent.h
     Source/DynamicDrawExampleComponent.h
     Source/DynamicDrawExampleComponent.cpp
     Source/DynamicDrawExampleComponent.cpp
     Source/DynamicMaterialTestComponent.cpp
     Source/DynamicMaterialTestComponent.cpp
@@ -133,6 +135,9 @@ set(FILES
     Source/MultiViewSingleSceneAuxGeomExampleComponent.h
     Source/MultiViewSingleSceneAuxGeomExampleComponent.h
     Source/ParallaxMappingExampleComponent.cpp
     Source/ParallaxMappingExampleComponent.cpp
     Source/ParallaxMappingExampleComponent.h
     Source/ParallaxMappingExampleComponent.h
+    Source/Passes/RayTracingAmbientOcclusionPass.cpp
+    Source/Passes/RayTracingAmbientOcclusionPass.h
+    Source/ParallaxMappingExampleComponent.h
     Source/ProceduralSkinnedMesh.cpp
     Source/ProceduralSkinnedMesh.cpp
     Source/ProceduralSkinnedMesh.h
     Source/ProceduralSkinnedMesh.h
     Source/ProceduralSkinnedMeshUtils.cpp
     Source/ProceduralSkinnedMeshUtils.cpp
@@ -153,6 +158,8 @@ set(FILES
     Source/SkinnedMeshExampleComponent.h
     Source/SkinnedMeshExampleComponent.h
     Source/SsaoExampleComponent.cpp
     Source/SsaoExampleComponent.cpp
     Source/SsaoExampleComponent.h
     Source/SsaoExampleComponent.h
+    Source/SSRExampleComponent.cpp
+    Source/SSRExampleComponent.h
     Source/StreamingImageExampleComponent.cpp
     Source/StreamingImageExampleComponent.cpp
     Source/StreamingImageExampleComponent.h
     Source/StreamingImageExampleComponent.h
     Source/TonemappingExampleComponent.cpp
     Source/TonemappingExampleComponent.cpp
@@ -161,10 +168,6 @@ set(FILES
     Source/TransparencyExampleComponent.h
     Source/TransparencyExampleComponent.h
     Source/Utils/FileIOErrorHandler.cpp
     Source/Utils/FileIOErrorHandler.cpp
     Source/Utils/FileIOErrorHandler.h
     Source/Utils/FileIOErrorHandler.h
-    Source/DiffuseGIExampleComponent.cpp
-    Source/DiffuseGIExampleComponent.h
-    Source/SSRExampleComponent.cpp
-    Source/SSRExampleComponent.h
     Source/Utils/ImGuiAssetBrowser.cpp
     Source/Utils/ImGuiAssetBrowser.cpp
     Source/Utils/ImGuiAssetBrowser.h
     Source/Utils/ImGuiAssetBrowser.h
     Source/Utils/ImGuiHistogramQueue.cpp
     Source/Utils/ImGuiHistogramQueue.cpp

+ 1 - 0
Gem/Code/runtime_dependencies.cmake

@@ -24,6 +24,7 @@ set(GEM_DEPENDENCIES
     Gem::Atom_Feature_Common
     Gem::Atom_Feature_Common
     Gem::Atom_Bootstrap
     Gem::Atom_Bootstrap
     Gem::Atom_RHI_DX12.Private
     Gem::Atom_RHI_DX12.Private
+    Gem::Atom_RHI_Null.Private
     Gem::Atom_Component_DebugCamera
     Gem::Atom_Component_DebugCamera
     Gem::AtomLyIntegration_CommonFeatures
     Gem::AtomLyIntegration_CommonFeatures
     Gem::EMotionFX_Atom
     Gem::EMotionFX_Atom

+ 2 - 0
Gem/Code/tool_dependencies.cmake

@@ -33,6 +33,8 @@ set(GEM_DEPENDENCIES
     Gem::Atom_Asset_Shader.Builders
     Gem::Atom_Asset_Shader.Builders
     Gem::Atom_RHI_DX12.Private
     Gem::Atom_RHI_DX12.Private
     Gem::Atom_RHI_DX12.Builders
     Gem::Atom_RHI_DX12.Builders
+    Gem::Atom_RHI_Null.Private
+    Gem::Atom_RHI_Null.Builders
     Gem::Atom_Component_DebugCamera
     Gem::Atom_Component_DebugCamera
     Gem::AtomImGuiTools
     Gem::AtomImGuiTools
     Gem::AtomLyIntegration_CommonFeatures.Editor
     Gem::AtomLyIntegration_CommonFeatures.Editor

+ 9 - 1
Passes/PassTemplates.azasset

@@ -11,7 +11,7 @@
             {
             {
                 "Name": "DepthMSAAPassTemplate",
                 "Name": "DepthMSAAPassTemplate",
                 "Path": "Passes/DepthMSAA.pass"
                 "Path": "Passes/DepthMSAA.pass"
-            },            
+            },
             {
             {
                 "Name": "DepthMSAA2xPassTemplate",
                 "Name": "DepthMSAA2xPassTemplate",
                 "Path": "Passes/DepthMSAA2x.pass"
                 "Path": "Passes/DepthMSAA2x.pass"
@@ -571,6 +571,14 @@
             {
             {
                 "Name": "UIParentTemplate",
                 "Name": "UIParentTemplate",
                 "Path": "Passes/UIParent.pass"
                 "Path": "Passes/UIParent.pass"
+            },
+            {
+                "Name": "RayTracingAmbientOcclusionPassTemplate",
+                "Path": "Passes/RayTracingAmbientOcclusion.pass"
+            },
+            {
+                "Name": "SelectorPassTemplate",
+                "Path": "Passes/SelectorPass.pass"
             }
             }
         ]
         ]
     }
     }

+ 72 - 0
Passes/RayTracingAmbientOcclusion.pass

@@ -0,0 +1,72 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "PassAsset",
+    "ClassData": {
+        "PassTemplate": {
+            "Name": "RayTracingAmbientOcclusionPassTemplate",
+            "PassClass": "RayTracingAmbientOcclusionPass",
+            "Slots": [
+                {
+                    "Name": "InputDepth",
+                    "ShaderInputName": "m_depth",
+                    "SlotType": "Input",
+                    "ScopeAttachmentUsage": "Shader",
+                    "ImageViewDesc": {
+                        "AspectFlags": [
+                            "Depth"
+                        ]
+                    }
+                },
+                {
+                    "Name": "WorldNormal",
+                    "ShaderInputName": "m_worldNormalMap",
+                    "SlotType": "Input",
+                    "ScopeAttachmentUsage": "Shader"
+                },
+                {
+                    "Name": "OutputAO",
+                    "ShaderInputName": "m_outputAO",
+                    "SlotType": "Output",
+                    "ScopeAttachmentUsage": "Shader",
+                    "LoadStoreAction": {
+                        "ClearValue": {
+                            "Value": [
+                                0.5,
+                                0.0,
+                                0.0,
+                                {}
+                            ]
+                        },
+                        "LoadAction": "Clear",
+                        "LoadActionStencil": "Clear"
+                    }
+                }
+            ],
+            "ImageAttachments": [
+                {
+                    "Name": "OutputAOImage",
+                    "SizeSource": {
+                        "Source": {
+                            "Pass": "Parent",
+                            "Attachment": "SwapChainOutput"
+                        }
+                    },
+                    "FormatSource": {
+                        "Pass": "Parent",
+                        "Attachment": "SwapChainOutput"
+                    }
+                }
+            ],
+            "Connections": [
+                {
+                    "LocalSlot": "OutputAO",
+                    "AttachmentRef": {
+                        "Pass": "This",
+                        "Attachment": "OutputAOImage"
+                    }
+                }
+            ]
+        }
+    }
+}

+ 28 - 0
Passes/SelectorPass.pass

@@ -0,0 +1,28 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "PassAsset",
+    "ClassData": {
+        "PassTemplate": {
+            "Name": "SelectorPassTemplate",
+            "PassClass": "SelectorPass",
+            "Slots": [
+                {
+                    "Name": "Input1",
+                    "SlotType": "Input",
+                    "ScopeAttachmentUsage": "Shader"
+                },
+                {
+                    "Name": "Input2",
+                    "SlotType": "Input",
+                    "ScopeAttachmentUsage": "Shader"
+                },
+                {
+                    "Name": "Output",
+                    "SlotType": "Output",
+                    "ScopeAttachmentUsage": "Shader"
+                }
+            ]
+        }
+    }
+}

+ 258 - 3
Passes/SsaoPipeline.pass

@@ -31,6 +31,80 @@
                         }
                         }
                     ]
                     ]
                 },
                 },
+                {
+                    "Name": "RayTracingAccelerationStructurePass",
+                    "TemplateName": "RayTracingAccelerationStructurePassTemplate"
+                },
+                {
+                    "Name": "CascadedShadowmapsPass",
+                    "TemplateName": "CascadedShadowmapsTemplate",
+                    "PassData": {
+                        "$type": "RasterPassData",
+                        "DrawListTag": "shadow",
+                        "PipelineViewTag": "DirectionalLightView"
+                    },
+                    "Connections": [
+                        {
+                            "LocalSlot": "SkinnedMeshes",
+                            "AttachmentRef": {
+                                "Pass": "SkinningPass",
+                                "Attachment": "SkinnedMeshOutputStream"
+                            }
+                        }
+                    ]
+                },
+                {
+                    "Name": "SpotLightShadowmapsPass",
+                    "TemplateName": "SpotLightShadowmapsTemplate",
+                    "PassData": {
+                        "$type": "RasterPassData",
+                        "DrawListTag": "shadow",
+                        "PipelineViewTag": "SpotLightView"
+                    },
+                    "Connections": [
+                        {
+                            "LocalSlot": "SkinnedMeshes",
+                            "AttachmentRef": {
+                                "Pass": "SkinningPass",
+                                "Attachment": "SkinnedMeshOutputStream"
+                            }
+                        }
+                    ]
+                },
+                {
+                    "Name": "EsmShadowmapsPassDirectional",
+                    "TemplateName": "EsmShadowmapsTemplate",
+                    "PassData": {
+                        "$type": "EsmShadowmapsPassData",
+                        "LightType": "directional"
+                    },
+                    "Connections": [
+                        {
+                            "LocalSlot": "DepthShadowmaps",
+                            "AttachmentRef": {
+                                "Pass": "CascadedShadowmapsPass",
+                                "Attachment": "Shadowmap"
+                            }
+                        }
+                    ]
+                },
+                {
+                    "Name": "EsmShadowmapsPassSpot",
+                    "TemplateName": "EsmShadowmapsTemplate",
+                    "PassData": {
+                        "$type": "EsmShadowmapsPassData",
+                        "LightType": "spot"
+                    },
+                    "Connections": [
+                        {
+                            "LocalSlot": "DepthShadowmaps",
+                            "AttachmentRef": {
+                                "Pass": "SpotLightShadowmapsPass",
+                                "Attachment": "Shadowmap"
+                            }
+                        }
+                    ]
+                },
                 {
                 {
                     "Name": "DepthPass",
                     "Name": "DepthPass",
                     "TemplateName": "DepthPassTemplate",
                     "TemplateName": "DepthPassTemplate",
@@ -49,6 +123,59 @@
                         }
                         }
                     ]
                     ]
                 },
                 },
+                {
+                    "Name": "LightCullingTilePreparePass",
+                    "TemplateName": "LightCullingTilePrepareTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "Depth",
+                            "AttachmentRef": {
+                                "Pass": "DepthPass",
+                                "Attachment": "Output"
+                            }
+                        }
+                    ]
+                },
+                {
+                    "Name": "LightCullingPass",
+                    "TemplateName": "LightCullingTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "TileLightData",
+                            "AttachmentRef": {
+                                "Pass": "LightCullingTilePreparePass",
+                                "Attachment": "TileLightData"
+                            }
+                        }
+                    ]
+                },
+                {
+                    "Name": "LightCullingRemapPass",
+                    "TemplateName": "LightCullingRemapTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "TileLightData",
+                            "AttachmentRef": {
+                                "Pass": "LightCullingTilePreparePass",
+                                "Attachment": "TileLightData"
+                            }
+                        },
+                        {
+                            "LocalSlot": "LightCount",
+                            "AttachmentRef": {
+                                "Pass": "LightCullingPass",
+                                "Attachment": "LightCount"
+                            }
+                        },
+                        {
+                            "LocalSlot": "LightList",
+                            "AttachmentRef": {
+                                "Pass": "LightCullingPass",
+                                "Attachment": "LightList"
+                            }
+                        }
+                    ]
+                },
                 {
                 {
                     "Name": "CameraMotionVectorPass",
                     "Name": "CameraMotionVectorPass",
                     "TemplateName": "CameraMotionVectorPassTemplate",
                     "TemplateName": "CameraMotionVectorPassTemplate",
@@ -84,6 +211,89 @@
                         }
                         }
                     ]
                     ]
                 },
                 },
+                {
+                    "Name": "ForwardPass",
+                    "TemplateName": "ForwardPassTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "DirectionalLightShadowmap",
+                            "AttachmentRef": {
+                                "Pass": "CascadedShadowmapsPass",
+                                "Attachment": "Shadowmap"
+                            }
+                        },
+                        {
+                            "LocalSlot": "ExponentialShadowmapDirectional",
+                            "AttachmentRef": {
+                                "Pass": "EsmShadowmapsPassDirectional",
+                                "Attachment": "EsmShadowmaps"
+                            }
+                        },
+                        {
+                            "LocalSlot": "SpotLightShadowmap",
+                            "AttachmentRef": {
+                                "Pass": "SpotLightShadowmapsPass",
+                                "Attachment": "Shadowmap"
+                            }
+                        },
+                        {
+                            "LocalSlot": "ExponentialShadowmapSpot",
+                            "AttachmentRef": {
+                                "Pass": "EsmShadowmapsPassSpot",
+                                "Attachment": "EsmShadowmaps"
+                            }
+                        },
+                        {
+                            "LocalSlot": "DepthStencilInputOutput",
+                            "AttachmentRef": {
+                                "Pass": "DepthPass",
+                                "Attachment": "Output"
+                            }
+                        },
+                        {
+                            "LocalSlot": "TileLightData",
+                            "AttachmentRef": {
+                                "Pass": "LightCullingTilePreparePass",
+                                "Attachment": "TileLightData"
+                            }
+                        },
+                        {
+                            "LocalSlot": "LightListRemapped",
+                            "AttachmentRef": {
+                                "Pass": "LightCullingRemapPass",
+                                "Attachment": "LightListRemapped"
+                            }
+                        }
+                    ],
+                    "PassData": {
+                        "$type": "RasterPassData",
+                        "DrawListTag": "forward",
+                        "PipelineViewTag": "MainCamera",
+                        "PassSrgAsset": {
+                            "FilePath": "shaderlib/atom/features/pbr/forwardpasssrg.azsli:PassSrg"
+                        }
+                    }
+                },
+                {
+                    "Name": "RayTracingAmbientOcclusionPass",
+                    "TemplateName": "RayTracingAmbientOcclusionPassTemplate",
+                    "Connections": [
+                        {
+                            "LocalSlot": "InputDepth",
+                            "AttachmentRef": {
+                                "Pass": "ForwardPass",
+                                "Attachment": "DepthStencilInputOutput"
+                            }
+                        },
+                        {
+                            "LocalSlot": "WorldNormal",
+                            "AttachmentRef": {
+                                "Pass": "ForwardPass",
+                                "Attachment": "NormalOutput"
+                            }
+                        }
+                    ]
+                },
                 {
                 {
                     "Name": "DepthToLinearDepthPass",
                     "Name": "DepthToLinearDepthPass",
                     "TemplateName": "DepthToLinearTemplate",
                     "TemplateName": "DepthToLinearTemplate",
@@ -113,6 +323,30 @@
                 {
                 {
                     "Name": "DebugWhiteTexture",
                     "Name": "DebugWhiteTexture",
                     "TemplateName": "FullscreenOutputOnlyTemplate",
                     "TemplateName": "FullscreenOutputOnlyTemplate",
+                    "ImageAttachments": [
+                        {
+                            "Name": "OutputAttachment",
+                            "SizeSource": {
+                                "Source": {
+                                    "Pass": "Parent",
+                                    "Attachment": "SwapChainOutput"
+                                }
+                            },
+                            "FormatSource": {
+                                "Pass": "Parent",
+                                "Attachment": "SwapChainOutput"
+                            }
+                        }
+                    ],
+                    "Connections": [
+                        {
+                            "LocalSlot": "Output",
+                            "AttachmentRef": {
+                                "Pass": "This",
+                                "Attachment": "OutputAttachment"
+                            }
+                        }
+                    ],
                     "PassData": {
                     "PassData": {
                         "$type": "FullscreenTrianglePassData",
                         "$type": "FullscreenTrianglePassData",
                         "ShaderDataMappings": {
                         "ShaderDataMappings": {
@@ -156,6 +390,27 @@
                         "Make Fullscreen Pass": true
                         "Make Fullscreen Pass": true
                     }
                     }
                 },
                 },
+                {
+                    "Name": "SelectorPass",
+                    "TemplateName": "SelectorPassTemplate",
+                    "Enabled": true,
+                    "Connections": [
+                        {
+                            "LocalSlot": "Input1",
+                            "AttachmentRef": {
+                                "Pass": "RayTracingAmbientOcclusionPass",
+                                "Attachment": "OutputAO"
+                            }
+                        },
+                        {
+                            "LocalSlot": "Input2",
+                            "AttachmentRef": {
+                                "Pass": "ModulateWithSsao",
+                                "Attachment": "InputOutput"
+                            }
+                        }
+                    ]
+                },
                 {
                 {
                     "Name": "DisplayMapperPass",
                     "Name": "DisplayMapperPass",
                     "TemplateName": "DisplayMapperTemplate",
                     "TemplateName": "DisplayMapperTemplate",
@@ -164,8 +419,8 @@
                         {
                         {
                             "LocalSlot": "Input",
                             "LocalSlot": "Input",
                             "AttachmentRef": {
                             "AttachmentRef": {
-                                "Pass": "ModulateWithSsao",
-                                "Attachment": "InputOutput"
+                                "Pass": "SelectorPass",
+                                "Attachment": "Output"
                             }
                             }
                         },
                         },
                         {
                         {
@@ -202,7 +457,7 @@
                         "$type": "ImGuiPassData",
                         "$type": "ImGuiPassData",
                         "IsDefaultImGui": true
                         "IsDefaultImGui": true
                     }
                     }
-                },
+                },		
                 {
                 {
                     "Name": "2DPass",
                     "Name": "2DPass",
                     "TemplateName": "UIPassTemplate",
                     "TemplateName": "UIPassTemplate",

+ 7 - 0
Registry/editorpreferences.setreg

@@ -0,0 +1,7 @@
+{
+    "Amazon": {
+        "Preferences": {
+            "EnablePrefabSystem": false
+        }
+    }
+}

+ 1 - 1
Scripts/DiffuseGITest.bv.lua

@@ -20,7 +20,7 @@ else
 
 
     OpenSample('Features/DiffuseGI')
     OpenSample('Features/DiffuseGI')
     ResizeViewport(800, 600)
     ResizeViewport(800, 600)
-    SelectImageComparisonToleranceLevel("Level E")
+    SelectImageComparisonToleranceLevel("Level G")
 
 
     IdleSeconds(5)
     IdleSeconds(5)
 
 

+ 1 - 1
Scripts/DynamicMaterialTest.bv.lua

@@ -31,7 +31,7 @@ function SetDefaultLatticeConfiguration()
 end
 end
 
 
 function TakeScreenshotSeries(filenamePrefix)
 function TakeScreenshotSeries(filenamePrefix)
-    SelectImageComparisonToleranceLevel("Level G")
+    SelectImageComparisonToleranceLevel("Level H")
 
 
     -- There could be variation in how long prior activities took so reset the clock for each series of screenshots
     -- There could be variation in how long prior activities took so reset the clock for each series of screenshots
     SetImguiValue('Reset Clock', true) 
     SetImguiValue('Reset Clock', true) 

+ 1 - 1
Scripts/ExpectedScreenshots/AreaLights/disk_double_sided_vary_rough_metal.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:af4885609e4a6695783b863ea43d22f30291f640fe9613cf90efc602d7ed6143
+oid sha256:20447abab4e19cb2bbb42b87d10870bc5fd303f1f6dcad422ba5b1e030e1a3eb
 size 1920016
 size 1920016

+ 1 - 1
Scripts/ExpectedScreenshots/AreaLights/polygon_double_sided_vary_rough_metal.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:8162b6ca5fc8f3a8cb8a0c9bc0bca75b1a8a0cd6ae2f8a08ec47b9324ad81121
+oid sha256:017032e737a39b59817a2fbd377e6a3f1c5ec5f545ffa7d5d21353066d312bd8
 size 1920016
 size 1920016

+ 1 - 1
Scripts/ExpectedScreenshots/AreaLights/polygon_vary_rough_metal.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:8b79ac6c930f74385f5c51a1ac13e78992251cc8912bbab1351f1b535d160d99
+oid sha256:ca9ad79e6b62206453f487800907ae9f1818e1ab170f1fe851cab20b85430039
 size 1920016
 size 1920016

+ 1 - 1
Scripts/ExpectedScreenshots/AreaLights/quad_double_sided_vary_rough_metal.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:63c2410ff4ce28a07762ce867d39ca96a18a87fb46bb9aa8f0911321d7bb935a
+oid sha256:8358ade06ab3e1c4d0d948df92f826c6ea0114371a557c6921c7c4884e8f2a94
 size 1920016
 size 1920016

+ 1 - 1
Scripts/ExpectedScreenshots/AutoBrick/brick.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:5f0ffdbb77b9bee07830f1a1ce6d607283b97867c3e2200ecb6200998665b9c6
+oid sha256:8a3c23e0bb2cd5aa809cf6118eccae44a910ccb224abecbfbb116d7c2f3a25aa
 size 2994018
 size 2994018

+ 1 - 1
Scripts/ExpectedScreenshots/Decals/screenshot_decals.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:81601ca5c13eabf51a244526c20f747ed691366e9e7ca45edf6ddbc63149c8ca
+oid sha256:4ac85a66a6a618ef5c39c08bc98481e502ea2a3a18f2fccb1d6e13e63b8c0a12
 size 4320016
 size 4320016

+ 1 - 1
Scripts/ExpectedScreenshots/DynamicMaterialTest/01_defaultsetup_A.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:848235d015d8bcc4fdee4f5701e46edf27286ae2cb5d2eee5a99d1c57565e043
+oid sha256:8baca90273604dd86f2f354c65765051e448fc2fc1af8327f35843d187197cd9
 size 2359312
 size 2359312

+ 1 - 1
Scripts/ExpectedScreenshots/DynamicMaterialTest/01_defaultsetup_B.ppm

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

+ 1 - 1
Scripts/ExpectedScreenshots/DynamicMaterialTest/02_manyentities_A.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:721338a2a20ee58c1932892c93c074027cde7be06a5f517fafd6c7710fa11db3
+oid sha256:c346eb606d5a72924c8e4fd8dca64b250179c38cb8b8c78b737da484a23c732e
 size 2359312
 size 2359312

+ 1 - 1
Scripts/ExpectedScreenshots/DynamicMaterialTest/02_manyentities_B.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:625a5f72bac1a9fc8fb061089741754091969645819bbe39c816f622fb7b88d2
+oid sha256:f9238938e647c9762a9834b52bf3bcba8b3eb985205492f997ce081d468089a8
 size 2359312
 size 2359312

+ 1 - 1
Scripts/ExpectedScreenshots/MinimalPBR/MinimalPBR_BlueMetal.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:6e085c92ca57d7fc0919659a3e83120cbb3f79f626a2d7cc9f8b1dbdf508d274
+oid sha256:8911787005a3fe5c3b5e6ca22cc266e7430b7d5ffdbee090c5b67f6872ba2b4d
 size 2994018
 size 2994018

+ 1 - 1
Scripts/ExpectedScreenshots/MinimalPBR/MinimalPBR_Default.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:4a84b00517511aa73862bfdc07cfc963a883d2793a6cfe735c99ecacf23d03da
+oid sha256:ce6a09d5928e7d085d29382ce435286c7b5ee1a4216aa55eb30c63b69dd2ac1c
 size 2994018
 size 2994018

+ 1 - 1
Scripts/ExpectedScreenshots/MinimalPBR/MinimalPBR_RedDielectric.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:dd455627bb881d14226882af6d2c9a4b30e32e0dd5d016f110ff3e80b08adb5c
+oid sha256:c7422028085944e08b102c52b644fd1d7214fdb6d77502cbee04fec4cc4f69cf
 size 2994018
 size 2994018

+ 1 - 1
Scripts/ExpectedScreenshots/MultiRenderPipeline/IBL_Skybox_Spot_Dir_DOF_window1.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:ee5449445a1cd4dacaee3e3c848347fd111d8d4559148845df8c61af0749badd
+oid sha256:4ae21c4e2d987947051e8266a113f5fa6c0593813768acc7f2b60f5b9f9b4c27
 size 1440015
 size 1440015

+ 1 - 1
Scripts/ExpectedScreenshots/MultiRenderPipeline/IBL_Skybox_Spot_Dir_DOF_window2.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:ed1fdcfbb2d2b2f170972e24d3c0e81daa3223b96873074807ab2c53f968951d
+oid sha256:9203aca6ba046effe31563feb7af35adcb3f5502c464e29ace48dfc552b9e3e7
 size 1440015
 size 1440015

+ 1 - 1
Scripts/ExpectedScreenshots/MultiRenderPipeline/IBL_Skybox_Spot_Dir_window2.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:ed1fdcfbb2d2b2f170972e24d3c0e81daa3223b96873074807ab2c53f968951d
+oid sha256:9203aca6ba046effe31563feb7af35adcb3f5502c464e29ace48dfc552b9e3e7
 size 1440015
 size 1440015

+ 1 - 1
Scripts/ExpectedScreenshots/MultiRenderPipeline/IBL_Skybox_Spot_window2.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:0de7ea4ecbcd949e2b848b1e7342381b54121fbef1069c1635cf6d0f91cc8445
+oid sha256:50d50507b85ea9b120bbf1b945d767b971bacb6d11bb19db29a3d59b9fbbf2c4
 size 1440015
 size 1440015

+ 1 - 1
Scripts/ExpectedScreenshots/MultiRenderPipeline/IBL_Skybox_window2.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:a8b359cc485258614e0f1f58245e08c28676496c861ceacf15d208dafa5c8830
+oid sha256:3bb98af2b1f0943bce343d5fedbe90037c528ccf5e73e89b786797d90599be3a
 size 1440015
 size 1440015

+ 1 - 1
Scripts/ExpectedScreenshots/MultiRenderPipeline/NoDOF_window2.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:ed1fdcfbb2d2b2f170972e24d3c0e81daa3223b96873074807ab2c53f968951d
+oid sha256:9203aca6ba046effe31563feb7af35adcb3f5502c464e29ace48dfc552b9e3e7
 size 1440015
 size 1440015

+ 1 - 1
Scripts/ExpectedScreenshots/MultiRenderPipeline/Start_window1.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:ea747700e1aea7f79f16e9afb856067a22296a0727487a5d05df9db046452410
+oid sha256:4ae21c4e2d987947051e8266a113f5fa6c0593813768acc7f2b60f5b9f9b4c27
 size 1440015
 size 1440015

+ 1 - 1
Scripts/ExpectedScreenshots/MultiRenderPipeline/Start_window2.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:ea747700e1aea7f79f16e9afb856067a22296a0727487a5d05df9db046452410
+oid sha256:4ae21c4e2d987947051e8266a113f5fa6c0593813768acc7f2b60f5b9f9b4c27
 size 1440015
 size 1440015

+ 1 - 1
Scripts/ExpectedScreenshots/MultiRenderPipeline/TwoCameras_window1.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:ea747700e1aea7f79f16e9afb856067a22296a0727487a5d05df9db046452410
+oid sha256:4ae21c4e2d987947051e8266a113f5fa6c0593813768acc7f2b60f5b9f9b4c27
 size 1440015
 size 1440015

+ 1 - 1
Scripts/ExpectedScreenshots/MultiRenderPipeline/TwoCameras_window2.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:ed1fdcfbb2d2b2f170972e24d3c0e81daa3223b96873074807ab2c53f968951d
+oid sha256:9203aca6ba046effe31563feb7af35adcb3f5502c464e29ace48dfc552b9e3e7
 size 1440015
 size 1440015

+ 1 - 1
Scripts/ExpectedScreenshots/MultiRenderPipeline/WithDOF_window1.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:ee5449445a1cd4dacaee3e3c848347fd111d8d4559148845df8c61af0749badd
+oid sha256:4ae21c4e2d987947051e8266a113f5fa6c0593813768acc7f2b60f5b9f9b4c27
 size 1440015
 size 1440015

+ 1 - 1
Scripts/ExpectedScreenshots/MultiRenderPipeline/WithDOF_window2.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:ed1fdcfbb2d2b2f170972e24d3c0e81daa3223b96873074807ab2c53f968951d
+oid sha256:9203aca6ba046effe31563feb7af35adcb3f5502c464e29ace48dfc552b9e3e7
 size 1440015
 size 1440015

+ 1 - 1
Scripts/ExpectedScreenshots/PassTree/specularResolved.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:ebc40bfd9d93d62d3b184cafe8fec8d62cf48015a21715ca8f53a7b3745250ee
+oid sha256:e166c051f9834a74c95255990e9b90c68e2ac1c56031f697fa62a3d967f4b9eb
 size 1440015
 size 1440015

+ 1 - 1
Scripts/ExpectedScreenshots/SceneReloadSoakTest/screenshot.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:28b29d90d54fa58b558c02cf776c138bc69a2197fd6e32bf3f5e7c9a9e207cc4
+oid sha256:f34586f9ae78ddbf72e995fc9afe1a492fc0380857c383d30f6a8c2cb6129866
 size 750015
 size 750015

+ 1 - 1
Scripts/ExpectedScreenshots/Skin/001_lucy_regression_test.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:5d956700f41864355f6249f648b054e8bea5b0e3bed0588f0ee7084081d4c513
+oid sha256:16816705c612d0d7efd5446fc65f22ba0d8e39c8e865448fab7b01a528045b3e
 size 2994018
 size 2994018

+ 1 - 1
Scripts/ExpectedScreenshots/Skin/002_wrinkle_regression_test.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:bffbae969ac7973ed22201d9c306f7949d22c333a6955f212341b4d1a38d22cf
+oid sha256:045a502e37630af3865080c5e1553a4159167d1e746f497fadfca6a8ce414327
 size 2994018
 size 2994018

+ 1 - 1
Scripts/ExpectedScreenshots/StandardPBR/001_defaultwhite.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:364b9cb1faf7abbb112ce3e3285eeddcd1bbd575a0fd2a2b5cbcc4e837e85cfd
+oid sha256:913805ae32d1c2ddb06918759abbaac47077aca62852ae4a978893ad41ee78f2
 size 2994018
 size 2994018

+ 1 - 1
Scripts/ExpectedScreenshots/StandardPBR/003_metalmatte.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:0ef7583faae8d7f9f1ce1b31afab309c4bfe38c46905671e5cf5f47d14fc16b1
+oid sha256:65a6bdda88b09e060bb5689d2a076c5dbafa6dcae3314ad116f7a7087dbcdf5e
 size 2994018
 size 2994018

+ 1 - 1
Scripts/ExpectedScreenshots/StandardPBR/003_metalpolished.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:60103e51a9291a35a5bb508cc26104c7d847e4ba3ea36fc3f49f8b66c1a207ce
+oid sha256:21db28300fb478765b0b4e2c3835ee1920c4b583fc69b09452d8c7bd652b4003
 size 2994018
 size 2994018

+ 1 - 1
Scripts/ExpectedScreenshots/StandardPBR/006_specularf0map.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:1039a35819d6fe8e6db75a0c49b7974e8e8c29ad98f4f26772e185569a533159
+oid sha256:a2b447111f3301f82698a6a0599668410a0fa47d5798549dfd0a26e912e49bcc
 size 2994018
 size 2994018

+ 1 - 1
Scripts/ExpectedScreenshots/StandardPBR/007_multiscatteringcompensationoff.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:02157b113a057761fc539ef64354e437ec0b8495d750e97ccd898535f86f917a
+oid sha256:88ca9c797054724f4f803e9bcefc2f389c1342d9f1dcc499f43374e9279d3ff1
 size 2994018
 size 2994018

+ 1 - 1
Scripts/ExpectedScreenshots/StandardPBR/007_multiscatteringcompensationon.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:8886854b30100293b0f127ecc09fa8903ca15c9e84e4553c3d0827d0617b38b1
+oid sha256:ee0d241820e0a01bc4645aaedec4072c5f04b7542249d1a03db525ba561adebb
 size 2994018
 size 2994018

+ 1 - 1
Scripts/ExpectedScreenshots/StandardPBR/101_detailmaps_lucybasenodetailmaps.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:b1474a4f583c750c106990232eddd00f4fda565d9362c634a9a415d96cd1a7c8
+oid sha256:77b13e4bbb989a4e9d2302957e53298fdc5a3ac8f6f0ece40c016426634f20a4
 size 2994018
 size 2994018

+ 1 - 1
Scripts/ExpectedScreenshots/StandardPBR/102_detailmaps_all.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:bf81fd69e5fde3c52ec397dca1ac380c89d63cfd1f25d1be8444b8142823ac6e
+oid sha256:d4d1315b06fe5af2f737ef9cc34547e3cad73751fa2036b91a1beeba167e9e09
 size 2994018
 size 2994018

+ 1 - 1
Scripts/ExpectedScreenshots/StandardPBR/104_detailmaps_normal.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:634a962aa5483fce6982fe6d700116976bcb448b5794bf2ced1b8faf63797a4c
+oid sha256:307a495952910f3a5bf99141e4736ec2329cdb9c1bd67fafa0779fa4af651229
 size 2994018
 size 2994018

+ 1 - 1
Scripts/ExpectedScreenshots/StandardPBR/104_detailmaps_normalwithmask.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:99bcdb04714bffbe4fd2ef4884a4bb54cc10ddc5b6ce49ea04a589d403955446
+oid sha256:2f150709aabda4a6235472c1e41df3dddcda497ca460cdbcafdc0123a2fe32c5
 size 2994018
 size 2994018

+ 1 - 1
Scripts/ExpectedScreenshots/StandardPBR/105_detailmaps_blendmaskusingdetailuvs.ppm

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

+ 1 - 1
Scripts/ExpectedScreenshots/StreamingImage/HotReloading.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:bfef845daa90d417dda61bd561eb4c873018cba4139a2f75e531ff61322fe889
+oid sha256:08808a1ceb5bcae60210e803e27574a1cd4988b6e3cc9a1191b1bbaf714e8850
 size 2430015
 size 2430015

+ 1 - 1
Scripts/ExpectedScreenshots/StreamingImage/Streaming2dImages.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:244a00a8150a98e156982253bb70d82a7698dd6eaeaf0a48b18e47d6e9c7f48b
+oid sha256:fb73b8ff3c75b34ebe3bb775bf825abcc6f4d268f75d9f8a2c3f6ce86ba91a75
 size 2430015
 size 2430015

+ 1 - 1
Scripts/ExpectedScreenshots/StreamingImage/Streaming3dImage.ppm

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:72ab23928c063b643185d04e6dd95282dae6bd8e5dba23d7e0a6493664e889bb
+oid sha256:00d7c2413d1dc2870f7cb21e9eb96ab15ae8b77b382015e19667b4bcb19d9e02
 size 2430015
 size 2430015

+ 1 - 1
Scripts/MSAA_RPI_Test.bv.lua

@@ -27,7 +27,7 @@ Print('Saving screenshots to ' .. NormalizePath(g_screenshotOutputFolder))
 OpenSample('RPI/MSAA')
 OpenSample('RPI/MSAA')
 ResizeViewport(800, 600)
 ResizeViewport(800, 600)
 
 
-SelectImageComparisonToleranceLevel("Level F")
+SelectImageComparisonToleranceLevel("Level G")
 TakeScreenShot4xCylinder()
 TakeScreenShot4xCylinder()
 
 
 OpenSample(nil)
 OpenSample(nil)

+ 12 - 12
Scripts/MaterialScreenshotTests.bv.lua

@@ -48,9 +48,9 @@ function GenerateMaterialScreenshot(imageComparisonThresholdLevel, materialName,
     SetImguiValue('Materials/##Available', g_testMaterialsFolder .. materialName .. '.azmaterial')
     SetImguiValue('Materials/##Available', g_testMaterialsFolder .. materialName .. '.azmaterial')
 
 
     if (options.lighting ~= nil) then
     if (options.lighting ~= nil) then
-        SetImguiValue('Lighting Preset##SampleBase', options.lighting)
+        SetImguiValue('Lighting Preset##SampleBase/' .. options.lighting, true)
     else
     else
-        SetImguiValue('Lighting Preset##SampleBase', "Neutral Urban (Alt)")
+        SetImguiValue('Lighting Preset##SampleBase/Neutral Urban (Alt)', true)
     end
     end
 
 
     -- The sample resets the camera position after loading the model and we need to
     -- The sample resets the camera position after loading the model and we need to
@@ -91,13 +91,13 @@ GenerateMaterialScreenshot('Level B', '001_DefaultWhite')
 GenerateMaterialScreenshot('Level G', '002_BaseColorLerp')
 GenerateMaterialScreenshot('Level G', '002_BaseColorLerp')
 GenerateMaterialScreenshot('Level H', '002_BaseColorLinearLight')
 GenerateMaterialScreenshot('Level H', '002_BaseColorLinearLight')
 GenerateMaterialScreenshot('Level G', '002_BaseColorMultiply')
 GenerateMaterialScreenshot('Level G', '002_BaseColorMultiply')
-GenerateMaterialScreenshot('Level B', '003_MetalMatte')
-GenerateMaterialScreenshot('Level C', '003_MetalPolished')
+GenerateMaterialScreenshot('Level E', '003_MetalMatte')
+GenerateMaterialScreenshot('Level F', '003_MetalPolished')
 GenerateMaterialScreenshot('Level E', '004_MetalMap')
 GenerateMaterialScreenshot('Level E', '004_MetalMap')
 GenerateMaterialScreenshot('Level E', '005_RoughnessMap')
 GenerateMaterialScreenshot('Level E', '005_RoughnessMap')
-GenerateMaterialScreenshot('Level C', '006_SpecularF0Map')
-GenerateMaterialScreenshot('Level B', '007_MultiscatteringCompensationOff')
-GenerateMaterialScreenshot('Level B', '007_MultiscatteringCompensationOn')
+GenerateMaterialScreenshot('Level E', '006_SpecularF0Map')
+GenerateMaterialScreenshot('Level C', '007_MultiscatteringCompensationOff')
+GenerateMaterialScreenshot('Level C', '007_MultiscatteringCompensationOn')
 GenerateMaterialScreenshot('Level H', '008_NormalMap')
 GenerateMaterialScreenshot('Level H', '008_NormalMap')
 GenerateMaterialScreenshot('Level E', '008_NormalMap_Bevels')
 GenerateMaterialScreenshot('Level E', '008_NormalMap_Bevels')
 GenerateMaterialScreenshot('Level F', '009_Opacity_Blended', {lighting="Neutral Urban", model=g_beveledCubeModel})
 GenerateMaterialScreenshot('Level F', '009_Opacity_Blended', {lighting="Neutral Urban", model=g_beveledCubeModel})
@@ -134,7 +134,7 @@ GenerateMaterialScreenshot('Level L', '100_UvTiling_Parallax_B', { uniqueSuffix=
 GenerateMaterialScreenshot('Level H', '100_UvTiling_Roughness')
 GenerateMaterialScreenshot('Level H', '100_UvTiling_Roughness')
 GenerateMaterialScreenshot('Level F', '100_UvTiling_SpecularF0')
 GenerateMaterialScreenshot('Level F', '100_UvTiling_SpecularF0')
 
 
-GenerateMaterialScreenshot('Level C', '101_DetailMaps_LucyBaseNoDetailMaps',         {model=g_modelLucy, cameraHeading=175.0, cameraPitch=5.0, cameraDistance=0.75, cameraZ=0.5})
+GenerateMaterialScreenshot('Level E', '101_DetailMaps_LucyBaseNoDetailMaps',         {model=g_modelLucy, cameraHeading=175.0, cameraPitch=5.0, cameraDistance=0.75, cameraZ=0.5})
 GenerateMaterialScreenshot('Level F', '102_DetailMaps_All',                          {model=g_modelLucy, cameraHeading=175.0, cameraPitch=5.0, cameraDistance=0.75, cameraZ=0.5})
 GenerateMaterialScreenshot('Level F', '102_DetailMaps_All',                          {model=g_modelLucy, cameraHeading=175.0, cameraPitch=5.0, cameraDistance=0.75, cameraZ=0.5})
 GenerateMaterialScreenshot('Level F', '103_DetailMaps_BaseColor',                    {model=g_modelLucy, cameraHeading=175.0, cameraPitch=5.0, cameraDistance=0.75, cameraZ=0.5})
 GenerateMaterialScreenshot('Level F', '103_DetailMaps_BaseColor',                    {model=g_modelLucy, cameraHeading=175.0, cameraPitch=5.0, cameraDistance=0.75, cameraZ=0.5})
 GenerateMaterialScreenshot('Level F', '103_DetailMaps_BaseColorWithMask',            {model=g_modelLucy, cameraHeading=175.0, cameraPitch=5.0, cameraDistance=0.75, cameraZ=0.5})
 GenerateMaterialScreenshot('Level F', '103_DetailMaps_BaseColorWithMask',            {model=g_modelLucy, cameraHeading=175.0, cameraPitch=5.0, cameraDistance=0.75, cameraZ=0.5})
@@ -165,7 +165,7 @@ Print('Saving screenshots to ' .. NormalizePath(g_screenshotOutputFolder))
 
 
 -- We should eventually replace this with realistic skin test cases, these are just placeholders for regression testing
 -- We should eventually replace this with realistic skin test cases, these are just placeholders for regression testing
 GenerateMaterialScreenshot('Level D', '001_lucy_regression_test',    {model=g_modelLucy, cameraHeading=-26.0, cameraPitch=15.0, cameraDistance=2.0, cameraZ=0.7})
 GenerateMaterialScreenshot('Level D', '001_lucy_regression_test',    {model=g_modelLucy, cameraHeading=-26.0, cameraPitch=15.0, cameraDistance=2.0, cameraZ=0.7})
-GenerateMaterialScreenshot('Level D', '002_wrinkle_regression_test', {model=g_modelWithLayerMask, cameraHeading=-135.0, cameraPitch=15.0, cameraZ=-0.3, cameraDistance=3.5})
+GenerateMaterialScreenshot('Level E', '002_wrinkle_regression_test', {model=g_modelWithLayerMask, cameraHeading=-135.0, cameraPitch=15.0, cameraZ=-0.3, cameraDistance=3.5})
 
 
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 -- MinimalPBR Materials...
 -- MinimalPBR Materials...
@@ -175,8 +175,8 @@ g_screenshotOutputFolder = ResolvePath('@user@/Scripts/Screenshots/MinimalPBR/')
 Print('Saving screenshots to ' .. NormalizePath(g_screenshotOutputFolder))
 Print('Saving screenshots to ' .. NormalizePath(g_screenshotOutputFolder))
 
 
 GenerateMaterialScreenshot('Level B', 'MinimalPbr_Default')
 GenerateMaterialScreenshot('Level B', 'MinimalPbr_Default')
-GenerateMaterialScreenshot('Level C', 'MinimalPbr_BlueMetal')
-GenerateMaterialScreenshot('Level B', 'MinimalPbr_RedDielectric')
+GenerateMaterialScreenshot('Level F', 'MinimalPbr_BlueMetal')
+GenerateMaterialScreenshot('Level D', 'MinimalPbr_RedDielectric')
 
 
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 -- AutoBrick Materials...
 -- AutoBrick Materials...
@@ -187,4 +187,4 @@ Print('Saving screenshots to ' .. NormalizePath(g_screenshotOutputFolder))
 g_testMaterialsFolder = 'testdata/materials/autobrick/'
 g_testMaterialsFolder = 'testdata/materials/autobrick/'
 
 
 GenerateMaterialScreenshot('Level C', 'Brick', {model=g_cubeModel})
 GenerateMaterialScreenshot('Level C', 'Brick', {model=g_cubeModel})
-GenerateMaterialScreenshot('Level C', 'Tile', {model=g_cubeModel})
+GenerateMaterialScreenshot('Level D', 'Tile', {model=g_cubeModel})

+ 1 - 1
Scripts/StreamingImageTest.bv.lua

@@ -18,7 +18,7 @@ Print('Saving screenshots to ' .. NormalizePath(g_screenshotOutputFolder))
 OpenSample('RPI/StreamingImage')
 OpenSample('RPI/StreamingImage')
 ResizeViewport(900, 900)
 ResizeViewport(900, 900)
 
 
-SelectImageComparisonToleranceLevel("Level C")
+SelectImageComparisonToleranceLevel("Level H")
 
 
 -- capture screenshot with all 2d images
 -- capture screenshot with all 2d images
 CaptureScreenshot(g_screenshotOutputFolder .. 'Streaming2dImages.ppm')
 CaptureScreenshot(g_screenshotOutputFolder .. 'Streaming2dImages.ppm')

+ 21 - 0
Shaders/RayTracing/RTAOClosestHit.azsl

@@ -0,0 +1,21 @@
+/*
+* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
+* its licensors.
+*
+* For complete copyright and license terms please see the LICENSE at the root of this
+* distribution (the "License"). All use of this software is governed by the License,
+* or, if provided, by the license below or the license accompanying this file. Do not
+* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*
+*/
+
+#include "RTAODefines.azsli"
+
+// We have to have a hit shader otherwise the code may crash if the hit group is empty
+[shader("closesthit")] 
+void AoClosestHit(inout AORayPayload payload, BuiltInTriangleIntersectionAttributes attr) 
+{
+}
+
+

+ 19 - 0
Shaders/RayTracing/RTAOClosestHit.shader

@@ -0,0 +1,19 @@
+{
+    "Source" : "RTAOClosestHit",
+    "DrawList" : "RayTracing",
+
+    "CompilerHints":
+    {
+        "DxcAdditionalFreeArguments" : "-fspv-target-env=vulkan1.2"
+    },
+
+    "ProgramSettings":
+    {
+        "EntryPoints":
+        [
+            {
+                "type": "RayTracing"
+            }
+        ]
+    }
+}

+ 16 - 0
Shaders/RayTracing/RTAODefines.azsli

@@ -0,0 +1,16 @@
+/*
+* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
+* its licensors.
+*
+* For complete copyright and license terms please see the LICENSE at the root of this
+* distribution (the "License"). All use of this software is governed by the License,
+* or, if provided, by the license below or the license accompanying this file. Do not
+* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*
+*/
+
+struct AORayPayload 
+{
+    float aoValue;  // Stores 0 on a ray hit, 1 on ray miss
+};

+ 140 - 0
Shaders/RayTracing/RTAOGeneration.azsl

@@ -0,0 +1,140 @@
+/*
+* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
+* its licensors.
+*
+* For complete copyright and license terms please see the LICENSE at the root of this
+* distribution (the "License"). All use of this software is governed by the License,
+* or, if provided, by the license below or the license accompanying this file. Do not
+* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*
+*/
+
+#include <Atom/Features/SrgSemantics.azsli>
+#include <Atom/RPI/Math.azsli> // PI
+
+#include "RTAODefines.azsli"
+
+// GlobalSrg
+ShaderResourceGroup RayTracingGlobalSrg : SRG_RayTracingGlobal
+{
+    RaytracingAccelerationStructure m_scene;
+
+    Texture2D<float> m_depth;
+    Texture2D<float4> m_worldNormalMap;
+
+    RWTexture2D<float4> m_outputAO;
+
+    float m_aoRadius;   // Ambient occlusion radius. Default: 0.4f
+    float m_rayMinT;    // The minimum ray extent
+    int m_frameCount;   // Used for unique random seeds each frame. Default: 0
+    uint  m_numRays;    // Number of ray casted for each pixel
+
+    // Copy of ViewSrg::m_viewProjectionInverseMatrix since we can't access ViewSrg in ray tracing shaders ATM.
+    row_major float4x4 m_viewProjectionInverseMatrix;
+};
+
+// Generates a seed for RNG from 2 input values
+uint InitRandomSeed(uint value1, uint value2)
+{
+    uint v0 = value1, v1 = value2, s0 = 0;
+
+    [unroll]
+    for (uint n = 0; n < 16; n++)
+    {
+        s0 += 0x9e3779b9;
+        v0 += ((v1 << 4) + 0xa341316c) ^ (v1 + s0) ^ ((v1 >> 5) + 0xc8013ea4);
+        v1 += ((v0 << 4) + 0xad90777d) ^ (v0 + s0) ^ ((v0 >> 5) + 0x7e95761e);
+    }
+    return v0;
+}
+
+// Get a vector perpendicular to an input vector 
+// "Efficient Construction of Perpendicular Vectors Without Branching"
+float3 GetPerpendicularVector(float3 input)
+{
+    float3 absInput = abs(input);
+    uint x = ((absInput.x < absInput.y) && (absInput.x < absInput.z)) ? 1 : 0;
+    uint y = (absInput.y < absInput.z)? (1 ^ x) : 0;
+    uint z = 1 ^ (x | y);
+    return normalize(cross(input, float3(x, y, z)));
+}
+
+// Get a cosine-weighted random vector centered around a specified normal direction.
+float3 GetCosHemisphereSample(inout uint randSeed, float3 hitNorm)
+{
+    // Get 2 random numbers to select our sample with
+    float2 randVal = float2(NextRandomFloatUniform(randSeed), NextRandomFloatUniform(randSeed));
+
+    float3 bitangent = GetPerpendicularVector(hitNorm);
+    float3 tangent = cross(bitangent, hitNorm);
+
+    float radius = sqrt(randVal.x);
+    float angle = 2.0f * PI * randVal.y;
+
+    // Get our cosine-weighted hemisphere lobe sample direction
+    return tangent * (radius * cos(angle)) + bitangent * (radius * sin(angle)) + hitNorm.xyz * sqrt(1 - randVal.x);
+}
+
+float ShootRay(float3 orig, float3 dir, float minT, float maxT)
+{
+    // Setup AO payload. Set the default to 0.0f (assume it's a hit)
+    AORayPayload  rayPayload = { 0.0f };
+
+    RayDesc rayAO = { orig, minT, dir, maxT };
+
+    // Skip closest-hit shader and stop as soon as any intersection found
+    uint rayFlags = RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER;
+
+    TraceRay(RayTracingGlobalSrg::m_scene, rayFlags, 0xFF, 0, 1, 0, rayAO, rayPayload );
+
+    return rayPayload.aoValue;
+}
+
+[shader("raygeneration")]
+void AoRayGen()
+{
+    // Where this thread's ray is on the screen
+    uint2 launchIndex = DispatchRaysIndex().xy;
+    uint2 launchDim   = DispatchRaysDimensions().xy;
+
+    // Initialize a random seed, per-pixel, based on a screen position and temporally varying count
+    uint randSeed = InitRandomSeed(launchIndex.x + launchIndex.y * launchDim.x, RayTracingGlobalSrg::m_frameCount);
+
+    // Get world position from screen position and depth
+    float depth = RayTracingGlobalSrg::m_depth.Load(uint3(launchIndex, 0));
+    // Position in native device coordinate
+    float2 ndcPos = float2((float)launchIndex.x/(float)launchDim.x, 1.0f - (float)launchIndex.y/(float)launchDim.y) * 2.0f - 1.0f;
+    float4 projectedPos = float4(ndcPos, depth, 1.0f);
+    float4 worldPos = mul(RayTracingGlobalSrg::m_viewProjectionInverseMatrix, projectedPos);
+    worldPos /= worldPos.w;
+
+    float4 encodedNormal = RayTracingGlobalSrg::m_worldNormalMap.Load(uint3(launchIndex, 0));
+    float3 worldNorm = DecodeNormalSignedOctahedron(encodedNormal.rgb);
+
+    // Default ambient occlusion value if it hits the background
+    float ambientOcclusion = 1.0f;
+
+    // depth == 0 for background pixels; only shoot AO rays elsewhere
+    if (depth != 0.0f)  
+    {
+        // Start accumulating from zero if we don't hit the background
+        ambientOcclusion = 0.0f;
+
+        for (int i = 0; i < RayTracingGlobalSrg::m_numRays; i++)
+        {
+            // Sample cosine-weighted hemisphere around surface normal to pick a random ray direction
+            float3 worldDir = GetCosHemisphereSample(randSeed, worldNorm.xyz);
+
+            // Shoot our ambient occlusion ray and update the value we'll output with the result
+            float minT = RayTracingGlobalSrg::m_rayMinT;
+            float maxT = RayTracingGlobalSrg::m_aoRadius;
+            ambientOcclusion += ShootRay(worldPos.xyz, worldDir, minT, maxT);
+        }
+        ambientOcclusion = ambientOcclusion / float(RayTracingGlobalSrg::m_numRays);
+    }
+    
+    // Save out AO color
+    RayTracingGlobalSrg::m_outputAO[launchIndex].rgb = ambientOcclusion;
+    RayTracingGlobalSrg::m_outputAO[launchIndex].a = 1.0f;
+}

+ 19 - 0
Shaders/RayTracing/RTAOGeneration.shader

@@ -0,0 +1,19 @@
+{
+    "Source" : "RTAOGeneration",
+    "DrawList" : "RayTracing",
+
+    "CompilerHints":
+    {
+        "DxcAdditionalFreeArguments" : "-fspv-target-env=vulkan1.2"
+    }, 
+
+    "ProgramSettings":
+    {
+        "EntryPoints":
+        [
+            {
+                "type": "RayTracing"
+            }
+        ]
+    }
+}

+ 23 - 0
Shaders/RayTracing/RTAOMiss.azsl

@@ -0,0 +1,23 @@
+/*
+* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
+* its licensors.
+*
+* For complete copyright and license terms please see the LICENSE at the root of this
+* distribution (the "License"). All use of this software is governed by the License,
+* or, if provided, by the license below or the license accompanying this file. Do not
+* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*
+*/
+
+#include "RTAODefines.azsli"
+
+// miss shader that runs when a ray does not intersect anything in the scene
+// Set the ao value to 1 which means illuminated
+[shader("miss")]
+void AoMiss(inout AORayPayload rayData)
+{
+    rayData.aoValue = 1.0f;
+}
+
+

+ 19 - 0
Shaders/RayTracing/RTAOMiss.shader

@@ -0,0 +1,19 @@
+{
+    "Source" : "RTAOMiss",
+    "DrawList" : "RayTracing",
+
+    "CompilerHints":
+    {
+        "DxcAdditionalFreeArguments" : "-fspv-target-env=vulkan1.2"
+    },
+
+    "ProgramSettings":
+    {
+        "EntryPoints":
+        [
+            {
+                "type": "RayTracing"
+            }
+        ]
+    }
+}

+ 10 - 0
Standalone/PythonTests/Automated/test_suites/main/__init__.py

@@ -0,0 +1,10 @@
+"""
+All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
+its licensors.
+
+For complete copyright and license terms please see the LICENSE at the root of this
+distribution (the "License"). All use of this software is governed by the License,
+or, if provided, by the license below or the license accompanying this file. Do not
+remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+"""

+ 74 - 0
Standalone/PythonTests/Automated/test_suites/main/test_AtomSampleViewer_main_dx12.py

@@ -0,0 +1,74 @@
+"""
+All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
+its licensors.
+
+For complete copyright and license terms please see the LICENSE at the root of this
+distribution (the "License"). All use of this software is governed by the License,
+or, if provided, by the license below or the license accompanying this file. Do not
+remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+"""
+import logging
+import os
+import subprocess
+import types
+
+import pytest
+
+import ly_test_tools.environment.process_utils as process_utils
+import ly_test_tools.launchers.platforms.base
+import ly_test_tools.log.log_monitor
+
+RENDER_HARDWARE_INTERFACE = 'dx12'
+logger = logging.getLogger(__name__)
+
+
+class AtomSampleViewerException(Exception):
+    """Custom Exception class for AtomSampleViewer tests."""
+    pass
+
+
[email protected]('launcher_platform', ['windows'])
[email protected]("project", ["AtomSampleViewer"])
[email protected]("setup_atomsampleviewer_assets", "clean_atomsampleviewer_logs")
+class TestDX12AutomationMainSuite:
+
+    @pytest.mark.test_case_id('C35638245')
+    def test_C35638245_dx12_CullingAndLod(self, request, workspace, launcher_platform):
+        # Script call setup.
+        test_script = 'CullingAndLod.bv.luac'
+        cmd = os.path.join(workspace.paths.build_directory(),
+                           'AtomSampleViewerStandalone.exe '
+                           f'--rhi {RENDER_HARDWARE_INTERFACE} '
+                           f'--runtestsuite scripts/{test_script} '
+                           '--exitontestend')
+
+        def teardown():
+            process_utils.kill_processes_named(['AssetProcessor', 'AtomSampleViewerStandalone'], ignore_extensions=True)
+
+        request.addfinalizer(teardown)
+
+        # Log monitor setup.
+        def is_alive(launcher_name):
+            return True
+        launcher = ly_test_tools.launchers.platforms.base.Launcher(workspace, [])  # Needed for log monitor to work.
+        launcher.is_alive = types.MethodType(is_alive, launcher)
+        file_to_monitor = os.path.join(workspace.paths.project_log(), 'atomsampleviewer.log')
+        log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher, log_file_path=file_to_monitor)
+
+        # Execute test.
+        process_utils.safe_check_call(cmd, stderr=subprocess.STDOUT, encoding='UTF-8', shell=True)
+        try:
+            unexpected_lines = ["Script: Screenshot check failed. Diff score"]  # "Diff score" ensures legit failure.
+            log_monitor.monitor_log_for_lines(unexpected_lines=unexpected_lines, halt_on_unexpected=True, timeout=5)
+        except ly_test_tools.log.log_monitor.LogMonitorException as e:
+            expected_screenshots_path = os.path.join(
+                workspace.paths.dev(), "AtomSampleViewer", "Scripts", "ExpectedScreenshots")
+            test_screenshots_path = os.path.join(
+                workspace.paths.project_cache(), "pc", "user", "Scripts", "Screenshots")
+            raise AtomSampleViewerException(
+                f"Got error: {e}\n"
+                "Screenshot comparison check failed. Please review logs and screenshots at:\n"
+                f"Log file: {file_to_monitor}\n"
+                f"Expected screenshots: {expected_screenshots_path}\n"
+                f"Test screenshots: {test_screenshots_path}\n")

+ 10 - 0
Standalone/PythonTests/Automated/test_suites/periodic/__init__.py

@@ -0,0 +1,10 @@
+"""
+All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
+its licensors.
+
+For complete copyright and license terms please see the LICENSE at the root of this
+distribution (the "License"). All use of this software is governed by the License,
+or, if provided, by the license below or the license accompanying this file. Do not
+remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+"""

+ 48 - 0
Standalone/PythonTests/Automated/test_suites/periodic/test_AtomSampleViewer_main_vulkan.py

@@ -0,0 +1,48 @@
+"""
+All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
+its licensors.
+
+For complete copyright and license terms please see the LICENSE at the root of this
+distribution (the "License"). All use of this software is governed by the License,
+or, if provided, by the license below or the license accompanying this file. Do not
+remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+"""
+import logging
+import os
+import subprocess
+
+import pytest
+
+import ly_test_tools.environment.process_utils as process_utils
+
+RENDER_HARDWARE_INTERFACE = 'vulkan'
+logger = logging.getLogger(__name__)
+
+
+def teardown():
+    process_utils.kill_processes_named(['AssetProcessor', 'AtomSampleViewerStandalone'], ignore_extensions=True)
+
+
[email protected]('launcher_platform', ['windows'])
[email protected]("project", ["AtomSampleViewer"])
[email protected]("setup_atomsampleviewer_assets", "clean_atomsampleviewer_logs")
+class TestVulkanAutomationMainSuite:
+
+    @pytest.mark.test_case_id('C35638265')
+    def test_C35638265_vulkan_CullingAndLod(self, request, workspace, editor, launcher_platform):
+        test_script = 'CullingAndLod.bv.luac'
+        cmd = os.path.join(workspace.paths.build_directory(),
+                           'AtomSampleViewerStandalone.exe '
+                           f'--rhi {RENDER_HARDWARE_INTERFACE} '
+                           f'--runtestsuite scripts/{test_script} '
+                           '--exitontestend')
+        request.addfinalizer(teardown)
+
+        try:
+            return_code = process_utils.check_call(cmd, stderr=subprocess.STDOUT, encoding='UTF-8', shell=True)
+            logger.debug(f"AtomSampleViewer {test_script} test command got response return code : {return_code}")
+            assert return_code == 0
+        except subprocess.CalledProcessError as e:
+            logger.error(f'AtomSampleViewer lua test "{test_script}" had a failure.\n')
+            raise e

+ 2 - 21
Standalone/PythonTests/Automated/test_AtomSampleViewer_dx12.py → Standalone/PythonTests/Automated/test_suites/periodic/test_AtomSampleViewer_periodic_dx12.py

@@ -17,7 +17,6 @@ import pytest
 
 
 import ly_test_tools.environment.process_utils as process_utils
 import ly_test_tools.environment.process_utils as process_utils
 
 
-pytest.importorskip("ly_test_tools")  # Bail if LyTT isn't installed.
 RENDER_HARDWARE_INTERFACE = 'dx12'
 RENDER_HARDWARE_INTERFACE = 'dx12'
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
@@ -28,8 +27,8 @@ def teardown():
 
 
 @pytest.mark.parametrize('launcher_platform', ['windows'])
 @pytest.mark.parametrize('launcher_platform', ['windows'])
 @pytest.mark.parametrize("project", ["AtomSampleViewer"])
 @pytest.mark.parametrize("project", ["AtomSampleViewer"])
[email protected]("automatic_process_killer")
-class TestDX12Automation:
[email protected]("setup_atomsampleviewer_assets", "clean_atomsampleviewer_logs")
+class TestDX12AutomationPeriodicSuite:
 
 
     @pytest.mark.test_case_id('C35638262')
     @pytest.mark.test_case_id('C35638262')
     def test_C35638262_dx12_FullTestSuite(self, request, workspace, editor, launcher_platform):
     def test_C35638262_dx12_FullTestSuite(self, request, workspace, editor, launcher_platform):
@@ -85,24 +84,6 @@ class TestDX12Automation:
             logger.error(f'AtomSampleViewer lua test "{test_script}" had a failure.\n')
             logger.error(f'AtomSampleViewer lua test "{test_script}" had a failure.\n')
             raise e
             raise e
 
 
-    @pytest.mark.test_case_id('C35638245')
-    def test_C35638245_dx12_CullingAndLod(self, request, workspace, editor, launcher_platform):
-        test_script = 'CullingAndLod.bv.luac'
-        cmd = os.path.join(workspace.paths.build_directory(),
-                           'AtomSampleViewerStandalone.exe '
-                           f'--rhi {RENDER_HARDWARE_INTERFACE} '
-                           f'--runtestsuite scripts/{test_script} '
-                           '--exitontestend')
-        request.addfinalizer(teardown)
-
-        try:
-            return_code = process_utils.check_call(cmd, stderr=subprocess.STDOUT, encoding='UTF-8', shell=True)
-            logger.debug(f"AtomSampleViewer {test_script} test command got response return code : {return_code}")
-            assert return_code == 0
-        except subprocess.CalledProcessError as e:
-            logger.error(f'AtomSampleViewer lua test "{test_script}" had a failure.\n')
-            raise e
-
     @pytest.mark.test_case_id('C35638246')
     @pytest.mark.test_case_id('C35638246')
     def test_C35638246_dx12_Decals(self, request, workspace, editor, launcher_platform):
     def test_C35638246_dx12_Decals(self, request, workspace, editor, launcher_platform):
         test_script = 'Decals.bv.luac'
         test_script = 'Decals.bv.luac'

+ 2 - 21
Standalone/PythonTests/Automated/test_AtomSampleViewer_vulkan.py → Standalone/PythonTests/Automated/test_suites/periodic/test_AtomSampleViewer_periodic_vulkan.py

@@ -17,7 +17,6 @@ import pytest
 
 
 import ly_test_tools.environment.process_utils as process_utils
 import ly_test_tools.environment.process_utils as process_utils
 
 
-pytest.importorskip("ly_test_tools")  # Bail if LyTT isn't installed.
 RENDER_HARDWARE_INTERFACE = 'vulkan'
 RENDER_HARDWARE_INTERFACE = 'vulkan'
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
@@ -28,8 +27,8 @@ def teardown():
 
 
 @pytest.mark.parametrize('launcher_platform', ['windows'])
 @pytest.mark.parametrize('launcher_platform', ['windows'])
 @pytest.mark.parametrize("project", ["AtomSampleViewer"])
 @pytest.mark.parametrize("project", ["AtomSampleViewer"])
[email protected]("automatic_process_killer")
-class TestVulkanAutomation:
[email protected]("setup_atomsampleviewer_assets", "clean_atomsampleviewer_logs")
+class TestVulkanAutomationPeriodicSuite:
 
 
     @pytest.mark.test_case_id('C35638262')
     @pytest.mark.test_case_id('C35638262')
     def test_C35638262_vulkan_FullTestSuite(self, request, workspace, editor, launcher_platform):
     def test_C35638262_vulkan_FullTestSuite(self, request, workspace, editor, launcher_platform):
@@ -85,24 +84,6 @@ class TestVulkanAutomation:
             logger.error(f'AtomSampleViewer lua test "{test_script}" had a failure.\n')
             logger.error(f'AtomSampleViewer lua test "{test_script}" had a failure.\n')
             raise e
             raise e
 
 
-    @pytest.mark.test_case_id('C35638265')
-    def test_C35638265_vulkan_CullingAndLod(self, request, workspace, editor, launcher_platform):
-        test_script = 'CullingAndLod.bv.luac'
-        cmd = os.path.join(workspace.paths.build_directory(),
-                           'AtomSampleViewerStandalone.exe '
-                           f'--rhi {RENDER_HARDWARE_INTERFACE} '
-                           f'--runtestsuite scripts/{test_script} '
-                           '--exitontestend')
-        request.addfinalizer(teardown)
-
-        try:
-            return_code = process_utils.check_call(cmd, stderr=subprocess.STDOUT, encoding='UTF-8', shell=True)
-            logger.debug(f"AtomSampleViewer {test_script} test command got response return code : {return_code}")
-            assert return_code == 0
-        except subprocess.CalledProcessError as e:
-            logger.error(f'AtomSampleViewer lua test "{test_script}" had a failure.\n')
-            raise e
-
     @pytest.mark.test_case_id('C35638266')
     @pytest.mark.test_case_id('C35638266')
     def test_C35638266_vulkan_Decals(self, request, workspace, editor, launcher_platform):
     def test_C35638266_vulkan_Decals(self, request, workspace, editor, launcher_platform):
         test_script = 'Decals.bv.luac'
         test_script = 'Decals.bv.luac'

+ 10 - 0
Standalone/PythonTests/Automated/test_suites/smoke/__init__.py

@@ -0,0 +1,10 @@
+"""
+All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
+its licensors.
+
+For complete copyright and license terms please see the LICENSE at the root of this
+distribution (the "License"). All use of this software is governed by the License,
+or, if provided, by the license below or the license accompanying this file. Do not
+remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+"""

+ 12 - 0
Standalone/PythonTests/Automated/test_suites/smoke/test_AtomSampleViewer_smoke_dx12.py

@@ -0,0 +1,12 @@
+"""
+All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
+its licensors.
+
+For complete copyright and license terms please see the LICENSE at the root of this
+distribution (the "License"). All use of this software is governed by the License,
+or, if provided, by the license below or the license accompanying this file. Do not
+remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+"""
+
+# To be determined.

+ 12 - 0
Standalone/PythonTests/Automated/test_suites/smoke/test_AtomSampleViewer_smoke_vulkan.py

@@ -0,0 +1,12 @@
+"""
+All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
+its licensors.
+
+For complete copyright and license terms please see the LICENSE at the root of this
+distribution (the "License"). All use of this software is governed by the License,
+or, if provided, by the license below or the license accompanying this file. Do not
+remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+"""
+
+# To be determined.

+ 18 - 3
Standalone/PythonTests/CMakeLists.txt

@@ -16,14 +16,29 @@
 
 
 if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED)
 if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED)
     ly_add_pytest(
     ly_add_pytest(
-        NAME AtomSampleViewer::PythonTests
-        PATH ${CMAKE_CURRENT_LIST_DIR}/Automated/
+        NAME AtomSampleViewer::PythonTestsMain
+        PATH ${CMAKE_CURRENT_LIST_DIR}/Automated/test_suites/main/
+        TEST_REQUIRES gpu
+        TEST_SUITE main
+        TEST_SERIAL
+        TIMEOUT 1200
+        RUNTIME_DEPENDENCIES
+            AssetProcessor
+            AssetProcessorBatch
+            AtomSampleViewerStandalone
+            AtomSampleViewer.GameLauncher
+            AtomSampleViewer.Assets
+    )
+    ly_add_pytest(
+        NAME AtomSampleViewer::PythonTestsMainPeriodic
+        PATH ${CMAKE_CURRENT_LIST_DIR}/Automated/test_suites/periodic/
         TEST_REQUIRES gpu
         TEST_REQUIRES gpu
         TEST_SUITE periodic
         TEST_SUITE periodic
         TEST_SERIAL
         TEST_SERIAL
-        TIMEOUT 3600
+        TIMEOUT 1200
         RUNTIME_DEPENDENCIES
         RUNTIME_DEPENDENCIES
             AssetProcessor
             AssetProcessor
+            AssetProcessorBatch
             AtomSampleViewerStandalone
             AtomSampleViewerStandalone
             AtomSampleViewer.GameLauncher
             AtomSampleViewer.GameLauncher
             AtomSampleViewer.Assets
             AtomSampleViewer.Assets

+ 11 - 13
Standalone/PythonTests/conftest.py

@@ -16,29 +16,27 @@ import os
 
 
 import pytest
 import pytest
 
 
+import ly_test_tools.builtin.helpers as helpers
 import ly_test_tools.environment.file_system as file_system
 import ly_test_tools.environment.file_system as file_system
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
 
 
-def pytest_addoption(parser):
-    parser.addoption("--clean-logs", action="store_true", default=False,
-                     help="Deletes any existing AssetProcessor, AtomSampleViewer, Editor, or Game log files.")
-
-
 @pytest.fixture(scope="function", autouse=True)
 @pytest.fixture(scope="function", autouse=True)
-def clean_logs(request, workspace):
-    enable_clean_logs = request.config.getoption('--clean-logs')
-    logs = ['AP_GUI.log', 'atomsampleviewer.log', 'Editor.log', 'Game.log']
-
-    if not enable_clean_logs:
-        logger.info(f'--clean-logs is "{enable_clean_logs}" - skipping the deletion of existing log files: {logs}')
-        return
+def clean_atomsampleviewer_logs(request, workspace):
+    """Deletes any AtomSampleViewer log files so that the test run can start with empty logs."""
+    logs = ['atomsampleviewer.log']
+    logger.info(f'Deleting log files for AtomSampleViewer tests: {logs}')
 
 
-    logger.info(f'--clean-logs is "{enable_clean_logs}" - deleting log files: {logs}')
     for log in logs:
     for log in logs:
         log_file = os.path.join(workspace.paths.project_log(), log)
         log_file = os.path.join(workspace.paths.project_log(), log)
         if os.path.exists(log_file):
         if os.path.exists(log_file):
             file_system.delete(file_list=[log_file],
             file_system.delete(file_list=[log_file],
                                del_files=True,
                                del_files=True,
                                del_dirs=False)
                                del_dirs=False)
+
+
[email protected](scope="function", autouse=True)
+def setup_atomsampleviewer_assets(project, workspace):
+    """Sets up bootstrap.cfg to target AtomSampleViewer before starting the tests (prevents AssetProcessor errors)."""
+    helpers.setup_bootstrap_project(workspace, project)