浏览代码

Merged main

antonmic 4 年之前
父节点
当前提交
a74d563eda
共有 47 个文件被更改,包括 1150 次插入1105 次删除
  1. 26 10
      Gem/Code/Source/DecalContainer.cpp
  2. 3 1
      Gem/Code/Source/DecalContainer.h
  3. 16 3
      Gem/Code/Source/DecalExampleComponent.cpp
  4. 3 0
      Gem/Code/Source/DecalExampleComponent.h
  5. 8 6
      Gem/Code/Source/Passes/RayTracingAmbientOcclusionPass.cpp
  6. 14 0
      Gem/Code/Source/Platform/Android/SceneReloadSoakTestComponent_Traits_Platform.h
  7. 1 0
      Gem/Code/Source/Platform/Android/atomsampleviewer_android_files.cmake
  8. 13 0
      Gem/Code/Source/Platform/Linux/SceneReloadSoakTestComponent_Traits_Platform.h
  9. 1 0
      Gem/Code/Source/Platform/Linux/atomsampleviewer_linux_files.cmake
  10. 13 0
      Gem/Code/Source/Platform/Mac/SceneReloadSoakTestComponent_Traits_Platform.h
  11. 1 0
      Gem/Code/Source/Platform/Mac/atomsampleviewer_mac_files.cmake
  12. 13 0
      Gem/Code/Source/Platform/Windows/SceneReloadSoakTestComponent_Traits_Platform.h
  13. 1 0
      Gem/Code/Source/Platform/Windows/atomsampleviewer_windows_files.cmake
  14. 13 0
      Gem/Code/Source/Platform/iOS/SceneReloadSoakTestComponent_Traits_Platform.h
  15. 1 0
      Gem/Code/Source/Platform/iOS/atomsampleviewer_ios_files.cmake
  16. 11 7
      Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.cpp
  17. 3 0
      Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.h
  18. 51 76
      Gem/Code/Source/RHI/RayTracingExampleComponent.cpp
  19. 1 2
      Gem/Code/Source/RHI/RayTracingExampleComponent.h
  20. 12 1
      Gem/Code/Source/SampleComponentManager.cpp
  21. 3 1
      Gem/Code/Source/SceneReloadSoakTestComponent.cpp
  22. 423 323
      Gem/Code/Source/ShadowExampleComponent.cpp
  23. 44 16
      Gem/Code/Source/ShadowExampleComponent.h
  24. 2 2
      Objects/bunny.fbx
  25. 2 2
      Objects/suzanne.fbx
  26. 117 0
      Passes/ASV/PassTemplates.azasset
  27. 0 609
      Passes/PassTemplates.azasset
  28. 3 1
      Scripts/Decals.bv.lua
  29. 3 0
      Scripts/ExpectedScreenshots/CopyQueue/screenshot_warp_CopyQueue.png
  30. 2 2
      Scripts/ExpectedScreenshots/Decals/screenshot_decals.png
  31. 3 0
      Scripts/ExpectedScreenshots/DualSourceBlending/screenshot_warp_DualSourceBlending.png
  32. 3 0
      Scripts/ExpectedScreenshots/InputAssembly/screenshot_warp_InputAssembly.png
  33. 3 0
      Scripts/ExpectedScreenshots/MSAA/screenshot_warp_MSAA.png
  34. 3 0
      Scripts/ExpectedScreenshots/MultiRenderTarget/screenshot_warp_MultiRenderTarget.png
  35. 3 0
      Scripts/ExpectedScreenshots/MultiThread/screenshot_warp_MultiThread.png
  36. 3 0
      Scripts/ExpectedScreenshots/Stencil/screenshot_warp_Stencil.png
  37. 3 0
      Scripts/ExpectedScreenshots/Texture/screenshot_warp_Texture.png
  38. 3 0
      Scripts/ExpectedScreenshots/Texture3d/screenshot_warp_Texture3d.png
  39. 3 0
      Scripts/ExpectedScreenshots/TextureMap/screenshot_warp_TextureMap.png
  40. 27 27
      Scripts/ShadowTest.bv.lua
  41. 48 0
      Scripts/_AutomatedReviewWARPTestSuite_.bv.lua
  42. 13 13
      Shaders/RHI/BindlessPrototype.azsl
  43. 136 0
      Shaders/RHI/BindlessPrototypeSrg.azsli
  44. 4 2
      Shaders/StaticMesh.azsl
  45. 70 0
      Standalone/PythonTests/Automated/test_AtomSampleViewer_warp_suite.py
  46. 14 1
      Standalone/PythonTests/CMakeLists.txt
  47. 7 0
      Textures/YokohamaCube.license.txt

+ 26 - 10
Gem/Code/Source/DecalContainer.cpp

@@ -34,8 +34,8 @@ namespace AtomSampleViewer
         };
         };
     }
     }
 
 
-    DecalContainer::DecalContainer(AZ::Render::DecalFeatureProcessorInterface* fp)
-        : m_decalFeatureProcessor(fp)
+    DecalContainer::DecalContainer(AZ::Render::DecalFeatureProcessorInterface* fp, const AZ::Vector3 position)
+        : m_decalFeatureProcessor(fp), m_position(position)
     {
     {
         SetupDecals();
         SetupDecals();
     }
     }
@@ -45,14 +45,14 @@ namespace AtomSampleViewer
         const float HalfLength = 0.25f;
         const float HalfLength = 0.25f;
         const float HalfProjectionDepth = 10.0f;
         const float HalfProjectionDepth = 10.0f;
         const AZ::Vector3 halfSize(HalfLength, HalfLength, HalfProjectionDepth);
         const AZ::Vector3 halfSize(HalfLength, HalfLength, HalfProjectionDepth);
-        SetupNewDecal(AZ::Vector3(-0.75f, -0.25f, 1), halfSize, DecalMaterialNames[0]);
-        SetupNewDecal(AZ::Vector3(-0.25f, -0.25f, 1), halfSize, DecalMaterialNames[1]);
-        SetupNewDecal(AZ::Vector3(0.25f, -0.25f, 1), halfSize, DecalMaterialNames[2]);
-        SetupNewDecal(AZ::Vector3(0.75f, -0.25f, 1), halfSize, DecalMaterialNames[3]);
-        SetupNewDecal(AZ::Vector3(-0.75f, 0.25f, 1), halfSize, DecalMaterialNames[4]);
-        SetupNewDecal(AZ::Vector3(-0.25f, 0.25f, 1), halfSize, DecalMaterialNames[5]);
-        SetupNewDecal(AZ::Vector3(0.25f, 0.25f, 1), halfSize, DecalMaterialNames[6]);
-        SetupNewDecal(AZ::Vector3(0.75f, 0.25f, 1), halfSize, DecalMaterialNames[7]);
+        SetupNewDecal(AZ::Vector3(-0.75f, -0.25f, 1) + m_position, halfSize, DecalMaterialNames[0]);
+        SetupNewDecal(AZ::Vector3(-0.25f, -0.25f, 1) + m_position, halfSize, DecalMaterialNames[1]);
+        SetupNewDecal(AZ::Vector3(0.25f, -0.25f, 1) + m_position, halfSize, DecalMaterialNames[2]);
+        SetupNewDecal(AZ::Vector3(0.75f, -0.25f, 1) + m_position, halfSize, DecalMaterialNames[3]);
+        SetupNewDecal(AZ::Vector3(-0.75f, 0.25f, 1) + m_position, halfSize, DecalMaterialNames[4]);
+        SetupNewDecal(AZ::Vector3(-0.25f, 0.25f, 1) + m_position, halfSize, DecalMaterialNames[5]);
+        SetupNewDecal(AZ::Vector3(0.25f, 0.25f, 1) + m_position, halfSize, DecalMaterialNames[6]);
+        SetupNewDecal(AZ::Vector3(0.75f, 0.25f, 1) + m_position, halfSize, DecalMaterialNames[7]);
     }
     }
 
 
     DecalContainer::~DecalContainer()
     DecalContainer::~DecalContainer()
@@ -113,4 +113,20 @@ namespace AtomSampleViewer
         m_decalFeatureProcessor->ReleaseDecal(decal.m_decalHandle);
         m_decalFeatureProcessor->ReleaseDecal(decal.m_decalHandle);
         decal.m_decalHandle = AZ::Render::DecalFeatureProcessorInterface::DecalHandle::Null;
         decal.m_decalHandle = AZ::Render::DecalFeatureProcessorInterface::DecalHandle::Null;
     }
     }
+
+    void DecalContainer::CloneFrom(const DecalContainer& containerToClone)
+    {
+        SetNumDecalsActive(0);
+        for (size_t i = 0; i < containerToClone.GetNumDecalsActive() ; ++i)
+        {
+            Decal& ourDecal = m_decals[i];
+            const Decal& otherDecal = containerToClone.m_decals[i];
+
+            ourDecal.m_decalHandle = m_decalFeatureProcessor->CloneDecal(otherDecal.m_decalHandle);
+
+            // Cloning sets the decal position to overlap the existing decal, lets move it so that it is visible
+            m_decalFeatureProcessor->SetDecalPosition(ourDecal.m_decalHandle, ourDecal.m_position);
+        }
+    }
+
 }
 }

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

@@ -24,7 +24,7 @@ namespace AtomSampleViewer
     {
     {
     public:
     public:
 
 
-        DecalContainer(AZ::Render::DecalFeatureProcessorInterface* fp);
+        DecalContainer(AZ::Render::DecalFeatureProcessorInterface* fp, const AZ::Vector3 position);
         DecalContainer(const DecalContainer&) = delete;
         DecalContainer(const DecalContainer&) = delete;
         DecalContainer& operator=(const DecalContainer&) = delete;
         DecalContainer& operator=(const DecalContainer&) = delete;
         ~DecalContainer();
         ~DecalContainer();
@@ -32,6 +32,7 @@ namespace AtomSampleViewer
         void SetNumDecalsActive(int numDecals);
         void SetNumDecalsActive(int numDecals);
         int GetMaxDecals() const { return aznumeric_cast<int>(m_decals.size()); }
         int GetMaxDecals() const { return aznumeric_cast<int>(m_decals.size()); }
         int GetNumDecalsActive() const { return m_numDecalsActive; }
         int GetNumDecalsActive() const { return m_numDecalsActive; }
+        void CloneFrom(const DecalContainer& containerToClone);
 
 
     private:
     private:
 
 
@@ -52,5 +53,6 @@ namespace AtomSampleViewer
         AZStd::vector<Decal> m_decals;
         AZStd::vector<Decal> m_decals;
         AZ::Render::DecalFeatureProcessorInterface* m_decalFeatureProcessor = nullptr;
         AZ::Render::DecalFeatureProcessorInterface* m_decalFeatureProcessor = nullptr;
         int m_numDecalsActive = 0;
         int m_numDecalsActive = 0;
+        AZ::Vector3 m_position;
     };
     };
 } // namespace AtomSampleViewer
 } // namespace AtomSampleViewer

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

@@ -87,8 +87,8 @@ namespace AtomSampleViewer
 
 
     void DecalExampleComponent::ScaleObjectToFitDecals()
     void DecalExampleComponent::ScaleObjectToFitDecals()
     {
     {
-        const AZ::Transform doubleSize = AZ::Transform::CreateScale(AZ::Vector3(2.0f, 1.0f, 1.0f));
-        GetMeshFeatureProcessor()->SetTransform(m_meshHandle, doubleSize);
+        const AZ::Transform transform = AZ::Transform::CreateScale(AZ::Vector3(4.0f, 1.0f, 1.0f));
+        GetMeshFeatureProcessor()->SetTransform(m_meshHandle, transform);
     }
     }
 
 
     void DecalExampleComponent::Deactivate()
     void DecalExampleComponent::Deactivate()
@@ -139,6 +139,18 @@ namespace AtomSampleViewer
             m_decalContainer->SetNumDecalsActive(numDecalsActive);
             m_decalContainer->SetNumDecalsActive(numDecalsActive);
         }
         }
 
 
+        if (ScriptableImGui::Checkbox("Clone decals", &m_cloneDecalsEnabled))
+        {
+            if (m_cloneDecalsEnabled)
+            {
+                m_decalContainerClone->CloneFrom(*m_decalContainer.get());
+            }
+            else
+            {
+                m_decalContainerClone->SetNumDecalsActive(0);
+            }
+        }
+
         m_imguiSidebar.End();
         m_imguiSidebar.End();
     }
     }
 
 
@@ -146,7 +158,8 @@ namespace AtomSampleViewer
     {
     {
         const AZ::RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get();
         const AZ::RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get();
         const auto decalFeatureProcessor = scene->GetFeatureProcessor<AZ::Render::DecalFeatureProcessorInterface>();
         const auto decalFeatureProcessor = scene->GetFeatureProcessor<AZ::Render::DecalFeatureProcessorInterface>();
-        m_decalContainer = AZStd::make_unique<DecalContainer>(decalFeatureProcessor);
+        m_decalContainer = AZStd::make_unique<DecalContainer>(decalFeatureProcessor, AZ::Vector3(1,0,0));
+        m_decalContainerClone = AZStd::make_unique<DecalContainer>(decalFeatureProcessor, AZ::Vector3(-1,0,0));
     }
     }
 
 
 }
 }

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

@@ -58,7 +58,10 @@ namespace AtomSampleViewer
         AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_meshHandle;
         AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_meshHandle;
         Utils::DefaultIBL m_defaultIbl;
         Utils::DefaultIBL m_defaultIbl;
         AZStd::unique_ptr<DecalContainer> m_decalContainer;
         AZStd::unique_ptr<DecalContainer> m_decalContainer;
+        // Used to test the DecalFeatureProcessor::Clone() function
+        AZStd::unique_ptr<DecalContainer> m_decalContainerClone;
         ImGuiSidebar m_imguiSidebar;
         ImGuiSidebar m_imguiSidebar;
+        bool m_cloneDecalsEnabled = false;
 
 
         // CommonSampleComponentBase overrides...
         // CommonSampleComponentBase overrides...
         void OnAllAssetsReadyActivate() override;
         void OnAllAssetsReadyActivate() override;

+ 8 - 6
Gem/Code/Source/Passes/RayTracingAmbientOcclusionPass.cpp

@@ -128,19 +128,21 @@ namespace AZ
 
 
             if (!m_rayTracingShaderTable)
             if (!m_rayTracingShaderTable)
             {
             {
+                RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
+                RHI::RayTracingBufferPools& rayTracingBufferPools = m_rayTracingFeatureProcessor->GetBufferPools();
+
                 // Build shader table once. Since we are not using local srg so we don't need to rebuild it even when scene changed 
                 // Build shader table once. Since we are not using local srg so we don't need to rebuild it even when scene changed 
                 m_rayTracingShaderTable = RHI::Factory::Get().CreateRayTracingShaderTable();
                 m_rayTracingShaderTable = RHI::Factory::Get().CreateRayTracingShaderTable();
-                RHI::RayTracingShaderTableDescriptor descriptor;
-                descriptor.Build(AZ::Name("RayTracingAOShaderTable"), m_rayTracingPipelineState)
+                m_rayTracingShaderTable->Init(*device.get(), rayTracingBufferPools);
+
+                AZStd::shared_ptr<RHI::RayTracingShaderTableDescriptor> descriptor = AZStd::make_shared<RHI::RayTracingShaderTableDescriptor>();
+                descriptor->Build(AZ::Name("RayTracingAOShaderTable"), m_rayTracingPipelineState)
                     ->RayGenerationRecord(AZ::Name("AoRayGen"))
                     ->RayGenerationRecord(AZ::Name("AoRayGen"))
                     ->MissRecord(AZ::Name("AoMiss"))
                     ->MissRecord(AZ::Name("AoMiss"))
                     ->HitGroupRecord(AZ::Name("ClosestHitGroup"))
                     ->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);
+                m_rayTracingShaderTable->Build(descriptor);
             }
             }
 
 
             RenderPass::FrameBeginInternal(params);
             RenderPass::FrameBeginInternal(params);

+ 14 - 0
Gem/Code/Source/Platform/Android/SceneReloadSoakTestComponent_Traits_Platform.h

@@ -0,0 +1,14 @@
+/*
+* 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.
+*
+*/
+
+// [ATOM-15367] On more limited devices the large lattice size would run into out of memory error, specifically "device lost" error
+#define ATOMSAMPLEVIEWER_TRAIT_SCENE_RELOAD_SOAK_TEST_COMPONENT_LATTICE_SIZE       2

+ 1 - 0
Gem/Code/Source/Platform/Android/atomsampleviewer_android_files.cmake

@@ -12,6 +12,7 @@
 set(FILES
 set(FILES
     AtomSampleViewerOptions_Android.cpp
     AtomSampleViewerOptions_Android.cpp
     MultiThreadComponent_Traits_Platform.h
     MultiThreadComponent_Traits_Platform.h
+    SceneReloadSoakTestComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     SampleComponentManager_Android.cpp
     SampleComponentManager_Android.cpp

+ 13 - 0
Gem/Code/Source/Platform/Linux/SceneReloadSoakTestComponent_Traits_Platform.h

@@ -0,0 +1,13 @@
+/*
+* 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.
+*
+*/
+
+#define ATOMSAMPLEVIEWER_TRAIT_SCENE_RELOAD_SOAK_TEST_COMPONENT_LATTICE_SIZE       5

+ 1 - 0
Gem/Code/Source/Platform/Linux/atomsampleviewer_linux_files.cmake

@@ -12,6 +12,7 @@
 set(FILES
 set(FILES
     AtomSampleViewerOptions_Linux.cpp
     AtomSampleViewerOptions_Linux.cpp
     MultiThreadComponent_Traits_Platform.h
     MultiThreadComponent_Traits_Platform.h
+    SceneReloadSoakTestComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     SampleComponentManager_Linux.cpp
     SampleComponentManager_Linux.cpp

+ 13 - 0
Gem/Code/Source/Platform/Mac/SceneReloadSoakTestComponent_Traits_Platform.h

@@ -0,0 +1,13 @@
+/*
+* 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.
+*
+*/
+
+#define ATOMSAMPLEVIEWER_TRAIT_SCENE_RELOAD_SOAK_TEST_COMPONENT_LATTICE_SIZE       5

+ 1 - 0
Gem/Code/Source/Platform/Mac/atomsampleviewer_mac_files.cmake

@@ -13,6 +13,7 @@ set(FILES
     ../../../../Resources/MacLauncher/Info.plist
     ../../../../Resources/MacLauncher/Info.plist
     AtomSampleViewerOptions_Mac.cpp
     AtomSampleViewerOptions_Mac.cpp
     MultiThreadComponent_Traits_Platform.h
     MultiThreadComponent_Traits_Platform.h
+    SceneReloadSoakTestComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     SampleComponentManager_Mac.cpp
     SampleComponentManager_Mac.cpp

+ 13 - 0
Gem/Code/Source/Platform/Windows/SceneReloadSoakTestComponent_Traits_Platform.h

@@ -0,0 +1,13 @@
+/*
+* 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.
+*
+*/
+
+#define ATOMSAMPLEVIEWER_TRAIT_SCENE_RELOAD_SOAK_TEST_COMPONENT_LATTICE_SIZE       5

+ 1 - 0
Gem/Code/Source/Platform/Windows/atomsampleviewer_windows_files.cmake

@@ -12,6 +12,7 @@
 set(FILES
 set(FILES
     AtomSampleViewerOptions_Windows.cpp
     AtomSampleViewerOptions_Windows.cpp
     MultiThreadComponent_Traits_Platform.h
     MultiThreadComponent_Traits_Platform.h
+    SceneReloadSoakTestComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     SampleComponentManager_Windows.cpp
     SampleComponentManager_Windows.cpp

+ 13 - 0
Gem/Code/Source/Platform/iOS/SceneReloadSoakTestComponent_Traits_Platform.h

@@ -0,0 +1,13 @@
+/*
+* 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.
+*
+*/
+
+#define ATOMSAMPLEVIEWER_TRAIT_SCENE_RELOAD_SOAK_TEST_COMPONENT_LATTICE_SIZE       5

+ 1 - 0
Gem/Code/Source/Platform/iOS/atomsampleviewer_ios_files.cmake

@@ -13,6 +13,7 @@ set(FILES
     ../../../../Resources/IOSLauncher/Info.plist
     ../../../../Resources/IOSLauncher/Info.plist
     AtomSampleViewerOptions_iOS.cpp
     AtomSampleViewerOptions_iOS.cpp
     MultiThreadComponent_Traits_Platform.h
     MultiThreadComponent_Traits_Platform.h
+    SceneReloadSoakTestComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     SSRExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     TriangleConstantBufferExampleComponent_Traits_Platform.h
     SampleComponentManager_iOS.cpp
     SampleComponentManager_iOS.cpp

+ 11 - 7
Gem/Code/Source/RHI/BindlessPrototypeExampleComponent.cpp

@@ -393,8 +393,8 @@ namespace AtomSampleViewer
 
 
         // Set up the SRGs
         // Set up the SRGs
         {
         {
-            const char* floatBufferSrgPath = "shaderlib/atom/rpi/shaderresourcegroups/bindlessprototypesrg_floatbuffersrg.azsrg";
-            const char* imageSrgPath = "shaderlib/atom/rpi/shaderresourcegroups/bindlessprototypesrg_imagesrg.azsrg";
+            const char* floatBufferSrgPath = "shaders/rhi/bindlessprototypesrg_floatbuffersrg.azsrg";
+            const char* imageSrgPath = "shaders/rhi/bindlessprototypesrg_imagesrg.azsrg";
 
 
             // Set the FloatBufferSrg
             // Set the FloatBufferSrg
             m_bindlessSrg = std::make_unique<BindlessSrg>(BindlessSrg({
             m_bindlessSrg = std::make_unique<BindlessSrg>(BindlessSrg({
@@ -466,15 +466,19 @@ namespace AtomSampleViewer
         // Set the images
         // Set the images
         {
         {
             Data::Instance<AZ::RPI::ShaderResourceGroup> imageSrg = m_bindlessSrg->GetSrg(m_imageSrgName);
             Data::Instance<AZ::RPI::ShaderResourceGroup> imageSrg = m_bindlessSrg->GetSrg(m_imageSrgName);
-            AZ::RHI::ShaderInputImageIndex imageIndex = imageSrg->FindShaderInputImageIndex(AZ::Name(textureArrayId));
+            AZ::RHI::ShaderInputImageUnboundedArrayIndex imageIndex = imageSrg->FindShaderInputImageUnboundedArrayIndex(AZ::Name(textureArrayId));
 
 
-            for (uint32_t textuerIdx = 0u; textuerIdx < InternalBP::ImageCount; textuerIdx++)
+            AZStd::vector<const RHI::ImageView*> imageViews;
+            for (uint32_t textureIdx = 0u; textureIdx < InternalBP::ImageCount; textureIdx++)
             {
             {
-                AZ::Data::Instance<AZ::RPI::StreamingImage> image = LoadStreamingImage(InternalBP::Images[textuerIdx], InternalBP::SampleName);
-                [[maybe_unused]] bool result = imageSrg->SetImage(imageIndex, image, textuerIdx);
-                AZ_Assert(result, "Failed to set image into shader resource group.");
+                AZ::Data::Instance<AZ::RPI::StreamingImage> image = LoadStreamingImage(InternalBP::Images[textureIdx], InternalBP::SampleName);
+                m_images.push_back(image);
+                imageViews.push_back(image->GetImageView());
             }
             }
 
 
+            [[maybe_unused]] bool result = imageSrg->SetImageViewUnboundedArray(imageIndex, imageViews);
+            AZ_Assert(result, "Failed to set image view unbounded array into shader resource group.");
+
             // Compile the image SRG
             // Compile the image SRG
             imageSrg->Compile();
             imageSrg->Compile();
         }
         }

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

@@ -219,6 +219,9 @@ namespace AtomSampleViewer
         // Handle array holding the FloatBuffer handles for all objects
         // Handle array holding the FloatBuffer handles for all objects
         AZStd::vector<FloatBufferHandle> m_objectHandleArray;
         AZStd::vector<FloatBufferHandle> m_objectHandleArray;
 
 
+        // Image array holding all of the StreamImages
+        AZStd::vector<AZ::Data::Instance<AZ::RPI::StreamingImage>> m_images;
+
         // Light direction handle
         // Light direction handle
         FloatBufferHandle m_lightDirectionHandle;
         FloatBufferHandle m_lightDirectionHandle;
 
 

+ 51 - 76
Gem/Code/Source/RHI/RayTracingExampleComponent.cpp

@@ -42,6 +42,30 @@ namespace AtomSampleViewer
         m_supportRHISamplePipeline = true;
         m_supportRHISamplePipeline = true;
     }
     }
 
 
+    void RayTracingExampleComponent::Activate()
+    {
+        CreateResourcePools();
+        CreateGeometry();
+        CreateFullScreenBuffer();
+        CreateOutputTexture();
+        CreateRasterShader();
+        CreateRayTracingAccelerationStructureObjects();
+        CreateRayTracingPipelineState();
+        CreateRayTracingShaderTable();
+        CreateRayTracingAccelerationTableScope();
+        CreateRayTracingDispatchScope();
+        CreateRasterScope();
+
+        RHI::RHISystemNotificationBus::Handler::BusConnect();
+    }
+
+    void RayTracingExampleComponent::Deactivate()
+    {
+        RHI::RHISystemNotificationBus::Handler::BusDisconnect();
+        m_windowContext = nullptr;
+        m_scopeProducers.clear();
+    }
+
     void RayTracingExampleComponent::CreateResourcePools()
     void RayTracingExampleComponent::CreateResourcePools()
     {
     {
         RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
         RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
@@ -295,55 +319,11 @@ namespace AtomSampleViewer
         m_rayTracingPipelineState->Init(*device.get(), &descriptor);
         m_rayTracingPipelineState->Init(*device.get(), &descriptor);
     }
     }
 
 
-    void RayTracingExampleComponent::CreateRayTracingShaderTableScope()
+    void RayTracingExampleComponent::CreateRayTracingShaderTable()
     {
     {
-        struct ScopeData
-        {
-        };
-
-        const auto prepareFunction = [this]([[maybe_unused]] RHI::FrameGraphInterface& scopeBuilder, [[maybe_unused]] ScopeData& scopeData)
-        {
-        };
-
-        const auto compileFunction = [this]([[maybe_unused]] const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
-        {
-        };
-
-        const auto executeFunction = [this]([[maybe_unused]] const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
-        {
-            RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
-
-            // build the ray tracing shader table descriptor
-            RHI::RayTracingShaderTableDescriptor descriptor;
-            descriptor.Build(AZ::Name("RayTracingExampleShaderTable"), m_rayTracingPipelineState)
-                ->RayGenerationRecord(AZ::Name("RayGenerationShader"))
-                ->MissRecord(AZ::Name("MissShader"))
-                ->HitGroupRecord(AZ::Name("HitGroupGradient")) // triangle1
-                ->HitGroupRecord(AZ::Name("HitGroupGradient")) // triangle2
-                ->HitGroupRecord(AZ::Name("HitGroupSolid")) // triangle3
-                ->HitGroupRecord(AZ::Name("HitGroupSolid")) // rectangle
-            ;
-
-            // initialize the ray tracing shader table object
-            m_rayTracingShaderTable->Init(*device.get(), &descriptor, *m_rayTracingBufferPools);
-        };
-
-        // create the shader table once, outside of the scope
+        RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
         m_rayTracingShaderTable = RHI::Factory::Get().CreateRayTracingShaderTable();
         m_rayTracingShaderTable = RHI::Factory::Get().CreateRayTracingShaderTable();
-
-        m_scopeProducers.emplace_back(
-            aznew RHI::ScopeProducerFunction<
-            ScopeData,
-            decltype(prepareFunction),
-            decltype(compileFunction),
-            decltype(executeFunction)>(
-                RHI::ScopeId{ "RayTracingBuildShaderTable" },
-                ScopeData{},
-                prepareFunction,
-                compileFunction,
-                executeFunction));
-
-        m_shaderTableScopeId = m_scopeProducers.back()->GetScopeId();
+        m_rayTracingShaderTable->Init(*device.get(), *m_rayTracingBufferPools);
     }
     }
 
 
     void RayTracingExampleComponent::CreateRayTracingAccelerationTableScope()
     void RayTracingExampleComponent::CreateRayTracingAccelerationTableScope()
@@ -463,6 +443,16 @@ namespace AtomSampleViewer
             m_rayTracingTlas->CreateBuffers(*device, &tlasDescriptor, *m_rayTracingBufferPools);
             m_rayTracingTlas->CreateBuffers(*device, &tlasDescriptor, *m_rayTracingBufferPools);
 
 
             m_tlasBufferViewDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, (uint32_t)m_rayTracingTlas->GetTlasBuffer()->GetDescriptor().m_byteCount);
             m_tlasBufferViewDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, (uint32_t)m_rayTracingTlas->GetTlasBuffer()->GetDescriptor().m_byteCount);
+
+            [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportBuffer(m_tlasBufferAttachmentId, m_rayTracingTlas->GetTlasBuffer());
+            AZ_Error(RayTracingExampleName, result == RHI::ResultCode::Success, "Failed to import TLAS buffer with error %d", result);
+
+            RHI::BufferScopeAttachmentDescriptor desc;
+            desc.m_attachmentId = m_tlasBufferAttachmentId;
+            desc.m_bufferViewDescriptor = m_tlasBufferViewDescriptor;
+            desc.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
+
+            frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite);
         };
         };
 
 
         RHI::EmptyCompileFunction<ScopeData> compileFunction;
         RHI::EmptyCompileFunction<ScopeData> compileFunction;
@@ -513,18 +503,14 @@ namespace AtomSampleViewer
             // attach TLAS buffer
             // attach TLAS buffer
             if (m_rayTracingTlas->GetTlasBuffer())
             if (m_rayTracingTlas->GetTlasBuffer())
             {
             {
-                [[maybe_unused]] RHI::ResultCode result = frameGraph.GetAttachmentDatabase().ImportBuffer(m_tlasBufferAttachmentId, m_rayTracingTlas->GetTlasBuffer());
-                AZ_Error(RayTracingExampleName, result == RHI::ResultCode::Success, "Failed to import TLAS buffer with error %d", result);
-
                 RHI::BufferScopeAttachmentDescriptor desc;
                 RHI::BufferScopeAttachmentDescriptor desc;
                 desc.m_attachmentId = m_tlasBufferAttachmentId;
                 desc.m_attachmentId = m_tlasBufferAttachmentId;
                 desc.m_bufferViewDescriptor = m_tlasBufferViewDescriptor;
                 desc.m_bufferViewDescriptor = m_tlasBufferViewDescriptor;
                 desc.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
                 desc.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
 
 
-                frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::Read);
+                frameGraph.UseShaderAttachment(desc, RHI::ScopeAttachmentAccess::ReadWrite);
             }
             }
 
 
-            frameGraph.ExecuteAfter(m_shaderTableScopeId);
             frameGraph.SetEstimatedItemCount(1);
             frameGraph.SetEstimatedItemCount(1);
         };
         };
 
 
@@ -586,6 +572,19 @@ namespace AtomSampleViewer
 
 
                 m_globalSrg->SetConstantArray(hitSolidDataConstantIndex, hitSolidData);
                 m_globalSrg->SetConstantArray(hitSolidDataConstantIndex, hitSolidData);
                 m_globalSrg->Compile();
                 m_globalSrg->Compile();
+
+                // update the ray tracing shader table
+                AZStd::shared_ptr<RHI::RayTracingShaderTableDescriptor> descriptor = AZStd::make_shared<RHI::RayTracingShaderTableDescriptor>();
+                descriptor->Build(AZ::Name("RayTracingExampleShaderTable"), m_rayTracingPipelineState)
+                    ->RayGenerationRecord(AZ::Name("RayGenerationShader"))
+                    ->MissRecord(AZ::Name("MissShader"))
+                    ->HitGroupRecord(AZ::Name("HitGroupGradient")) // triangle1
+                    ->HitGroupRecord(AZ::Name("HitGroupGradient")) // triangle2
+                    ->HitGroupRecord(AZ::Name("HitGroupSolid")) // triangle3
+                    ->HitGroupRecord(AZ::Name("HitGroupSolid")) // rectangle
+                    ;
+
+                m_rayTracingShaderTable->Build(descriptor);
             }
             }
         };
         };
 
 
@@ -704,28 +703,4 @@ namespace AtomSampleViewer
                 compileFunction,
                 compileFunction,
                 executeFunction));
                 executeFunction));
     }
     }
-
-    void RayTracingExampleComponent::Activate()
-    {
-        CreateResourcePools();
-        CreateGeometry();
-        CreateFullScreenBuffer();
-        CreateOutputTexture();
-        CreateRasterShader();
-        CreateRayTracingAccelerationStructureObjects();
-        CreateRayTracingPipelineState();
-        CreateRayTracingShaderTableScope();
-        CreateRayTracingAccelerationTableScope();
-        CreateRayTracingDispatchScope();
-        CreateRasterScope();
-
-        RHI::RHISystemNotificationBus::Handler::BusConnect();
-    }
-
-    void RayTracingExampleComponent::Deactivate()
-    {
-        RHI::RHISystemNotificationBus::Handler::BusDisconnect();
-        m_windowContext = nullptr;
-        m_scopeProducers.clear();
-    }
 } // namespace AtomSampleViewer
 } // namespace AtomSampleViewer

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

@@ -61,7 +61,7 @@ namespace AtomSampleViewer
         void CreateRasterShader();
         void CreateRasterShader();
         void CreateRayTracingAccelerationStructureObjects();
         void CreateRayTracingAccelerationStructureObjects();
         void CreateRayTracingPipelineState();
         void CreateRayTracingPipelineState();
-        void CreateRayTracingShaderTableScope();
+        void CreateRayTracingShaderTable();
         void CreateRayTracingAccelerationTableScope();
         void CreateRayTracingAccelerationTableScope();
         void CreateRayTracingDispatchScope();
         void CreateRayTracingDispatchScope();
         void CreateRasterScope();
         void CreateRasterScope();
@@ -106,7 +106,6 @@ namespace AtomSampleViewer
 
 
         // ray tracing shader table
         // ray tracing shader table
         RHI::Ptr<RHI::RayTracingShaderTable> m_rayTracingShaderTable;
         RHI::Ptr<RHI::RayTracingShaderTable> m_rayTracingShaderTable;
-        RHI::ScopeId m_shaderTableScopeId;
 
 
         // ray tracing global shader resource group and pipeline state
         // ray tracing global shader resource group and pipeline state
         Data::Instance<RPI::ShaderResourceGroup> m_globalSrg;
         Data::Instance<RPI::ShaderResourceGroup> m_globalSrg;

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

@@ -233,7 +233,7 @@ namespace AtomSampleViewer
 
 
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/AlphaToCoverage", azrtti_typeid<AlphaToCoverageExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/AlphaToCoverage", azrtti_typeid<AlphaToCoverageExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/AsyncCompute", azrtti_typeid<AsyncComputeExampleComponent>() ) );
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/AsyncCompute", azrtti_typeid<AsyncComputeExampleComponent>() ) );
-        SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/BindlessPrototype", azrtti_typeid<BindlessPrototypeExampleComponent>() ));
+        SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/BindlessPrototype", azrtti_typeid<BindlessPrototypeExampleComponent>(), []() {return Utils::GetRHIDevice()->GetFeatures().m_unboundedArrays; } ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/Compute", azrtti_typeid<ComputeExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/Compute", azrtti_typeid<ComputeExampleComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/CopyQueue", azrtti_typeid<CopyQueueComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/CopyQueue", azrtti_typeid<CopyQueueComponent>() ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/DualSourceBlending", azrtti_typeid<DualSourceBlendingComponent>(), []() {return Utils::GetRHIDevice()->GetFeatures().m_dualSourceBlending; } ));
         SampleComponentManager::RegisterSampleComponent(SampleEntry::NewRHISample( "RHI/DualSourceBlending", azrtti_typeid<DualSourceBlendingComponent>(), []() {return Utils::GetRHIDevice()->GetFeatures().m_dualSourceBlending; } ));
@@ -333,6 +333,17 @@ namespace AtomSampleViewer
         auto* passSystem = RPI::PassSystemInterface::Get();
         auto* passSystem = RPI::PassSystemInterface::Get();
         passSystem->AddPassCreator(Name("RHISamplePass"), &AtomSampleViewer::RHISamplePass::Create);
         passSystem->AddPassCreator(Name("RHISamplePass"), &AtomSampleViewer::RHISamplePass::Create);
 
 
+        // Load ASV's own pass templates mapping
+        // It can be loaded here and it doesn't need be added via OnReadyLoadTemplatesEvent::Handler
+        // since the first render pipeline is created after this point.
+        const char* asvPassTemplatesFile = "Passes/ASV/PassTemplates.azasset";
+        bool loaded = passSystem->LoadPassTemplateMappings(asvPassTemplatesFile);
+        if (!loaded)
+        {
+            AZ_Fatal("SampleComponentManager", "Failed to load AtomSampleViewer's pass templates at %s", asvPassTemplatesFile);
+            return;
+        }
+
         // Use scene and render pipeline for RHI samples as default scene and render pipeline
         // Use scene and render pipeline for RHI samples as default scene and render pipeline
         CreateSceneForRHISample();
         CreateSceneForRHISample();
 
 

+ 3 - 1
Gem/Code/Source/SceneReloadSoakTestComponent.cpp

@@ -23,6 +23,8 @@
 
 
 #include <RHI/BasicRHIComponent.h>
 #include <RHI/BasicRHIComponent.h>
 
 
+#include <SceneReloadSoakTestComponent_Traits_Platform.h>
+
 namespace AtomSampleViewer
 namespace AtomSampleViewer
 {
 {
     using namespace AZ;
     using namespace AZ;
@@ -52,7 +54,7 @@ namespace AtomSampleViewer
         m_currentCount = 0;
         m_currentCount = 0;
         m_totalResetCount = 0;
         m_totalResetCount = 0;
 
 
-        SetLatticeDimensions(5, 5, 5);
+        SetLatticeDimensions(ATOMSAMPLEVIEWER_TRAIT_SCENE_RELOAD_SOAK_TEST_COMPONENT_LATTICE_SIZE, ATOMSAMPLEVIEWER_TRAIT_SCENE_RELOAD_SOAK_TEST_COMPONENT_LATTICE_SIZE, ATOMSAMPLEVIEWER_TRAIT_SCENE_RELOAD_SOAK_TEST_COMPONENT_LATTICE_SIZE);
         Base::Activate();
         Base::Activate();
 
 
         TickBus::Handler::BusConnect();
         TickBus::Handler::BusConnect();

+ 423 - 323
Gem/Code/Source/ShadowExampleComponent.cpp

@@ -29,6 +29,8 @@
 #include <SampleComponentConfig.h>
 #include <SampleComponentConfig.h>
 
 
 #include <RHI/BasicRHIComponent.h>
 #include <RHI/BasicRHIComponent.h>
+#include <Atom/RPI.Public/ColorManagement/TransformColor.h>
+
 
 
 namespace AtomSampleViewer
 namespace AtomSampleViewer
 {
 {
@@ -39,12 +41,13 @@ namespace AtomSampleViewer
     };
     };
 
 
     const AZ::Color ShadowExampleComponent::DirectionalLightColor = AZ::Color::CreateOne();
     const AZ::Color ShadowExampleComponent::DirectionalLightColor = AZ::Color::CreateOne();
-    AZ::Color ShadowExampleComponent::s_diskLightColors[] = {
+    AZ::Color ShadowExampleComponent::s_positionalLightColors[] = {
         // they will be initialized in the constructor.
         // they will be initialized in the constructor.
         AZ::Color::CreateZero(),
         AZ::Color::CreateZero(),
         AZ::Color::CreateZero(),
         AZ::Color::CreateZero(),
         AZ::Color::CreateZero()
         AZ::Color::CreateZero()
     };
     };
+
     const AZ::Render::ShadowmapSize ShadowExampleComponent::s_shadowmapImageSizes[] = 
     const AZ::Render::ShadowmapSize ShadowExampleComponent::s_shadowmapImageSizes[] = 
     {
     {
         AZ::Render::ShadowmapSize::Size256,
         AZ::Render::ShadowmapSize::Size256,
@@ -77,9 +80,9 @@ namespace AtomSampleViewer
     ShadowExampleComponent::ShadowExampleComponent()
     ShadowExampleComponent::ShadowExampleComponent()
         : m_imguiSidebar("@user@/ShadowExampleComponent/sidebar.xml")
         : m_imguiSidebar("@user@/ShadowExampleComponent/sidebar.xml")
     {
     {
-        s_diskLightColors[0] = AZ::Colors::Red;
-        s_diskLightColors[1] = AZ::Colors::Green;
-        s_diskLightColors[2] = AZ::Colors::Blue;
+        s_positionalLightColors[0] = AZ::Colors::Red;
+        s_positionalLightColors[1] = AZ::Colors::Green;
+        s_positionalLightColors[2] = AZ::Colors::Blue;
     }
     }
 
 
     void ShadowExampleComponent::Reflect(AZ::ReflectContext* context)
     void ShadowExampleComponent::Reflect(AZ::ReflectContext* context)
@@ -123,9 +126,9 @@ namespace AtomSampleViewer
         m_floorMeshIsReady = false;
         m_floorMeshIsReady = false;
 
 
         m_directionalLightImageSizeIndex = 2; // image size is 1024.
         m_directionalLightImageSizeIndex = 2; // image size is 1024.
-        for (uint32_t index = 0; index < DiskLightCount; ++index)
+        for (uint32_t index = 0; index < PositionalLightCount; ++index)
         {
         {
-            m_diskLightImageSizeIndices[index] = 2; // image size is 1024.
+            m_positionalLightImageSizeIndices[index] = 2; // image size is 1024.
         }
         }
         m_cascadeCount = 2;
         m_cascadeCount = 2;
         m_ratioLogarithmUniform = 0.5f;
         m_ratioLogarithmUniform = 0.5f;
@@ -135,6 +138,7 @@ namespace AtomSampleViewer
         RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get();
         RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get();
         m_directionalLightFeatureProcessor = scene->GetFeatureProcessor<Render::DirectionalLightFeatureProcessorInterface>();
         m_directionalLightFeatureProcessor = scene->GetFeatureProcessor<Render::DirectionalLightFeatureProcessorInterface>();
         m_diskLightFeatureProcessor = scene->GetFeatureProcessor<Render::DiskLightFeatureProcessorInterface>();
         m_diskLightFeatureProcessor = scene->GetFeatureProcessor<Render::DiskLightFeatureProcessorInterface>();
+        m_pointLightFeatureProcessor = scene->GetFeatureProcessor<Render::PointLightFeatureProcessorInterface>();
 
 
         CreateMeshes();
         CreateMeshes();
         CreateDirectionalLight();
         CreateDirectionalLight();
@@ -154,12 +158,10 @@ namespace AtomSampleViewer
 
 
         GetMeshFeatureProcessor()->ReleaseMesh(m_floorMeshHandle);
         GetMeshFeatureProcessor()->ReleaseMesh(m_floorMeshHandle);
         GetMeshFeatureProcessor()->ReleaseMesh(m_bunnyMeshHandle);
         GetMeshFeatureProcessor()->ReleaseMesh(m_bunnyMeshHandle);
+        DestroyDiskLights();
+        DestroyPointLights();
 
 
         m_directionalLightFeatureProcessor->ReleaseLight(m_directionalLightHandle);
         m_directionalLightFeatureProcessor->ReleaseLight(m_directionalLightHandle);
-        for (DiskLightHandle& handle : m_diskLightHandles)
-        {
-            m_diskLightFeatureProcessor->ReleaseLight(handle);
-        }
 
 
         m_imguiSidebar.Deactivate();
         m_imguiSidebar.Deactivate();
     }
     }
@@ -168,9 +170,7 @@ namespace AtomSampleViewer
     {
     {
         using namespace AZ;
         using namespace AZ;
         constexpr float directionalLightPeriodTime = 5.f; // 5 seconds for a rotation of directional light position.
         constexpr float directionalLightPeriodTime = 5.f; // 5 seconds for a rotation of directional light position.
-        constexpr float diskLightPeriodTime = 7.f; // 7 seconds for a rotation of disk light positions.
-        constexpr float directionalLightDist = 10.f;
-        constexpr float diskLightDist = 5.f;
+        constexpr float lightPeriodTime = 7.f; // 7 seconds for a rotation of light positions.
 
 
         if (m_elapsedTime == 0.f)
         if (m_elapsedTime == 0.f)
         {
         {
@@ -185,67 +185,98 @@ namespace AtomSampleViewer
         {
         {
             m_directionalLightRotationAngle = fmodf(m_directionalLightRotationAngle + deltaTime * Constants::TwoPi / directionalLightPeriodTime, Constants::TwoPi);
             m_directionalLightRotationAngle = fmodf(m_directionalLightRotationAngle + deltaTime * Constants::TwoPi / directionalLightPeriodTime, Constants::TwoPi);
         }
         }
-        if (m_isDiskLightAutoRotate)
+        if (m_isPositionalLightAutoRotate)
         {
         {
-            m_diskLightRotationAngle = fmodf(m_diskLightRotationAngle - deltaTime * Constants::TwoPi / diskLightPeriodTime + Constants::TwoPi, Constants::TwoPi);
+            m_positionalLightRotationAngle = fmodf(m_positionalLightRotationAngle - deltaTime * Constants::TwoPi / lightPeriodTime + Constants::TwoPi, Constants::TwoPi);
         }
         }
 
 
-        // Directional Light Transform
+
+        UpdateDirectionalLight();
+
+        for (uint32_t index = 0; index < PositionalLightCount; ++index)
         {
         {
-            const auto lightLocation = Vector3(
-                directionalLightDist * sinf(m_directionalLightRotationAngle),
-                directionalLightDist * cosf(m_directionalLightRotationAngle),
-                m_directionalLightHeight);
-            const auto lightTransform = Transform::CreateLookAt(
-                lightLocation,
-                Vector3::CreateZero());
-            m_directionalLightFeatureProcessor->SetDirection(m_directionalLightHandle, lightTransform.GetBasis(1));
-        }
+            const auto transform = GetTransformForLight(index);
 
 
-        // Disk Lights Transform
-        for (uint32_t index = 0; index < DiskLightCount; ++index)
-        { 
-            const float angle = m_diskLightRotationAngle + index * Constants::TwoPi / 3;
-            const auto location = Vector3(
-                diskLightDist * sinf(angle),
-                diskLightDist * cosf(angle),
-                m_diskLightHeights[index]);
-            const auto transform = Transform::CreateLookAt(
-                location,
-                Vector3::CreateZero());
-            m_diskLightFeatureProcessor->SetPosition(m_diskLightHandles[index], location);
-            m_diskLightFeatureProcessor->SetDirection(m_diskLightHandles[index], transform.GetBasis(1));
-        }
+            if (m_diskLightHandles[index].IsValid())
+            {
+                m_diskLightFeatureProcessor->SetPosition(m_diskLightHandles[index], transform.GetTranslation());
+                m_diskLightFeatureProcessor->SetDirection(m_diskLightHandles[index], transform.GetBasis(1));
+            }
 
 
+            if (m_pointLightHandles[index].IsValid())
+            {
+                m_pointLightFeatureProcessor->SetPosition(m_pointLightHandles[index], transform.GetTranslation());
+            }
+        }
         Camera::CameraRequestBus::Event(
         Camera::CameraRequestBus::Event(
             GetCameraEntityId(),
             GetCameraEntityId(),
             &Camera::CameraRequestBus::Events::SetFovRadians,
             &Camera::CameraRequestBus::Events::SetFovRadians,
             m_cameraFovY);
             m_cameraFovY);
 
 
+
+        DrawSidebar();
+    }
+
+    AZ::Transform ShadowExampleComponent::GetTransformForLight(const uint32_t index) const
+    {
+        constexpr float diskLightDist = 5.f;
+        using namespace AZ;
+        const float angle = m_positionalLightRotationAngle + index * Constants::TwoPi / 3;
+        const auto location = Vector3(diskLightDist * sinf(angle), diskLightDist * cosf(angle), m_positionalLightHeights[index]);
+        const auto transform = Transform::CreateLookAt(location, Vector3::CreateZero());
+        return transform;
+    }
+
+    float ShadowExampleComponent::GetAttenuationForLight(const uint32_t index) const
+    {
+        return sqrtf(m_lightIntensities[index] / CutoffIntensity);
+    }
+
+    AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Candela> ShadowExampleComponent::GetRgbIntensityForLight(
+        const uint32_t index) const
+    {
+        AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Candela> lightRgbIntensity(
+            s_positionalLightColors[index] * m_lightIntensities[index]);
+       
+        return lightRgbIntensity;
+    }
+
+    AZStd::pair<float, float> ShadowExampleComponent::GetConeAnglesForLight(const uint32_t index) const
+    {
+        const float innerConeAngle = AZ::DegToRad(m_outerConeAngles[index]) * ConeAngleInnerRatio;
+        const float outerConeAngle = AZ::DegToRad(m_outerConeAngles[index]);
+
+        return AZStd::make_pair(innerConeAngle, outerConeAngle);
+    }
+
+
+    void ShadowExampleComponent::UpdateDirectionalLight()
+    {
+        using namespace AZ;
+        constexpr float directionalLightDist = 10.f;
+
+        // Directional Light Transform
+        {
+            const auto lightLocation = Vector3(
+                directionalLightDist * sinf(m_directionalLightRotationAngle), directionalLightDist * cosf(m_directionalLightRotationAngle),
+                m_directionalLightHeight);
+            const auto lightTransform = Transform::CreateLookAt(lightLocation, Vector3::CreateZero());
+            m_directionalLightFeatureProcessor->SetDirection(m_directionalLightHandle, lightTransform.GetBasis(1));
+        }
+
         // Camera Configuration
         // Camera Configuration
         {
         {
             Camera::Configuration config;
             Camera::Configuration config;
-            Camera::CameraRequestBus::EventResult(
-                config,
-                GetCameraEntityId(),
-                &Camera::CameraRequestBus::Events::GetCameraConfiguration);
-            m_directionalLightFeatureProcessor->SetCameraConfiguration(
-                m_directionalLightHandle,
-                config);
+            Camera::CameraRequestBus::EventResult(config, GetCameraEntityId(), &Camera::CameraRequestBus::Events::GetCameraConfiguration);
+            m_directionalLightFeatureProcessor->SetCameraConfiguration(m_directionalLightHandle, config);
         }
         }
 
 
         // Camera Transform
         // Camera Transform
         {
         {
             Transform transform = Transform::CreateIdentity();
             Transform transform = Transform::CreateIdentity();
-            TransformBus::EventResult(
-                transform,
-                GetCameraEntityId(),
-                &TransformBus::Events::GetWorldTM);
-            m_directionalLightFeatureProcessor->SetCameraTransform(
-                m_directionalLightHandle, transform);
+            TransformBus::EventResult(transform, GetCameraEntityId(), &TransformBus::Events::GetWorldTM);
+            m_directionalLightFeatureProcessor->SetCameraTransform(m_directionalLightHandle, transform);
         }
         }
-
-        DrawSidebar();
     }
     }
 
 
     void ShadowExampleComponent::SaveCameraConfiguration()
     void ShadowExampleComponent::SaveCameraConfiguration()
@@ -401,34 +432,33 @@ namespace AtomSampleViewer
 
 
     void ShadowExampleComponent::CreateDiskLights()
     void ShadowExampleComponent::CreateDiskLights()
     {
     {
+        DestroyDiskLights();
+
         using namespace AZ;
         using namespace AZ;
         Render::DiskLightFeatureProcessorInterface* const featureProcessor = m_diskLightFeatureProcessor;
         Render::DiskLightFeatureProcessorInterface* const featureProcessor = m_diskLightFeatureProcessor;
 
 
-        for (uint32_t index = 0; index < DiskLightCount; ++index)
+        for (uint32_t index = 0; index < PositionalLightCount; ++index)
         {
         {
             const DiskLightHandle handle = featureProcessor->AcquireLight();
             const DiskLightHandle handle = featureProcessor->AcquireLight();
-
-            AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Candela> lightColor(s_diskLightColors[index] * m_diskLightIntensities[index]);
-            featureProcessor->SetRgbIntensity(handle, lightColor);
-            featureProcessor->SetAttenuationRadius(
-                handle,
-                sqrtf(m_diskLightIntensities[index] / CutoffIntensity));
-            featureProcessor->SetConeAngles(
-                handle,
-                DegToRad(m_outerConeAngles[index]) * ConeAngleInnerRatio,
-                DegToRad(m_outerConeAngles[index]));
-            featureProcessor->SetShadowsEnabled(handle, m_diskLightShadowEnabled[index]);
-            if (m_diskLightShadowEnabled[index])
-            {
-                featureProcessor->SetShadowmapMaxResolution(handle, s_shadowmapImageSizes[m_diskLightImageSizeIndices[index]]);
-                featureProcessor->SetShadowFilterMethod(handle, s_shadowFilterMethods[m_shadowFilterMethodIndicesDisk[index]]);
-                featureProcessor->SetSofteningBoundaryWidthAngle(handle, AZ::DegToRad(m_boundaryWidthsDisk[index]));
-                featureProcessor->SetPredictionSampleCount(handle, m_predictionSampleCountsDisk[index]);
-                featureProcessor->SetFilteringSampleCount(handle, m_filteringSampleCountsDisk[index]);
-                featureProcessor->SetPcfMethod(handle, m_pcfMethod[index]);
-            }
+            AZ_Assert(m_diskLightHandles[index].IsNull(), "Unreleased light");
             m_diskLightHandles[index] = handle;
             m_diskLightHandles[index] = handle;
         }
         }
+        ApplyDiskLightSettings();
+    }
+    void ShadowExampleComponent::CreatePointLights()
+    {
+        DestroyPointLights();
+
+        using namespace AZ;
+        Render::PointLightFeatureProcessorInterface* const featureProcessor = m_pointLightFeatureProcessor;
+
+        for (uint32_t index = 0; index < PositionalLightCount; ++index)
+        {
+            const PointLightHandle handle = featureProcessor->AcquireLight();        
+            AZ_Assert(m_pointLightHandles[index].IsNull(), "Unreleased light");
+            m_pointLightHandles[index] = handle;
+        }
+        ApplyPointLightSettings();
     }
     }
 
 
     void ShadowExampleComponent::DrawSidebar()
     void ShadowExampleComponent::DrawSidebar()
@@ -439,316 +469,305 @@ namespace AtomSampleViewer
         {
         {
             ImGui::Spacing();
             ImGui::Spacing();
 
 
-            ImGui::Text("Directional Light");
-            ImGui::Indent();
+            DrawSidebarDirectionalLight();
+
+            ImGui::Separator();
+
+            if (DrawSidebarPositionalLights())
             {
             {
-                ScriptableImGui::SliderFloat("Height##Directional", &m_directionalLightHeight, 1.f, 30.f, "%.1f", ImGuiSliderFlags_Logarithmic);
+                ApplyDiskLightSettings();
+                ApplyPointLightSettings();
+            }
 
 
-                ScriptableImGui::Checkbox("Auto Rotation##Directional", &m_isDirectionalLightAutoRotate);
-                ScriptableImGui::SliderAngle("Direction##Directional", &m_directionalLightRotationAngle, 0, 360);
+            ImGui::Separator();
 
 
-                if (ScriptableImGui::SliderFloat("Intensity##Directional", &m_directionalLightIntensity, 0.f, 20.f, "%.1f", ImGuiSliderFlags_Logarithmic))
-                {
-                    AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Lux> lightColor(DirectionalLightColor * m_directionalLightIntensity);
-                    m_directionalLightFeatureProcessor->SetRgbIntensity(m_directionalLightHandle, lightColor);
-                }
+            DrawSidebarCamera();
 
 
-                ImGui::Separator();
+            ImGui::Separator();
 
 
-                ImGui::Text("Shadowmap Size");
-                if (ScriptableImGui::Combo(
-                    "Size##Directional",
-                    &m_directionalLightImageSizeIndex,
-                    s_shadowmapImageSizeLabels,
+            if (ScriptableImGui::Button("Material Details..."))
+            {
+                m_imguiMaterialDetails.SetMaterial(m_materialInstance);
+                m_imguiMaterialDetails.OpenDialog();
+            }
+
+            m_imguiSidebar.End();
+        }
+        m_imguiMaterialDetails.Tick();
+    }
+
+    void ShadowExampleComponent::DrawSidebarDirectionalLight()
+    {
+        ImGui::Indent();
+        if (ImGui::CollapsingHeader("Directional Light", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
+        {
+            ScriptableImGui::SliderFloat("Height##Directional", &m_directionalLightHeight, 1.f, 30.f, "%.1f", ImGuiSliderFlags_Logarithmic);
+
+            ScriptableImGui::Checkbox("Auto Rotation##Directional", &m_isDirectionalLightAutoRotate);
+            ScriptableImGui::SliderAngle("Direction##Directional", &m_directionalLightRotationAngle, 0, 360);
+
+            if (ScriptableImGui::SliderFloat(
+                    "Intensity##Directional", &m_directionalLightIntensity, 0.f, 20.f, "%.1f", ImGuiSliderFlags_Logarithmic))
+            {
+                AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Lux> lightColor(
+                    DirectionalLightColor * m_directionalLightIntensity);
+                m_directionalLightFeatureProcessor->SetRgbIntensity(m_directionalLightHandle, lightColor);
+            }
+
+            ImGui::Text("Shadowmap Size");
+            if (ScriptableImGui::Combo(
+                    "Size##Directional", &m_directionalLightImageSizeIndex, s_shadowmapImageSizeLabels,
                     AZ_ARRAY_SIZE(s_shadowmapImageSizeLabels)))
                     AZ_ARRAY_SIZE(s_shadowmapImageSizeLabels)))
-                {
-                    m_directionalLightFeatureProcessor->SetShadowmapSize(
-                        m_directionalLightHandle,
-                        s_shadowmapImageSizes[m_directionalLightImageSizeIndex]);
-                }
+            {
+                m_directionalLightFeatureProcessor->SetShadowmapSize(
+                    m_directionalLightHandle, s_shadowmapImageSizes[m_directionalLightImageSizeIndex]);
+            }
+
+            ImGui::Text("Number of cascades");
+            bool cascadesChanged = false;
+            cascadesChanged = cascadesChanged || ScriptableImGui::RadioButton("1", &m_cascadeCount, 1);
+            ImGui::SameLine();
+            cascadesChanged = cascadesChanged || ScriptableImGui::RadioButton("2", &m_cascadeCount, 2);
+            ImGui::SameLine();
+            cascadesChanged = cascadesChanged || ScriptableImGui::RadioButton("3", &m_cascadeCount, 3);
+            ImGui::SameLine();
+            cascadesChanged = cascadesChanged || ScriptableImGui::RadioButton("4", &m_cascadeCount, 4);
+            if (cascadesChanged)
+            {
+                m_directionalLightFeatureProcessor->SetCascadeCount(m_directionalLightHandle, m_cascadeCount);
+            }
 
 
-                ImGui::Text("Number of cascades");
-                bool cascadesChanged = false;
-                cascadesChanged = cascadesChanged ||
-                    ScriptableImGui::RadioButton("1", &m_cascadeCount, 1);
-                ImGui::SameLine();
-                cascadesChanged = cascadesChanged ||
-                    ScriptableImGui::RadioButton("2", &m_cascadeCount, 2);
-                ImGui::SameLine();
-                cascadesChanged = cascadesChanged ||
-                    ScriptableImGui::RadioButton("3", &m_cascadeCount, 3);
-                ImGui::SameLine();
-                cascadesChanged = cascadesChanged ||
-                    ScriptableImGui::RadioButton("4", &m_cascadeCount, 4);
-                if (cascadesChanged)
+            ImGui::Spacing();
+            bool cascadeDepthIsChanged = ScriptableImGui::Checkbox("Automatic Cascade Split", &m_shadowmapFrustumSplitIsAutomatic);
+            if (m_shadowmapFrustumSplitIsAutomatic)
+            {
+                ImGui::Text("Cascade partition scheme");
+                ImGui::Text("  (uniform <--> logarithm)");
+                cascadeDepthIsChanged =
+                    cascadeDepthIsChanged || ScriptableImGui::SliderFloat("Ratio", &m_ratioLogarithmUniform, 0.f, 1.f, "%0.3f");
+                if (cascadeDepthIsChanged)
                 {
                 {
-                    m_directionalLightFeatureProcessor->SetCascadeCount(
-                        m_directionalLightHandle,
-                        m_cascadeCount);
+                    m_directionalLightFeatureProcessor->SetShadowmapFrustumSplitSchemeRatio(
+                        m_directionalLightHandle, m_ratioLogarithmUniform);
                 }
                 }
-
-                ImGui::Spacing();
-                bool cascadeDepthIsChanged = 
-                    ScriptableImGui::Checkbox("Automatic Cascade Split", &m_shadowmapFrustumSplitIsAutomatic);
-                if (m_shadowmapFrustumSplitIsAutomatic)
+            }
+            else
+            {
+                for (int cascadeIndex = 0; cascadeIndex < m_cascadeCount; ++cascadeIndex)
                 {
                 {
-                    ImGui::Text("Cascade partition scheme");
-                    ImGui::Text("  (uniform <--> logarithm)");
-                    cascadeDepthIsChanged = cascadeDepthIsChanged ||
-                        ScriptableImGui::SliderFloat("Ratio", &m_ratioLogarithmUniform, 0.f, 1.f, "%0.3f");
-                    if (cascadeDepthIsChanged)
-                    {
-                        m_directionalLightFeatureProcessor->SetShadowmapFrustumSplitSchemeRatio(
-                            m_directionalLightHandle,
-                            m_ratioLogarithmUniform);
-                    }
+                    const AZStd::string label = AZStd::string::format("FarDepth %d", cascadeIndex);
+                    cascadeDepthIsChanged =
+                        cascadeDepthIsChanged || ScriptableImGui::SliderFloat(label.c_str(), &m_cascadeFarDepth[cascadeIndex], 0.01f, 20.f);
                 }
                 }
-                else
+                if (cascadeDepthIsChanged)
                 {
                 {
                     for (int cascadeIndex = 0; cascadeIndex < m_cascadeCount; ++cascadeIndex)
                     for (int cascadeIndex = 0; cascadeIndex < m_cascadeCount; ++cascadeIndex)
                     {
                     {
-                        const AZStd::string label = AZStd::string::format("FarDepth %d", cascadeIndex);
-                        cascadeDepthIsChanged = cascadeDepthIsChanged ||
-                            ScriptableImGui::SliderFloat(label.c_str(), &m_cascadeFarDepth[cascadeIndex], 0.01f, 20.f);
-                    }
-                    if (cascadeDepthIsChanged)
-                    {
-                        for (int cascadeIndex = 0; cascadeIndex < m_cascadeCount; ++cascadeIndex)
-                        {
-                            m_directionalLightFeatureProcessor->SetCascadeFarDepth(
-                                m_directionalLightHandle,
-                                aznumeric_cast<uint16_t>(cascadeIndex),
-                                m_cascadeFarDepth[cascadeIndex]);
-                        }
+                        m_directionalLightFeatureProcessor->SetCascadeFarDepth(
+                            m_directionalLightHandle, aznumeric_cast<uint16_t>(cascadeIndex), m_cascadeFarDepth[cascadeIndex]);
                     }
                     }
                 }
                 }
+            }
 
 
-                ImGui::Spacing();
+            ImGui::Spacing();
 
 
-                ImGui::Text("Filtering");
-                if (ScriptableImGui::Combo(
-                    "Filter Method##Directional",
-                    &m_shadowFilterMethodIndexDirectional,
-                    s_shadowFilterMethodLabels,
+            ImGui::Text("Filtering");
+            if (ScriptableImGui::Combo(
+                    "Filter Method##Directional", &m_shadowFilterMethodIndexDirectional, s_shadowFilterMethodLabels,
                     AZ_ARRAY_SIZE(s_shadowFilterMethodLabels)))
                     AZ_ARRAY_SIZE(s_shadowFilterMethodLabels)))
+            {
+                m_directionalLightFeatureProcessor->SetShadowFilterMethod(
+                    m_directionalLightHandle, s_shadowFilterMethods[m_shadowFilterMethodIndexDirectional]);
+            }
+            if (m_shadowFilterMethodIndexDirectional != aznumeric_cast<int>(AZ::Render::ShadowFilterMethod::None))
+            {
+                ImGui::Text("Boundary Width in meter");
+                if (ScriptableImGui::SliderFloat("Width##Directional", &m_boundaryWidthDirectional, 0.f, 0.1f, "%.3f"))
                 {
                 {
-                    m_directionalLightFeatureProcessor->SetShadowFilterMethod(
-                        m_directionalLightHandle,
-                        s_shadowFilterMethods[m_shadowFilterMethodIndexDirectional]);
-                }
-                if (m_shadowFilterMethodIndexDirectional != aznumeric_cast<int>(ShadowFilterMethod::None))
-                {
-                    ImGui::Text("Boundary Width in meter");
-                    if (ScriptableImGui::SliderFloat("Width##Directional", &m_boundaryWidthDirectional, 0.f, 0.1f, "%.3f"))
-                    {
-                        m_directionalLightFeatureProcessor->SetShadowBoundaryWidth(
-                            m_directionalLightHandle,
-                            m_boundaryWidthDirectional);
-                    }
-                }
-
-                if (m_shadowFilterMethodIndexDirectional == aznumeric_cast<int>(ShadowFilterMethod::Pcf) ||
-                    m_shadowFilterMethodIndexDirectional == aznumeric_cast<int>(ShadowFilterMethod::EsmPcf))
-                {
-                    ImGui::Spacing();
-                    ImGui::Text("Filtering (PCF specific)");
-                    if (ScriptableImGui::SliderInt("Prediction # ##Directional", &m_predictionSampleCountDirectional, 4, 16))
-                    {
-                        m_directionalLightFeatureProcessor->SetPredictionSampleCount(
-                            m_directionalLightHandle,
-                            m_predictionSampleCountDirectional);
-                    }
-                    if (ScriptableImGui::SliderInt("Filtering # ##Directional", &m_filteringSampleCountDirectional, 4, 64))
-                    {
-                        m_directionalLightFeatureProcessor->SetFilteringSampleCount(
-                            m_directionalLightHandle,
-                            m_filteringSampleCountDirectional);
-                    }
+                    m_directionalLightFeatureProcessor->SetShadowBoundaryWidth(m_directionalLightHandle, m_boundaryWidthDirectional);
                 }
                 }
+            }
 
 
+            if (m_shadowFilterMethodIndexDirectional == aznumeric_cast<int>(AZ::Render::ShadowFilterMethod::Pcf) ||
+                m_shadowFilterMethodIndexDirectional == aznumeric_cast<int>(AZ::Render::ShadowFilterMethod::EsmPcf))
+            {
                 ImGui::Spacing();
                 ImGui::Spacing();
-
-                bool debugFlagsChanged = false;
-                debugFlagsChanged = ScriptableImGui::Checkbox("Debug Coloring", &m_isDebugColoringEnabled) || debugFlagsChanged;
-                debugFlagsChanged = ScriptableImGui::Checkbox("Debug Bounding Box", &m_isDebugBoundingBoxEnabled) || debugFlagsChanged;
-
-                if (debugFlagsChanged)
+                ImGui::Text("Filtering (PCF specific)");
+                if (ScriptableImGui::SliderInt("Prediction # ##Directional", &m_predictionSampleCountDirectional, 4, 16))
                 {
                 {
-                    SetupDebugFlags();
+                    m_directionalLightFeatureProcessor->SetPredictionSampleCount(
+                        m_directionalLightHandle, m_predictionSampleCountDirectional);
                 }
                 }
-                
-                if (ScriptableImGui::Checkbox("Cascade Position Correction", &m_isCascadeCorrectionEnabled))
+                if (ScriptableImGui::SliderInt("Filtering # ##Directional", &m_filteringSampleCountDirectional, 4, 64))
                 {
                 {
-                    m_directionalLightFeatureProcessor->SetViewFrustumCorrectionEnabled(
-                        m_directionalLightHandle,
-                        m_isCascadeCorrectionEnabled);
+                    m_directionalLightFeatureProcessor->SetFilteringSampleCount(
+                        m_directionalLightHandle, m_filteringSampleCountDirectional);
                 }
                 }
             }
             }
-            ImGui::Unindent();
 
 
+            ImGui::Spacing();
 
 
-            ImGui::Separator();
+            bool debugFlagsChanged = false;
+            debugFlagsChanged = ScriptableImGui::Checkbox("Debug Coloring", &m_isDebugColoringEnabled) || debugFlagsChanged;
+            debugFlagsChanged = ScriptableImGui::Checkbox("Debug Bounding Box", &m_isDebugBoundingBoxEnabled) || debugFlagsChanged;
 
 
-            ImGui::Text("Disk Lights");
-            ImGui::Indent();
+            if (debugFlagsChanged)
             {
             {
-                ScriptableImGui::Checkbox("Auto Rotation##Disk", &m_isDiskLightAutoRotate);
-                ScriptableImGui::SliderAngle("Base Direction##Disk", &m_diskLightRotationAngle, 0, 360);
+                SetupDebugFlags();
+            }
 
 
-                ImGui::Spacing();
+            if (ScriptableImGui::Checkbox("Cascade Position Correction", &m_isCascadeCorrectionEnabled))
+            {
+                m_directionalLightFeatureProcessor->SetViewFrustumCorrectionEnabled(m_directionalLightHandle, m_isCascadeCorrectionEnabled);
+            }
+        }
+        ImGui::Unindent();
+    }
 
 
-                ImGui::Text("Control Target");
-                ScriptableImGui::RadioButton("Red", &m_controlTargetDiskLightIndex, 0);
-                ImGui::SameLine();
-                ScriptableImGui::RadioButton("Green", &m_controlTargetDiskLightIndex, 1);
-                ImGui::SameLine();
-                ScriptableImGui::RadioButton("Blue", &m_controlTargetDiskLightIndex, 2);
+    bool ShadowExampleComponent::DrawSidebarPositionalLights()
+    {
+        using namespace AZ::Render;
+        bool settingsChanged = false;
+        ImGui::Indent();
+        if (ImGui::CollapsingHeader("Positional Lights", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
+        {
+            ImGui::Text("Light Type");
+            if (ScriptableImGui::RadioButton("Disk", &m_positionalLightTypeActive, 0))
+            {
+                DestroyPointLights();
+                CreateDiskLights();
+                settingsChanged = true;
+            }
+            if (ScriptableImGui::RadioButton("Point", &m_positionalLightTypeActive, 1))
+            {
+                DestroyDiskLights();
+                CreatePointLights();
+                settingsChanged = true;
+            }
 
 
-                const int index = m_controlTargetDiskLightIndex;
-                const DiskLightHandle lightId = m_diskLightHandles[index];
+            ScriptableImGui::Checkbox("Auto Rotation##Positional", &m_isPositionalLightAutoRotate);
+            ScriptableImGui::SliderAngle("Base Direction##Positional", &m_positionalLightRotationAngle, 0, 360);
 
 
-                ScriptableImGui::SliderFloat("Height##Disk", &m_diskLightHeights[index], 1.f, 30.f, "%.1f", ImGuiSliderFlags_Logarithmic);
+            ImGui::Spacing();
 
 
-                if (ScriptableImGui::SliderFloat("Cone Angle", &m_outerConeAngles[index], 0.f, 120.f))
-                {
-                    m_diskLightFeatureProcessor->SetConeAngles(
-                        lightId,
-                        AZ::DegToRad(m_outerConeAngles[index]) * ConeAngleInnerRatio,
-                        AZ::DegToRad(m_outerConeAngles[index]));
-                }
+            ImGui::Text("Control Target");
+            ScriptableImGui::RadioButton("Red", &m_controlTargetPositionalLightIndex, 0);
+            ImGui::SameLine();
+            ScriptableImGui::RadioButton("Green", &m_controlTargetPositionalLightIndex, 1);
+            ImGui::SameLine();
+            ScriptableImGui::RadioButton("Blue", &m_controlTargetPositionalLightIndex, 2);
 
 
-                if (ScriptableImGui::SliderFloat("Intensity##Disk", &m_diskLightIntensities[index], 0.f, 20000.f, "%.1f", ImGuiSliderFlags_Logarithmic))
-                {
-                    AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Candela> lightColor(s_diskLightColors[index] * m_diskLightIntensities[index]);
-                    m_diskLightFeatureProcessor->SetRgbIntensity(lightId, lightColor);
-                    m_diskLightFeatureProcessor->SetAttenuationRadius(
-                        lightId,
-                        sqrtf(m_diskLightIntensities[index] / CutoffIntensity));
-                }
+            const int index = m_controlTargetPositionalLightIndex;
 
 
-                bool shadowmapSizeChanged =
-                    ScriptableImGui::Checkbox("Enable Shadow", &m_diskLightShadowEnabled[index]);
-
-                ImGui::Text("Shadowmap Size");
-                shadowmapSizeChanged = shadowmapSizeChanged ||
-                    ScriptableImGui::Combo(
-                        "Size##Disk",
-                        &m_diskLightImageSizeIndices[index],
-                        s_shadowmapImageSizeLabels,
-                        AZ_ARRAY_SIZE(s_shadowmapImageSizeLabels));
-                if (shadowmapSizeChanged)
-                {
-                    m_diskLightFeatureProcessor->SetShadowsEnabled(lightId, m_diskLightShadowEnabled[index]);
-                    if (m_diskLightShadowEnabled[index])
-                    {
-                        m_diskLightFeatureProcessor->SetShadowmapMaxResolution(lightId, s_shadowmapImageSizes[m_diskLightImageSizeIndices[index]]);
-                    }
+            ScriptableImGui::SliderFloat(
+                "Height##Positional", &m_positionalLightHeights[index], 1.f, 30.f, "%.1f", ImGuiSliderFlags_Logarithmic);
 
 
-                    // Reset shadow parameters when shadow is disabled.
-                    if (!m_diskLightShadowEnabled[index])
-                    {
-                        m_shadowFilterMethodIndicesDisk[index] = 0;
-                        m_boundaryWidthsDisk[index] = 0.f;
-                        m_predictionSampleCountsDisk[index] = 0;
-                        m_filteringSampleCountsDisk[index] = 0;
-                    }
-                }
+            if (ScriptableImGui::SliderFloat("Cone Angle", &m_outerConeAngles[index], 0.f, 120.f))
+            {
+                settingsChanged = true;
+            }
 
 
-                ImGui::Spacing();
+            if (ScriptableImGui::SliderFloat(
+                    "Intensity##Positional", &m_lightIntensities[index], 0.f, 20000.f, "%.1f", ImGuiSliderFlags_Logarithmic))
+            {
+                settingsChanged = true;
+            }
 
 
-                ImGui::Text("Filtering");
-                if (ScriptableImGui::Combo(
-                    "Filter Method##Disk",
-                    &m_shadowFilterMethodIndicesDisk[index],
-                    s_shadowFilterMethodLabels,
-                    AZ_ARRAY_SIZE(s_shadowFilterMethodLabels)))
-                {
-                    m_diskLightFeatureProcessor->SetShadowFilterMethod(
-                        lightId,
-                        s_shadowFilterMethods[m_shadowFilterMethodIndicesDisk[index]]);
-                }
+            bool shadowmapSizeChanged = ScriptableImGui::Checkbox("Enable Shadow", &m_positionalLightShadowEnabled[index]);
 
 
-                if (m_shadowFilterMethodIndicesDisk[index] != aznumeric_cast<int>(ShadowFilterMethod::None))
+            ImGui::Text("Shadowmap Size");
+            shadowmapSizeChanged = shadowmapSizeChanged ||
+                ScriptableImGui::Combo("Size##Positional", &m_positionalLightImageSizeIndices[index], s_shadowmapImageSizeLabels,
+                                       AZ_ARRAY_SIZE(s_shadowmapImageSizeLabels));
+            if (shadowmapSizeChanged)
+            {
+                // Reset shadow parameters when shadow is disabled.
+                if (!m_positionalLightShadowEnabled[index])
                 {
                 {
-                    ImGui::Text("Boundary Width in degrees");
-                    if (ScriptableImGui::SliderFloat("Width##Disk", &m_boundaryWidthsDisk[index], 0.f, 1.0f, "%.3f"))
-                    {
-                        m_diskLightFeatureProcessor->SetSofteningBoundaryWidthAngle(lightId, AZ::DegToRad(m_boundaryWidthsDisk[index]));
-                    }
+                    m_shadowFilterMethodIndicesPositional[index] = 0;
+                    m_boundaryWidthsPositional[index] = 0.f;
+                    m_predictionSampleCountsPositional[index] = 0;
+                    m_filteringSampleCountsPositional[index] = 0;
                 }
                 }
+                settingsChanged = true;
+            }
 
 
-                if (m_shadowFilterMethodIndicesDisk[index] == aznumeric_cast<int>(ShadowFilterMethod::Pcf) ||
-                    m_shadowFilterMethodIndicesDisk[index] == aznumeric_cast<int>(ShadowFilterMethod::EsmPcf))
-                {
-                    ImGui::Spacing();
-                    ImGui::Text("Filtering (PCF specific)");
-                    
-                    if (m_pcfMethod[index] == PcfMethod::BoundarySearch && ScriptableImGui::SliderInt("Prediction # ##Disk", &m_predictionSampleCountsDisk[index], 4, 16))
-                    {
-                        m_diskLightFeatureProcessor->SetPredictionSampleCount(lightId, m_predictionSampleCountsDisk[index]);
-                    }
-                    if (ScriptableImGui::SliderInt("Filtering # ##Disk", &m_filteringSampleCountsDisk[index], 4, 64))
-                    {
-                        m_diskLightFeatureProcessor->SetFilteringSampleCount(lightId, m_filteringSampleCountsDisk[index]);
-                    }
+            ImGui::Spacing();
 
 
-                    int pcfMethodAsInteger = aznumeric_cast<int>(m_pcfMethod[index]);
-                    if (ScriptableImGui::RadioButton(
-                            "Boundary Search filtering", &pcfMethodAsInteger, static_cast<int>(PcfMethod::BoundarySearch)))
-                    {
-                        m_pcfMethod[index] = PcfMethod::BoundarySearch;
-                        m_diskLightFeatureProcessor->SetPcfMethod(lightId, m_pcfMethod[index]);
-                    }
-                    if (ScriptableImGui::RadioButton(
-                            "Bicubic filtering", &pcfMethodAsInteger, static_cast<int>(PcfMethod::Bicubic)))
-                    {
-                        m_pcfMethod[index] = PcfMethod::Bicubic;
-                        m_diskLightFeatureProcessor->SetPcfMethod(lightId, m_pcfMethod[index]);
-                    }
+            ImGui::Text("Filtering");
+            if (ScriptableImGui::Combo(
+                    "Filter Method##Positional", &m_shadowFilterMethodIndicesPositional[index], s_shadowFilterMethodLabels,
+                    AZ_ARRAY_SIZE(s_shadowFilterMethodLabels)))
+            {
+                settingsChanged = true;
+            }
 
 
+            if (m_shadowFilterMethodIndicesPositional[index] != aznumeric_cast<int>(ShadowFilterMethod::None))
+            {
+                ImGui::Text("Boundary Width in degrees");
+                if (ScriptableImGui::SliderFloat("Width##Positional", &m_boundaryWidthsPositional[index], 0.f, 1.0f, "%.3f"))
+                {
+                    settingsChanged = true;
                 }
                 }
             }
             }
-            ImGui::Unindent();
 
 
-            ImGui::Separator();
-
-            ImGui::Text("Camera");
-            ImGui::Indent();
+            if (m_shadowFilterMethodIndicesPositional[index] == aznumeric_cast<int>(ShadowFilterMethod::Pcf) ||
+                m_shadowFilterMethodIndicesPositional[index] == aznumeric_cast<int>(ShadowFilterMethod::EsmPcf))
             {
             {
-                using namespace AZ;
-
-                ScriptableImGui::SliderAngle("FoVY", &m_cameraFovY, 1.f, 120.f);
                 ImGui::Spacing();
                 ImGui::Spacing();
+                ImGui::Text("Filtering (PCF specific)");
+
+                if (m_pcfMethod[index] == PcfMethod::BoundarySearch &&
+                    ScriptableImGui::SliderInt("Prediction # ##Positional", &m_predictionSampleCountsPositional[index], 4, 16))
+                {
+                    settingsChanged = true;
+                }
+                if (ScriptableImGui::SliderInt("Filtering # ##Positional", &m_filteringSampleCountsPositional[index], 4, 64))
+                {
+                    settingsChanged = true;
+                }
 
 
-                Transform cameraTransform;
-                TransformBus::EventResult(
-                    cameraTransform,
-                    GetCameraEntityId(),
-                    &TransformBus::Events::GetWorldTM);
-                const Vector3 eularDegrees = cameraTransform.GetEulerDegrees();
-                float cameraPitch = eularDegrees.GetElement(0);
-                const float cameraYaw = eularDegrees.GetElement(1);
-                if (cameraPitch > 180.f)
+                int pcfMethodAsInteger = aznumeric_cast<int>(m_pcfMethod[index]);
+                if (ScriptableImGui::RadioButton(
+                        "Boundary Search filtering", &pcfMethodAsInteger, static_cast<int>(PcfMethod::BoundarySearch)))
                 {
                 {
-                    cameraPitch -= 360.f;
+                    m_pcfMethod[index] = PcfMethod::BoundarySearch;
+                    settingsChanged = true;
+                }
+                if (ScriptableImGui::RadioButton("Bicubic filtering", &pcfMethodAsInteger, static_cast<int>(PcfMethod::Bicubic)))
+                {
+                    m_pcfMethod[index] = PcfMethod::Bicubic;
+                    settingsChanged = true;
                 }
                 }
-                ImGui::Text("Pitch: %f", cameraPitch);
-                ImGui::Text("Yaw:   %f", cameraYaw);
             }
             }
-            ImGui::Unindent();
+        }
+        ImGui::Unindent();
+        return settingsChanged;
+    }
 
 
-            ImGui::Separator();
+    void ShadowExampleComponent::DrawSidebarCamera()
+    {
+        ImGui::Indent();
+        if (ImGui::CollapsingHeader("Camera", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
+        {
+            using namespace AZ;
 
 
-            if (ScriptableImGui::Button("Material Details..."))
+            ScriptableImGui::SliderAngle("FoVY", &m_cameraFovY, 1.f, 120.f);
+            ImGui::Spacing();
+
+            Transform cameraTransform;
+            TransformBus::EventResult(cameraTransform, GetCameraEntityId(), &TransformBus::Events::GetWorldTM);
+            const Vector3 eularDegrees = cameraTransform.GetEulerDegrees();
+            float cameraPitch = eularDegrees.GetElement(0);
+            const float cameraYaw = eularDegrees.GetElement(1);
+            if (cameraPitch > 180.f)
             {
             {
-                m_imguiMaterialDetails.SetMaterial(m_materialInstance);
-                m_imguiMaterialDetails.OpenDialog();
+                cameraPitch -= 360.f;
             }
             }
-
-            m_imguiSidebar.End();
+            ImGui::Text("Pitch: %f", cameraPitch);
+            ImGui::Text("Yaw:   %f", cameraYaw);
         }
         }
-
-        m_imguiMaterialDetails.Tick();
+        ImGui::Unindent();
     }
     }
 
 
     void ShadowExampleComponent::SetupDebugFlags()
     void ShadowExampleComponent::SetupDebugFlags()
@@ -766,4 +785,85 @@ namespace AtomSampleViewer
             static_cast<AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags>(flags));
             static_cast<AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags>(flags));
     }
     }
 
 
+    void ShadowExampleComponent::DestroyDiskLights()
+    {
+        for (DiskLightHandle& handle : m_diskLightHandles)
+        {
+            m_diskLightFeatureProcessor->ReleaseLight(handle);
+        }
+    }
+
+    void ShadowExampleComponent::DestroyPointLights()
+    {
+        for (PointLightHandle& handle : m_pointLightHandles)
+        {
+            m_pointLightFeatureProcessor->ReleaseLight(handle);
+        }
+    }
+
+    void ShadowExampleComponent::ApplyDiskLightSettings()
+    {
+        for (uint32_t index = 0; index < PositionalLightCount; ++index)
+        {
+            auto lightHandle = m_diskLightHandles[index];
+            if (lightHandle.IsValid())
+            {
+                auto [innerConeAngle, outerConeAngle] = GetConeAnglesForLight(index);
+                m_diskLightFeatureProcessor->SetConeAngles(lightHandle, innerConeAngle, outerConeAngle);
+                m_diskLightFeatureProcessor->SetPosition(lightHandle, GetTransformForLight(index).GetTranslation());
+                m_diskLightFeatureProcessor->SetDirection(lightHandle, GetTransformForLight(index).GetBasis(1));
+                m_diskLightFeatureProcessor->SetAttenuationRadius(lightHandle, GetAttenuationForLight(index));
+                m_diskLightFeatureProcessor->SetRgbIntensity(lightHandle, GetRgbIntensityForLight(index));
+
+                const bool shadowEnabled = m_positionalLightShadowEnabled[index];
+                m_diskLightFeatureProcessor->SetShadowsEnabled(lightHandle, shadowEnabled);
+                if (shadowEnabled)
+                {
+                    m_diskLightFeatureProcessor->SetShadowmapMaxResolution(
+                        lightHandle, s_shadowmapImageSizes[m_positionalLightImageSizeIndices[index]]);
+
+                    m_diskLightFeatureProcessor->SetShadowFilterMethod(
+                        lightHandle, s_shadowFilterMethods[m_shadowFilterMethodIndicesPositional[index]]);
+
+                    m_diskLightFeatureProcessor->SetSofteningBoundaryWidthAngle(lightHandle, AZ::DegToRad(m_boundaryWidthsPositional[index]));
+                    m_diskLightFeatureProcessor->SetFilteringSampleCount(lightHandle, m_filteringSampleCountsPositional[index]);
+                    m_diskLightFeatureProcessor->SetPredictionSampleCount(lightHandle, m_predictionSampleCountsPositional[index]);
+                    m_diskLightFeatureProcessor->SetPcfMethod(lightHandle, m_pcfMethod[index]);
+                }
+            }
+        }
+    }
+
+    void ShadowExampleComponent::ApplyPointLightSettings()
+    {
+        for (uint32_t index = 0; index < PositionalLightCount; ++index)
+        {
+            auto lightHandle = m_pointLightHandles[index];
+            if (lightHandle.IsValid())
+            {
+
+                m_pointLightFeatureProcessor->SetPosition(lightHandle, GetTransformForLight(index).GetTranslation());
+                m_pointLightFeatureProcessor->SetBulbRadius(lightHandle, 0.0f);
+                m_pointLightFeatureProcessor->SetAttenuationRadius(lightHandle, GetAttenuationForLight(index));
+                m_pointLightFeatureProcessor->SetRgbIntensity(lightHandle, GetRgbIntensityForLight(index));
+
+                const bool shadowEnabled = m_positionalLightShadowEnabled[index];
+                m_pointLightFeatureProcessor->SetShadowsEnabled(lightHandle, shadowEnabled);
+                if (shadowEnabled)
+                {
+                    m_pointLightFeatureProcessor->SetShadowmapMaxResolution(
+                        lightHandle, s_shadowmapImageSizes[m_positionalLightImageSizeIndices[index]]);
+
+                    m_pointLightFeatureProcessor->SetShadowFilterMethod(
+                        lightHandle, s_shadowFilterMethods[m_shadowFilterMethodIndicesPositional[index]]);
+
+                    m_pointLightFeatureProcessor->SetPcfMethod(lightHandle, m_pcfMethod[index]);
+                    m_pointLightFeatureProcessor->SetFilteringSampleCount(lightHandle, m_filteringSampleCountsPositional[index]);
+                    m_pointLightFeatureProcessor->SetPredictionSampleCount(lightHandle, m_predictionSampleCountsPositional[index]);
+                    m_pointLightFeatureProcessor->SetSofteningBoundaryWidthAngle(lightHandle, AZ::DegToRad(m_boundaryWidthsPositional[index]));
+                }
+            }
+        }
+    }
+
 } // namespace AtomSampleViewer
 } // namespace AtomSampleViewer

+ 44 - 16
Gem/Code/Source/ShadowExampleComponent.h

@@ -18,6 +18,9 @@
 #include <Atom/Feature/CoreLights/ShadowConstants.h>
 #include <Atom/Feature/CoreLights/ShadowConstants.h>
 #include <Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h>
 #include <Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h>
 
 
+#include <Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h>
+
+
 #include <AzCore/Component/TickBus.h>
 #include <AzCore/Component/TickBus.h>
 
 
 #include <Utils/ImGuiSidebar.h>
 #include <Utils/ImGuiSidebar.h>
@@ -51,6 +54,7 @@ namespace AtomSampleViewer
     private:
     private:
         using DirectionalLightHandle = AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle;
         using DirectionalLightHandle = AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle;
         using DiskLightHandle = AZ::Render::DiskLightFeatureProcessorInterface::LightHandle;
         using DiskLightHandle = AZ::Render::DiskLightFeatureProcessorInterface::LightHandle;
+        using PointLightHandle = AZ::Render::PointLightFeatureProcessorInterface::LightHandle;
         
         
         // AZ::TickBus::Handler overrides...
         // AZ::TickBus::Handler overrides...
         void OnTick(float deltaTime, AZ::ScriptTimePoint time);
         void OnTick(float deltaTime, AZ::ScriptTimePoint time);
@@ -68,18 +72,38 @@ namespace AtomSampleViewer
         void CreateMeshes();
         void CreateMeshes();
         void CreateDirectionalLight();
         void CreateDirectionalLight();
         void CreateDiskLights();
         void CreateDiskLights();
+        void CreatePointLights();
+        void DestroyDiskLights();
+        void DestroyPointLights();
+
         void SetInitialShadowParams();
         void SetInitialShadowParams();
         void SetupDebugFlags();
         void SetupDebugFlags();
 
 
         void DrawSidebar();
         void DrawSidebar();
+        void DrawSidebarDirectionalLight();
+
+        // Return true if settings have changed
+        bool DrawSidebarPositionalLights();
+
+        void DrawSidebarCamera();
+
+        void ApplyDiskLightSettings();
+        void ApplyPointLightSettings();
+
+        void UpdateDirectionalLight();
+
+        AZ::Transform GetTransformForLight(const uint32_t index) const;
+        float GetAttenuationForLight(const uint32_t index) const;
+        AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Candela> GetRgbIntensityForLight(const uint32_t index) const;
+        AZStd::pair<float, float> GetConeAnglesForLight(const uint32_t index) const;
 
 
-        static constexpr uint32_t DiskLightCount = 3;
+        static constexpr uint32_t PositionalLightCount = 3;
         static constexpr float ConeAngleInnerRatio = 0.9f;
         static constexpr float ConeAngleInnerRatio = 0.9f;
         static constexpr float CutoffIntensity = 0.1f;
         static constexpr float CutoffIntensity = 0.1f;
         static constexpr float FarClipDistance = 20.f;
         static constexpr float FarClipDistance = 20.f;
 
 
         static const AZ::Color DirectionalLightColor;
         static const AZ::Color DirectionalLightColor;
-        static AZ::Color s_diskLightColors[DiskLightCount];
+        static AZ::Color s_positionalLightColors[PositionalLightCount];
         
         
         // Mesh Handles
         // Mesh Handles
         using MeshHandle = AZ::Render::MeshFeatureProcessorInterface::MeshHandle;
         using MeshHandle = AZ::Render::MeshFeatureProcessorInterface::MeshHandle;
@@ -89,8 +113,10 @@ namespace AtomSampleViewer
         // lights
         // lights
         AZ::Render::DirectionalLightFeatureProcessorInterface* m_directionalLightFeatureProcessor = nullptr;
         AZ::Render::DirectionalLightFeatureProcessorInterface* m_directionalLightFeatureProcessor = nullptr;
         AZ::Render::DiskLightFeatureProcessorInterface* m_diskLightFeatureProcessor = nullptr;
         AZ::Render::DiskLightFeatureProcessorInterface* m_diskLightFeatureProcessor = nullptr;
+        AZ::Render::PointLightFeatureProcessorInterface* m_pointLightFeatureProcessor = nullptr;
         DirectionalLightHandle m_directionalLightHandle;
         DirectionalLightHandle m_directionalLightHandle;
-        DiskLightHandle m_diskLightHandles[DiskLightCount];
+        DiskLightHandle m_diskLightHandles[PositionalLightCount];
+        PointLightHandle m_pointLightHandles[PositionalLightCount];
 
 
         // asset
         // asset
         AZ::Data::Asset<AZ::RPI::ModelAsset> m_bunnyModelAsset;
         AZ::Data::Asset<AZ::RPI::ModelAsset> m_bunnyModelAsset;
@@ -108,23 +134,24 @@ namespace AtomSampleViewer
         // GUI
         // GUI
         float m_elapsedTime = 0.f;
         float m_elapsedTime = 0.f;
 
 
-        int m_controlTargetDiskLightIndex = 0;
+        int m_positionalLightTypeActive = 0;
+        int m_controlTargetPositionalLightIndex = 0;
         float m_directionalLightRotationAngle = 0.f; // in radian
         float m_directionalLightRotationAngle = 0.f; // in radian
-        float m_diskLightRotationAngle = 0.f; // in radian
+        float m_positionalLightRotationAngle = 0.f; // in radian
         bool m_isDirectionalLightAutoRotate = true;
         bool m_isDirectionalLightAutoRotate = true;
-        bool m_isDiskLightAutoRotate = true;
+        bool m_isPositionalLightAutoRotate = true;
         float m_directionalLightHeight = 10.f;
         float m_directionalLightHeight = 10.f;
-        float m_diskLightHeights[DiskLightCount] = {5.f, 6.f, 7.f};
+        float m_positionalLightHeights[PositionalLightCount] = {5.f, 6.f, 7.f};
         float m_directionalLightIntensity = 5.f;
         float m_directionalLightIntensity = 5.f;
-        float m_diskLightIntensities[DiskLightCount] = {500.f, 900.f, 500.f};
-        float m_outerConeAngles[DiskLightCount] = {17.5f, 20.f, 22.5f};
+        float m_lightIntensities[PositionalLightCount] = {500.f, 900.f, 500.f};
+        float m_outerConeAngles[PositionalLightCount] = {17.5f, 20.f, 22.5f};
         float m_cameraFovY = AZ::Constants::QuarterPi;
         float m_cameraFovY = AZ::Constants::QuarterPi;
 
 
         // Shadowmap
         // Shadowmap
         static const AZ::Render::ShadowmapSize s_shadowmapImageSizes[];
         static const AZ::Render::ShadowmapSize s_shadowmapImageSizes[];
         static const char* s_shadowmapImageSizeLabels[];
         static const char* s_shadowmapImageSizeLabels[];
         int m_directionalLightImageSizeIndex = 2; // image size is 1024.
         int m_directionalLightImageSizeIndex = 2; // image size is 1024.
-        int m_diskLightImageSizeIndices[DiskLightCount] = {2, 2, 2}; // image size is 1024.
+        int m_positionalLightImageSizeIndices[PositionalLightCount] = {2, 2, 2}; // image size is 1024.
         int m_cascadeCount = 2;
         int m_cascadeCount = 2;
         bool m_shadowmapFrustumSplitIsAutomatic = true;
         bool m_shadowmapFrustumSplitIsAutomatic = true;
         float m_ratioLogarithmUniform = 0.5f;
         float m_ratioLogarithmUniform = 0.5f;
@@ -138,20 +165,21 @@ namespace AtomSampleViewer
         bool m_isCascadeCorrectionEnabled = false;
         bool m_isCascadeCorrectionEnabled = false;
         bool m_isDebugColoringEnabled = false;
         bool m_isDebugColoringEnabled = false;
         bool m_isDebugBoundingBoxEnabled = false;
         bool m_isDebugBoundingBoxEnabled = false;
-        bool m_diskLightShadowEnabled[DiskLightCount] = {true, true, true};
+        bool m_positionalLightShadowEnabled[PositionalLightCount] = {true, true, true};
+        bool m_pointLightShadowEnabled[PositionalLightCount] = {true, true, true};
 
 
         // Edge-softening of shadows
         // Edge-softening of shadows
         static const AZ::Render::ShadowFilterMethod s_shadowFilterMethods[];
         static const AZ::Render::ShadowFilterMethod s_shadowFilterMethods[];
         static const char* s_shadowFilterMethodLabels[];
         static const char* s_shadowFilterMethodLabels[];
         int m_shadowFilterMethodIndexDirectional = 0; // filter method is None.
         int m_shadowFilterMethodIndexDirectional = 0; // filter method is None.
-        int m_shadowFilterMethodIndicesDisk[DiskLightCount] = { 0, 0, 0 }; // filter method is None.
+        int m_shadowFilterMethodIndicesPositional[PositionalLightCount] = { 0, 0, 0 }; // filter method is None.
         float m_boundaryWidthDirectional = 0.03f; // 3cm
         float m_boundaryWidthDirectional = 0.03f; // 3cm
-        float m_boundaryWidthsDisk[DiskLightCount] = { 0.25f, 0.25f, 0.25f }; // 0.25 degrees
+        float m_boundaryWidthsPositional[PositionalLightCount] = { 0.25f, 0.25f, 0.25f }; // 0.25 degrees
         int m_predictionSampleCountDirectional = 8;
         int m_predictionSampleCountDirectional = 8;
-        int m_predictionSampleCountsDisk[DiskLightCount] = { 8, 8, 8 };
+        int m_predictionSampleCountsPositional[PositionalLightCount] = { 8, 8, 8 };
         int m_filteringSampleCountDirectional = 32;
         int m_filteringSampleCountDirectional = 32;
-        int m_filteringSampleCountsDisk[DiskLightCount] = { 32, 32, 32 };
-        AZ::Render::PcfMethod m_pcfMethod[DiskLightCount] = {
+        int m_filteringSampleCountsPositional[PositionalLightCount] = { 32, 32, 32 };
+        AZ::Render::PcfMethod m_pcfMethod[PositionalLightCount] = {
             AZ::Render::PcfMethod::BoundarySearch, AZ::Render::PcfMethod::BoundarySearch, AZ::Render::PcfMethod::BoundarySearch};
             AZ::Render::PcfMethod::BoundarySearch, AZ::Render::PcfMethod::BoundarySearch, AZ::Render::PcfMethod::BoundarySearch};
 
 
         ImGuiSidebar m_imguiSidebar;
         ImGuiSidebar m_imguiSidebar;

+ 2 - 2
Objects/bunny.fbx

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:ee81db4d3aa5c76316cb51c700d6b35f500bc599d91b9f62d057df16ed9be929
-size 3266108
+oid sha256:ef71257b240a7e704731806f8cd4b966af5d3c60f22881f9c6a6320180ee71a4
+size 2496384

+ 2 - 2
Objects/suzanne.fbx

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:832ebacab9593d877d59419e2970901806e514b12cccfc72808d6bcd031050db
-size 45436
+oid sha256:e3bcfac5de831c269dac58e7d73d1dc61eb8d9f6d8a241f5c029537b6bcdf166
+size 1088304

+ 117 - 0
Passes/ASV/PassTemplates.azasset

@@ -0,0 +1,117 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "AssetAliasesSourceData",
+    "ClassData": {
+        "AssetPaths": [
+            {
+                "Name": "MainPipeline_Mobile",
+                "Path": "Passes/MainPipeline_Mobile.pass"
+            },
+            {
+                "Name": "ImGuiOnlyPipeline",
+                "Path": "Passes/ImGuiOnlyPipeline.pass"
+            },
+            {
+                "Name": "ImGuiNoInputPassTemplate",
+                "Path": "Passes/ImGuiNoInput.pass"
+            },
+            {
+                "Name": "ColorBlindPassTemplate",
+                "Path": "Passes/ColorblindnessSimulation.pass"
+            },
+            {
+                "Name": "ComplexPipeline",
+                "Path": "Passes/ComplexPipeline.pass"
+            },
+            {
+                "Name": "CheckerboardResolveColorTemplate",
+                "Path": "Passes/CheckerboardResolveColor.pass"
+            },
+            {
+                "Name": "CheckerboardResolveDepthTemplate",
+                "Path": "Passes/CheckerboardResolveDepth.pass"
+            },
+            {
+                "Name": "ForwardCheckerboardPassTemplate",
+                "Path": "Passes/ForwardCheckerboard.pass"
+            },
+            {
+                "Name": "DepthCheckerboardPassTemplate",
+                "Path": "Passes/DepthCheckerboard.pass"
+            },
+            {
+                "Name": "CheckerboardPipeline",
+                "Path": "Passes/CheckerboardPipeline.pass"
+            },
+            {
+                "Name": "MonochromeTemplate",
+                "Path": "Passes/Monochrome.pass"
+            },
+            {
+                "Name": "MSAA_RPI_Pipeline_Core",
+                "Path": "Passes/MSAA_RPI_Pipeline_Core.pass"
+            },
+            {
+                "Name": "MSAA_2x_RPI_Pipeline",
+                "Path": "Passes/MSAA_2x_RPI_Pipeline.pass"
+            },
+            {
+                "Name": "MSAA_4x_RPI_Pipeline",
+                "Path": "Passes/MSAA_4x_RPI_Pipeline.pass"
+            },
+            {
+                "Name": "MSAA_8x_RPI_Pipeline",
+                "Path": "Passes/MSAA_8x_RPI_Pipeline.pass"
+            },
+            {
+                "Name": "No_MSAA_RPI_Pipeline",
+                "Path": "Passes/No_MSAA_RPI_Pipeline.pass"
+            },
+            {
+                "Name": "ColorInvertCS",
+                "Path": "Passes/ColorInvertCS.pass"
+            },
+            {
+                "Name": "LuxCoreTexturePassTemplate",
+                "Path": "Passes/LuxCoreTexture.pass"
+            },
+            {
+                "Name": "RenderTextureTemplate",
+                "Path": "Passes/RenderTexture.pass"
+            },
+            {
+                "Name": "ReflectionsParentPass_nomsaaTemplate",
+                "Path": "Passes/Reflections_nomsaa.pass"
+            },
+            {
+                "Name": "ReflectionGlobalFullscreenPass_nomsaaTemplate",
+                "Path": "Passes/ReflectionGlobalFullscreen_nomsaa.pass"
+            },
+            {
+                "Name": "RHISamplePassTemplate",
+                "Path": "Passes/RHISamplePass.pass"
+            },
+            {
+                "Name": "RHISamplePipelineTemplate",
+                "Path": "Passes/RHISamplePipeline.pass"
+            },
+            {
+                "Name": "SsaoPipeline",
+                "Path": "Passes/SsaoPipeline.pass"
+            },
+            {
+                "Name": "DiffuseGlobalFullscreenPass_nomsaaTemplate",
+                "Path": "Passes/DiffuseGlobalFullscreen_nomsaa.pass"
+            },
+            {
+                "Name": "RayTracingAmbientOcclusionPassTemplate",
+                "Path": "Passes/RayTracingAmbientOcclusion.pass"
+            },
+            {
+                "Name": "SelectorPassTemplate",
+                "Path": "Passes/SelectorPass.pass"
+            }
+        ]
+    }
+}

+ 0 - 609
Passes/PassTemplates.azasset

@@ -1,609 +0,0 @@
-{
-    "Type": "JsonSerialization",
-    "Version": 1,
-    "ClassName": "AssetAliasesSourceData",
-    "ClassData": {
-        "AssetPaths": [
-            {
-                "Name": "DepthPassTemplate",
-                "Path": "Passes/Depth.pass"
-            },
-            {
-                "Name": "DepthMSAAPassTemplate",
-                "Path": "Passes/DepthMSAA.pass"
-            },
-            {
-                "Name": "DepthMSAA2xPassTemplate",
-                "Path": "Passes/DepthMSAA2x.pass"
-            },
-            {
-                "Name": "DepthMSAA4xPassTemplate",
-                "Path": "Passes/DepthMSAA4x.pass"
-            },
-            {
-                "Name": "DepthMSAA8xPassTemplate",
-                "Path": "Passes/DepthMSAA8x.pass"
-            },
-            {
-                "Name": "DepthMaxPassTemplate",
-                "Path": "Passes/DepthMax.pass"
-            },
-            {
-                "Name": "ForwardPassTemplate",
-                "Path": "Passes/Forward.pass"
-            },
-            {
-                "Name": "ForwardMSAAPassTemplate",
-                "Path": "Passes/ForwardMSAA.pass"
-            },
-            {
-                "Name": "ForwardSubsurfaceMSAAPassTemplate",
-                "Path": "Passes/ForwardSubsurfaceMSAA.pass"
-            },
-            {
-                "Name": "CascadedShadowmapsTemplate",
-                "Path": "Passes/CascadedShadowmaps.pass"
-            },
-            {
-                "Name": "MainPipeline",
-                "Path": "Passes/MainPipeline.pass"
-            },
-            {
-                "Name": "MainPipeline_Mobile",
-                "Path": "Passes/MainPipeline_Mobile.pass"
-            },
-            {
-                "Name": "ImGuiOnlyPipeline",
-                "Path": "Passes/ImGuiOnlyPipeline.pass"
-            },
-            {
-                "Name": "ImGuiNoInputPassTemplate",
-                "Path": "Passes/ImGuiNoInput.pass"
-            },
-            {
-                "Name": "MainPipelineRenderToTexture",
-                "Path": "Passes/MainPipelineRenderToTexture.pass"
-            },
-            {
-                "Name": "FullscreenCopyTemplate",
-                "Path": "Passes/FullscreenCopy.pass"
-            },
-            {
-                "Name": "ModulateTextureTemplate",
-                "Path": "Passes/ModulateTexture.pass"
-            },
-            {
-                "Name": "BlendColorGradingLutsTemplate",
-                "Path": "Passes/BlendColorGradingLuts.pass"
-            },
-            {
-                "Name": "LookModificationCompositeTemplate",
-                "Path": "Passes/LookModificationComposite.pass"
-            },
-            {
-                "Name": "LookModificationTransformTemplate",
-                "Path": "Passes/LookModificationTransform.pass"
-            },
-            {
-                "Name": "ColorBlindPassTemplate",
-                "Path": "Passes/ColorblindnessSimulation.pass"
-            },
-            {
-                "Name": "ComplexPipeline",
-                "Path": "Passes/ComplexPipeline.pass"
-            },
-            {
-                "Name": "CheckerboardResolveColorTemplate",
-                "Path": "Passes/CheckerboardResolveColor.pass"
-            },
-            {
-                "Name": "CheckerboardResolveDepthTemplate",
-                "Path": "Passes/CheckerboardResolveDepth.pass"
-            },
-            {
-                "Name": "ForwardCheckerboardPassTemplate",
-                "Path": "Passes/ForwardCheckerboard.pass"
-            },
-            {
-                "Name": "DepthCheckerboardPassTemplate",
-                "Path": "Passes/DepthCheckerboard.pass"
-            },
-            {
-                "Name": "CheckerboardPipeline",
-                "Path": "Passes/CheckerboardPipeline.pass"
-            },
-            {
-                "Name": "LightCullingTilePrepareMSAATemplate",
-                "Path": "Passes/LightCullingTilePrepareMSAA.pass"
-            },
-            {
-                "Name": "LuminanceHeatmapTemplate",
-                "Path": "Passes/LuminanceHeatmap.pass"
-            },
-            {
-                "Name": "LuminanceHistogramGeneratorTemplate",
-                "Path": "Passes/LuminanceHistogramGenerator.pass"
-            },
-            {
-                "Name": "SkyBoxTemplate",
-                "Path": "Passes/SkyBox.pass"
-            },
-            {
-                "Name": "SkyBoxTwoOutputsTemplate",
-                "Path": "Passes/SkyBox_TwoOutputs.pass"
-            },
-            {
-                "Name": "MonochromeTemplate",
-                "Path": "Passes/Monochrome.pass"
-            },
-            {
-                "Name": "MSAAResolveColorTemplate",
-                "Path": "Passes/MSAAResolveColor.pass"
-            },
-            {
-                "Name": "MSAAResolveCustomTemplate",
-                "Path": "Passes/MSAAResolveCustom.pass"
-            },
-            {
-                "Name": "MSAAResolveDepthTemplate",
-                "Path": "Passes/MSAAResolveDepth.pass"
-            },
-            {
-                "Name": "MSAA_RPI_Pipeline_Core",
-                "Path": "Passes/MSAA_RPI_Pipeline_Core.pass"
-            },
-            {
-                "Name": "MSAA_2x_RPI_Pipeline",
-                "Path": "Passes/MSAA_2x_RPI_Pipeline.pass"
-            },
-            {
-                "Name": "MSAA_4x_RPI_Pipeline",
-                "Path": "Passes/MSAA_4x_RPI_Pipeline.pass"
-            },
-            {
-                "Name": "MSAA_8x_RPI_Pipeline",
-                "Path": "Passes/MSAA_8x_RPI_Pipeline.pass"
-            },
-            {
-                "Name": "No_MSAA_RPI_Pipeline",
-                "Path": "Passes/No_MSAA_RPI_Pipeline.pass"
-            },
-            {
-                "Name": "ColorInvertCS",
-                "Path": "Passes/ColorInvertCS.pass"
-            },
-            {
-                "Name": "EyeAdaptationTemplate",
-                "Path": "Passes/EyeAdaptation.pass"
-            },
-            {
-                "Name": "DownsampleLuminanceMinAvgMaxCS",
-                "Path": "Passes/DownsampleLuminanceMinAvgMaxCS.pass"
-            },
-            {
-                "Name": "DownsampleMinAvgMaxCS",
-                "Path": "Passes/DownsampleMinAvgMaxCS.pass"
-            },
-            {
-                "Name": "DownsampleMipChainTemplate",
-                "Path": "Passes/DownsampleMipChain.pass"
-            },
-            {
-                "Name": "DisplayMapperTemplate",
-                "Path": "Passes/DisplayMapper.pass"
-            },
-            {
-                "Name": "UIPassTemplate",
-                "Path": "Passes/UI.pass"
-            },
-            {
-                "Name": "ImGuiPassTemplate",
-                "Path": "Passes/ImGui.pass"
-            },
-            {
-                "Name": "AuxGeomPassTemplate",
-                "Path": "Passes/AuxGeom.pass"
-            },
-            {
-                "Name": "DepthOfFieldTemplate",
-                "Path": "Passes/DepthOfField.pass"
-            },
-            {
-                "Name": "DepthOfFieldOnBokehTemplate",
-                "Path": "Passes/DepthOfFieldOnBokeh.pass"
-            },
-            {
-                "Name": "DepthOfFieldBlurBokehTemplate",
-                "Path": "Passes/DepthOfFieldBlurBokeh.pass"
-            },
-            {
-                "Name": "DepthOfFieldCompositeTemplate",
-                "Path": "Passes/DepthOfFieldComposite.pass"
-            },
-            {
-                "Name": "DepthOfFieldDownSampleTemplate",
-                "Path": "Passes/DepthOfFieldDownSample.pass"
-            },
-            {
-                "Name": "DepthOfFieldPrepareTemplate",
-                "Path": "Passes/DepthOfFieldPrepare.pass"
-            },
-            {
-                "Name": "DepthOfFieldMaskTemplate",
-                "Path": "Passes/DepthOfFieldMask.pass"
-            },
-            {
-                "Name": "DepthOfFieldReadBackFocusDepthTemplate",
-                "Path": "Passes/DepthOfFieldReadBackFocusDepth.pass"
-            },
-            {
-                "Name": "DepthOfFieldWriteFocusDepthFromGpuTemplate",
-                "Path": "Passes/DepthOfFieldWriteFocusDepthFromGpu.pass"
-            },
-            {
-                "Name": "SkinningPassTemplate",
-                "Path": "Passes/Skinning.pass"
-            },
-            {
-                "Name": "BRDFTexturePipeline",
-                "Path": "Passes/BRDFTexturePipeline.pass"
-            },
-            {
-                "Name": "BRDFTextureTemplate",
-                "Path": "Passes/BRDFTexture.pass"
-            },
-            {
-                "Name": "TransparentPassTemplate",
-                "Path": "Passes/Transparent.pass"
-            },
-            {
-                "Name": "LuxCoreTexturePassTemplate",
-                "Path": "Passes/LuxCoreTexture.pass"
-            },
-            {
-                "Name": "RenderTextureTemplate",
-                "Path": "Passes/RenderTexture.pass"
-            },
-            {
-                "Name": "ConvertToAcescgTemplate",
-                "Path": "Passes/ConvertToAcescg.pass"
-            },
-            {
-                "Name": "DepthExponentiationTemplate",
-                "Path": "Passes/DepthExponentiation.pass"
-            },
-            {
-                "Name": "FilterDepthHorizontalTemplate",
-                "Path": "Passes/FilterDepthHorizontal.pass"
-            },
-            {
-                "Name": "FilterDepthVerticalTemplate",
-                "Path": "Passes/FilterDepthVertical.pass"
-            },
-            {
-                "Name": "EsmShadowmapsTemplate",
-                "Path": "Passes/EsmShadowmaps.pass"
-            },
-            {
-                "Name": "LightCullingHeatmapTemplate",
-                "Path": "Passes/LightCullingHeatmap.pass"
-            },
-            {
-                "Name": "LightCullingTemplate",
-                "Path": "Passes/LightCulling.pass"
-            },
-            {
-                "Name": "LightCullingTilePrepareTemplate",
-                "Path": "Passes/LightCullingTilePrepare.pass"
-            },
-            {
-                "Name": "LightCullingRemapTemplate",
-                "Path": "Passes/LightCullingRemap.pass"
-            },
-            {
-                "Name": "ProjectedShadowmapsTemplate",
-                "Path": "Passes/ProjectedShadowmaps.pass"
-            },
-            {
-                "Name": "EnvironmentCubeMapPipeline",
-                "Path": "Passes/EnvironmentCubeMapPipeline.pass"
-            },
-            {
-                "Name": "EnvironmentCubeMapForwardMSAAPassTemplate",
-                "Path": "Passes/EnvironmentCubeMapForwardMSAA.pass"
-            },
-            {
-                "Name": "EnvironmentCubeMapDepthMSAAPassTemplate",
-                "Path": "Passes/EnvironmentCubeMapDepthMSAA.pass"
-            },
-            {
-                "Name": "DepthToLinearTemplate",
-                "Path": "Passes/DepthToLinearDepth.pass"
-            },
-            {
-                "Name": "DeferredFogPassTemplate",
-                "Path": "Passes/DeferredFog.pass"
-            },
-            {
-                "Name": "SubsurfaceScatteringPassTemplate",
-                "Path": "Passes/SubsurfaceScattering.pass"
-            },
-            {
-                "Name": "DiffuseSpecularMergeTemplate",
-                "Path": "Passes/DiffuseSpecularMerge.pass"
-            },
-            {
-                "Name": "SMAAEdgeDetectionTemplate",
-                "Path": "Passes/SMAAEdgeDetection.pass"
-            },
-            {
-                "Name": "SMAABlendingWeightCalculationTemplate",
-                "Path": "Passes/SMAABlendingWeightCalculation.pass"
-            },
-            {
-                "Name": "SMAANeighborhoodBlendingTemplate",
-                "Path": "Passes/SMAANeighborhoodBlending.pass"
-            },
-            {
-                "Name": "SMAAConvertToPerceptualColorTemplate",
-                "Path": "Passes/SMAAConvertToPerceptualColor.pass"
-            },
-            {
-                "Name": "SMAA1xApplyLinearHDRColorTemplate",
-                "Path": "Passes/SMAA1xApplyLinearHDRColor.pass"
-            },
-            {
-                "Name": "SMAA1xApplyPerceptualColorTemplate",
-                "Path": "Passes/SMAA1xApplyPerceptualColor.pass"
-            },
-            {
-                "Name": "SsaoParentTemplate",
-                "Path": "Passes/SsaoParent.pass"
-            },
-            {
-                "Name": "SsaoComputeTemplate",
-                "Path": "Passes/SsaoCompute.pass"
-            },
-            {
-                "Name": "ReflectionsParentPassTemplate",
-                "Path": "Passes/Reflections.pass"
-            },
-            {
-                "Name": "ReflectionsParentPass_nomsaaTemplate",
-                "Path": "Passes/Reflections_nomsaa.pass"
-            },
-            {
-                "Name": "ReflectionProbeStencilPassTemplate",
-                "Path": "Passes/ReflectionProbeStencil.pass"
-            },
-            {
-                "Name": "ReflectionProbeBlendWeightPassTemplate",
-                "Path": "Passes/ReflectionProbeBlendWeight.pass"
-            },
-            {
-                "Name": "ReflectionGlobalFullscreenPassTemplate",
-                "Path": "Passes/ReflectionGlobalFullscreen.pass"
-            },
-            {
-                "Name": "ReflectionGlobalFullscreenPass_nomsaaTemplate",
-                "Path": "Passes/ReflectionGlobalFullscreen_nomsaa.pass"
-            },
-            {
-                "Name": "ReflectionProbeRenderOuterPassTemplate",
-                "Path": "Passes/ReflectionProbeRenderOuter.pass"
-            },
-            {
-                "Name": "ReflectionProbeRenderInnerPassTemplate",
-                "Path": "Passes/ReflectionProbeRenderInner.pass"
-            },
-            {
-                "Name": "ReflectionCopyFrameBufferPassTemplate",
-                "Path": "Passes/ReflectionCopyFrameBuffer.pass"
-            },
-            {
-                "Name": "ReflectionScreenSpacePassTemplate",
-                "Path": "Passes/ReflectionScreenSpace.pass"
-            },
-            {
-                "Name": "ReflectionScreenSpaceTracePassTemplate",
-                "Path": "Passes/ReflectionScreenSpaceTrace.pass"
-            },
-            {
-                "Name": "ReflectionScreenSpaceBlurPassTemplate",
-                "Path": "Passes/ReflectionScreenSpaceBlur.pass"
-            },
-            {
-                "Name": "ReflectionScreenSpaceBlurVerticalPassTemplate",
-                "Path": "Passes/ReflectionScreenSpaceBlurVertical.pass"
-            },
-            {
-                "Name": "ReflectionScreenSpaceBlurHorizontalPassTemplate",
-                "Path": "Passes/ReflectionScreenSpaceBlurHorizontal.pass"
-            },
-            {
-                "Name": "ReflectionScreenSpaceCompositePassTemplate",
-                "Path": "Passes/ReflectionScreenSpaceComposite.pass"
-            },
-            {
-                "Name": "ReflectionCompositePassTemplate",
-                "Path": "Passes/ReflectionComposite.pass"
-            },
-            {
-                "Name": "CameraMotionVectorPassTemplate",
-                "Path": "Passes/CameraMotionVector.pass"
-            },
-            {
-                "Name": "MeshMotionVectorPassTemplate",
-                "Path": "Passes/MeshMotionVector.pass"
-            },
-            {
-                "Name": "RayTracingAccelerationStructurePassTemplate",
-                "Path": "Passes/RayTracingAccelerationStructure.pass"
-            },
-            {
-                "Name": "RHISamplePassTemplate",
-                "Path": "Passes/RHISamplePass.pass"
-            },
-            {
-                "Name": "RHISamplePipelineTemplate",
-                "Path": "Passes/RHISamplePipeline.pass"
-            },
-            {
-                "Name": "FastDepthAwareBlurTemplate",
-                "Path": "Passes/FastDepthAwareBlur.pass"
-            },
-            {
-                "Name": "FastDepthAwareBlurHorTemplate",
-                "Path": "Passes/FastDepthAwareBlurHor.pass"
-            },
-            {
-                "Name": "FastDepthAwareBlurVerTemplate",
-                "Path": "Passes/FastDepthAwareBlurVer.pass"
-            },
-            {
-                "Name": "DepthDownsampleTemplate",
-                "Path": "Passes/DepthDownsample.pass"
-            },
-            {
-                "Name": "DepthUpsampleTemplate",
-                "Path": "Passes/DepthUpsample.pass"
-            },
-            {
-                "Name": "FullscreenOutputOnlyTemplate",
-                "Path": "Passes/FullscreenOutputOnly.pass"
-            },
-            {
-                "Name": "SsaoPipeline",
-                "Path": "Passes/SsaoPipeline.pass"
-            },
-            {
-                "Name": "BloomDownsamplePassTemplate",
-                "Path": "Passes/BloomDownsample.pass"
-            },
-            {
-                "Name": "BloomBlurPassTemplate",
-                "Path": "Passes/BloomBlur.pass"
-            },
-            {
-                "Name": "BloomCompositePassTemplate",
-                "Path": "Passes/BloomComposite.pass"
-            },
-            {
-                "Name": "BloomPassTemplate",
-                "Path": "Passes/Bloom.pass"
-            },
-            {
-                "Name": "DiffuseProbeGridRayTracingPassTemplate",
-                "Path": "Passes/DiffuseProbeGridRayTracing.pass"
-            },
-            {
-                "Name": "DiffuseProbeGridUpdatePassTemplate",
-                "Path": "Passes/DiffuseProbeGridUpdate.pass"
-            },
-            {
-                "Name": "DiffuseProbeGridBlendIrradiancePassTemplate",
-                "Path": "Passes/DiffuseProbeGridBlendIrradiance.pass"
-            },
-            {
-                "Name": "DiffuseProbeGridBlendDistancePassTemplate",
-                "Path": "Passes/DiffuseProbeGridBlendDistance.pass"
-            },
-            {
-                "Name": "DiffuseProbeGridBorderUpdatePassTemplate",
-                "Path": "Passes/DiffuseProbeGridBorderUpdate.pass"
-            },
-            {
-                "Name": "DiffuseProbeGridRelocationPassTemplate",
-                "Path": "Passes/DiffuseProbeGridRelocation.pass"
-            },
-            {
-                "Name": "DiffuseProbeGridClassificationPassTemplate",
-                "Path": "Passes/DiffuseProbeGridClassification.pass"
-            },
-            {
-                "Name": "DiffuseGlobalIlluminationPassTemplate",
-                "Path": "Passes/DiffuseGlobalIllumination.pass"
-            },
-            {
-                "Name": "DiffuseProbeGridDownsamplePassTemplate",
-                "Path": "Passes/DiffuseProbeGridDownsample.pass"
-            },
-            {
-                "Name": "DiffuseProbeGridRenderPassTemplate",
-                "Path": "Passes/DiffuseProbeGridRender.pass"
-            },
-            {
-                "Name": "DiffuseCompositePassTemplate",
-                "Path": "Passes/DiffuseComposite.pass"
-            },
-            {
-                "Name": "DiffuseGlobalFullscreenPassTemplate",
-                "Path": "Passes/DiffuseGlobalFullscreen.pass"
-            },
-            {
-                "Name": "DiffuseGlobalFullscreenPass_nomsaaTemplate",
-                "Path": "Passes/DiffuseGlobalFullscreen_nomsaa.pass"
-            },
-            {
-                "Name": "MorphTargetPassTemplate",
-                "Path": "Passes/MorphTarget.pass"
-            },
-            {
-                "Name": "DepthMSAAParentTemplate",
-                "Path": "Passes/DepthMSAAParent.pass"
-            },
-            {
-                "Name": "MotionVectorParentTemplate",
-                "Path": "Passes/MotionVectorParent.pass"
-            },
-            {
-                "Name": "LightCullingParentTemplate",
-                "Path": "Passes/LightCullingParent.pass"
-            },
-            {
-                "Name": "ShadowParentTemplate",
-                "Path": "Passes/ShadowParent.pass"
-            },
-            {
-                "Name": "OpaqueParentTemplate",
-                "Path": "Passes/OpaqueParent.pass"
-            },
-            {
-                "Name": "TransparentParentTemplate",
-                "Path": "Passes/TransparentParent.pass"
-            },
-            {
-                "Name": "PostProcessParentTemplate",
-                "Path": "Passes/PostProcessParent.pass"
-            },
-            {
-                "Name": "DebugOverlayParentTemplate",
-                "Path": "Passes/DebugOverlayParent.pass"
-            },
-            {
-                "Name": "UIParentTemplate",
-                "Path": "Passes/UIParent.pass"
-            },
-            {
-                "Name": "RayTracingAmbientOcclusionPassTemplate",
-                "Path": "Passes/RayTracingAmbientOcclusion.pass"
-            },
-            {
-                "Name": "SelectorPassTemplate",
-                "Path": "Passes/SelectorPass.pass"
-            },
-            {
-                "Name": "LightAdaptationParentTemplate",
-                "Path": "Passes/LightAdaptationParent.pass"
-            },
-            {
-                "Name": "LowEndForwardPassTemplate",
-                "Path": "Passes/LowEndForward.pass"
-            },
-            {
-                "Name": "LowEndPipelineTemplate",
-                "Path": "Passes/LowEndPipeline.pass"
-            }
-        ]
-    }
-}

+ 3 - 1
Scripts/Decals.bv.lua

@@ -22,8 +22,10 @@ Print('Saving screenshots to ' .. NormalizePath(g_screenshotOutputFolder))
 OpenSample('RPI/Decals')
 OpenSample('RPI/Decals')
 ResizeViewport(1600, 900)
 ResizeViewport(1600, 900)
 SelectImageComparisonToleranceLevel("Level G")
 SelectImageComparisonToleranceLevel("Level G")
-ArcBallCameraController_SetDistance(2.0)
+ArcBallCameraController_SetDistance(4.0)
+-- Wait until decals are loaded in
 IdleFrames(5)
 IdleFrames(5)
+SetImguiValue('Clone decals', true)
 TakeScreenshots()
 TakeScreenshots()
 
 
 OpenSample(nil)
 OpenSample(nil)

+ 3 - 0
Scripts/ExpectedScreenshots/CopyQueue/screenshot_warp_CopyQueue.png

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

+ 2 - 2
Scripts/ExpectedScreenshots/Decals/screenshot_decals.png

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
 version https://git-lfs.github.com/spec/v1
-oid sha256:da89aea3a80053f6bd03f660f9b156872e5d6b2c77e41d5e5fa9ad9598c56d8f
-size 904183
+oid sha256:1a44e936b61fd9181b72d2e4e4decf73a68cd6e0f9b88646f4ad5de5ce819d94
+size 379981

+ 3 - 0
Scripts/ExpectedScreenshots/DualSourceBlending/screenshot_warp_DualSourceBlending.png

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

+ 3 - 0
Scripts/ExpectedScreenshots/InputAssembly/screenshot_warp_InputAssembly.png

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

+ 3 - 0
Scripts/ExpectedScreenshots/MSAA/screenshot_warp_MSAA.png

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

+ 3 - 0
Scripts/ExpectedScreenshots/MultiRenderTarget/screenshot_warp_MultiRenderTarget.png

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

+ 3 - 0
Scripts/ExpectedScreenshots/MultiThread/screenshot_warp_MultiThread.png

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

+ 3 - 0
Scripts/ExpectedScreenshots/Stencil/screenshot_warp_Stencil.png

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

+ 3 - 0
Scripts/ExpectedScreenshots/Texture/screenshot_warp_Texture.png

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

+ 3 - 0
Scripts/ExpectedScreenshots/Texture3d/screenshot_warp_Texture3d.png

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

+ 3 - 0
Scripts/ExpectedScreenshots/TextureMap/screenshot_warp_TextureMap.png

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

+ 27 - 27
Scripts/ShadowTest.bv.lua

@@ -19,24 +19,24 @@ OpenSample('Features/Shadow')
 ResizeViewport(800, 600)
 ResizeViewport(800, 600)
 SelectImageComparisonToleranceLevel("Level H")
 SelectImageComparisonToleranceLevel("Level H")
 SetImguiValue('Auto Rotation##Directional', false)
 SetImguiValue('Auto Rotation##Directional', false)
-SetImguiValue('Auto Rotation##Disk', false)
+SetImguiValue('Auto Rotation##Positional', false)
 
 
 -- Initial
 -- Initial
 SetImguiValue('Direction##Directional', 0.0)
 SetImguiValue('Direction##Directional', 0.0)
-SetImguiValue('Base Direction##Disk', 0.0)
+SetImguiValue('Base Direction##Positional', 0.0)
 IdleFrames(1)
 IdleFrames(1)
 CaptureScreenshot(g_screenshotOutputFolder .. '/initial.png')
 CaptureScreenshot(g_screenshotOutputFolder .. '/initial.png')
 
 
 -- Directional Light Initial
 -- Directional Light Initial
--- Diabling Disk lights
+-- Diabling Positional lights
 SetImguiValue('Red', true)
 SetImguiValue('Red', true)
-SetImguiValue('Intensity##Disk', 0.0)
+SetImguiValue('Intensity##Positional', 0.0)
 IdleFrames(1)
 IdleFrames(1)
 SetImguiValue('Green', true)
 SetImguiValue('Green', true)
-SetImguiValue('Intensity##Disk', 0.0)
+SetImguiValue('Intensity##Positional', 0.0)
 IdleFrames(1)
 IdleFrames(1)
 SetImguiValue('Blue', true)
 SetImguiValue('Blue', true)
-SetImguiValue('Intensity##Disk', 0.0)
+SetImguiValue('Intensity##Positional', 0.0)
 -- Set Camera
 -- Set Camera
 ArcBallCameraController_SetHeading(DegToRad(90.0))
 ArcBallCameraController_SetHeading(DegToRad(90.0))
 ArcBallCameraController_SetPitch(DegToRad(-45.0))
 ArcBallCameraController_SetPitch(DegToRad(-45.0))
@@ -98,60 +98,60 @@ SetImguiValue('Filter Method##Directional', 'ESM+PCF')
 IdleFrames(1)
 IdleFrames(1)
 CaptureScreenshot(g_screenshotOutputFolder .. '/directional_esm_pcf.png')
 CaptureScreenshot(g_screenshotOutputFolder .. '/directional_esm_pcf.png')
 
 
--- Disk Light Initial
+-- Positional Light Initial
 -- Disabling directional light
 -- Disabling directional light
 SetImguiValue('Intensity##Directional', 0.0)
 SetImguiValue('Intensity##Directional', 0.0)
 SetImguiValue('Red', true)
 SetImguiValue('Red', true)
-SetImguiValue('Intensity##Disk', 500.0)
+SetImguiValue('Intensity##Positional', 500.0)
 IdleFrames(1)
 IdleFrames(1)
 SetImguiValue('Green', true)
 SetImguiValue('Green', true)
-SetImguiValue('Height##Disk', 4.0)
-SetImguiValue('Intensity##Disk', 400.0)
+SetImguiValue('Height##Positional', 4.0)
+SetImguiValue('Intensity##Positional', 400.0)
 IdleFrames(1)
 IdleFrames(1)
 SetImguiValue('Blue', true)
 SetImguiValue('Blue', true)
-SetImguiValue('Intensity##Disk', 500.0)
+SetImguiValue('Intensity##Positional', 500.0)
 IdleFrames(1)
 IdleFrames(1)
 CaptureScreenshot(g_screenshotOutputFolder .. '/spot_initial.png')
 CaptureScreenshot(g_screenshotOutputFolder .. '/spot_initial.png')
 
 
--- Disk Light Disabling Shadow for Red
+-- Positional Light Disabling Shadow for Red
 SetImguiValue('Red', true)
 SetImguiValue('Red', true)
 SetImguiValue('Enable Shadow', false)
 SetImguiValue('Enable Shadow', false)
 IdleFrames(1)
 IdleFrames(1)
 CaptureScreenshot(g_screenshotOutputFolder .. '/spot_no_red_shadow.png')
 CaptureScreenshot(g_screenshotOutputFolder .. '/spot_no_red_shadow.png')
 
 
--- Disk Light Various Shadowmap Sizes
+-- Positional Light Various Shadowmap Sizes
 SetImguiValue('Red', true)
 SetImguiValue('Red', true)
 SetImguiValue('Enable Shadow', true)
 SetImguiValue('Enable Shadow', true)
 IdleFrames(1)
 IdleFrames(1)
-SetImguiValue('Size##Disk', '2048')
+SetImguiValue('Size##Positional', '2048')
 IdleFrames(1)
 IdleFrames(1)
 SetImguiValue('Green', true)
 SetImguiValue('Green', true)
-SetImguiValue('Size##Disk', '1024')
+SetImguiValue('Size##Positional', '1024')
 IdleFrames(1)
 IdleFrames(1)
 SetImguiValue('Blue', true)
 SetImguiValue('Blue', true)
-SetImguiValue('Size##Disk', '512')
+SetImguiValue('Size##Positional', '512')
 IdleFrames(1)
 IdleFrames(1)
 CaptureScreenshot(g_screenshotOutputFolder .. '/spot_shadowmap_size.png')
 CaptureScreenshot(g_screenshotOutputFolder .. '/spot_shadowmap_size.png')
 
 
--- Disk Light Various Filter Method
+-- Positional Light Various Filter Method
 SetImguiValue('Red', true)
 SetImguiValue('Red', true)
-SetImguiValue('Filter Method##Disk', 'PCF')
+SetImguiValue('Filter Method##Positional', 'PCF')
 IdleFrames(1)
 IdleFrames(1)
-SetImguiValue('Width##Disk', 0.5)
-SetImguiValue('Prediction # ##Disk', 16)
-SetImguiValue('Filtering # ##Disk', 64)
+SetImguiValue('Width##Positional', 0.5)
+SetImguiValue('Prediction # ##Positional', 16)
+SetImguiValue('Filtering # ##Positional', 64)
 IdleFrames(1)
 IdleFrames(1)
 SetImguiValue('Green', true)
 SetImguiValue('Green', true)
-SetImguiValue('Filter Method##Disk', 'ESM')
+SetImguiValue('Filter Method##Positional', 'ESM')
 IdleFrames(1)
 IdleFrames(1)
-SetImguiValue('Width##Disk', 0.5)
+SetImguiValue('Width##Positional', 0.5)
 IdleFrames(1)
 IdleFrames(1)
 SetImguiValue('Blue', true)
 SetImguiValue('Blue', true)
-SetImguiValue('Filter Method##Disk', 'ESM+PCF')
+SetImguiValue('Filter Method##Positional', 'ESM+PCF')
 IdleFrames(1)
 IdleFrames(1)
-SetImguiValue('Width##Disk', 0.5)
-SetImguiValue('Prediction # ##Disk', 16)
-SetImguiValue('Filtering # ##Disk', 64)
+SetImguiValue('Width##Positional', 0.5)
+SetImguiValue('Prediction # ##Positional', 16)
+SetImguiValue('Filtering # ##Positional', 64)
 IdleFrames(1)
 IdleFrames(1)
 CaptureScreenshot(g_screenshotOutputFolder .. '/spot_filter_method.png')
 CaptureScreenshot(g_screenshotOutputFolder .. '/spot_filter_method.png')
 
 

+ 48 - 0
Scripts/_AutomatedReviewWARPTestSuite_.bv.lua

@@ -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.
+--
+--
+----------------------------------------------------------------------------------------------------
+
+-- Test suite "Main" tests for the automated review process using WARP software rasterizer.
+-- These tests do not require GPU nodes. They will run only on AtomSampleViewer repository.
+-- They are intended to run simply with the most basic RHI tests focused on deterministic outcomes
+-- screen compares expect to be run under WARP use either dxcpl to force WARP or command line
+-- argument: -forceAdapter="Microsoft Basic Render Driver"
+
+-- We need to lock the frame time to get deterministic timing of the screenshots for consistency between runs
+LockFrameTime(1/10)
+
+function SimpleScreenshotTest(sample, threshold)
+    local sampleSplit = {}
+    for str in string.gmatch(sample, "([^/]+)") do
+        table.insert(sampleSplit, str)
+    end
+    g_screenshotOutputFolder = ResolvePath('@user@/Scripts/Screenshots/' .. sampleSplit[#sampleSplit] .. '/')
+    Print('Saving screenshots to ' .. NormalizePath(g_screenshotOutputFolder))
+    OpenSample(sample)
+    ResizeViewport(1600, 900)
+    SelectImageComparisonToleranceLevel(threshold)
+    IdleFrames(15)
+    CaptureScreenshot(g_screenshotOutputFolder .. '/screenshot_warp_' .. sampleSplit[#sampleSplit] .. '.png')
+    OpenSample(nil)
+end
+
+SimpleScreenshotTest('RHI/CopyQueue', 'Level A')
+SimpleScreenshotTest('RHI/DualSourceBlending', 'Level A')
+SimpleScreenshotTest('RHI/InputAssembly', 'Level E')
+SimpleScreenshotTest('RHI/MSAA', 'Level A')
+SimpleScreenshotTest('RHI/MultiRenderTarget', 'Level A')
+SimpleScreenshotTest('RHI/MultiThread', 'Level A')
+SimpleScreenshotTest('RHI/Stencil', 'Level A')
+SimpleScreenshotTest('RHI/Texture', 'Level A')
+SimpleScreenshotTest('RHI/Texture3d', 'Level A')
+SimpleScreenshotTest('RHI/TextureMap', 'Level D')

+ 13 - 13
Shaders/RHI/BindlessPrototype.azsl

@@ -13,7 +13,7 @@
 // GlobalSrg::m_floatBuffer
 // GlobalSrg::m_floatBuffer
 // PerSceneSrg::m_textureArray
 // PerSceneSrg::m_textureArray
 // PerSceneSrg::m_sampler
 // PerSceneSrg::m_sampler
-#include <Atom/RPI/ShaderResourceGroups/BindlessPrototypeSrg.azsli>
+#include "BindlessPrototypeSrg.azsli"
 
 
 struct BindlessMaterial0
 struct BindlessMaterial0
 {
 {
@@ -87,15 +87,15 @@ VertexOutput MainVS(VertexInput vsInput)
     uint offset = 0;
     uint offset = 0;
     PerObject perObject;
     PerObject perObject;
     {
     {
-        SetFloat4x4(perObject.m_localToWorldMatrix, HandleSrg::m_perObjectHandle, offset);
-        SetFloat4(perObject.rotation, HandleSrg::m_perObjectHandle, offset);
+        ReadFromFloatBuffer(perObject.m_localToWorldMatrix, HandleSrg::m_perObjectHandle, offset);
+        ReadFromFloatBuffer(perObject.rotation, HandleSrg::m_perObjectHandle, offset);
     }
     }
 
 
     // Read the world matrix from the FloatBuffer
     // Read the world matrix from the FloatBuffer
     float4x4 worldToClipMatrix;
     float4x4 worldToClipMatrix;
     offset = 0;
     offset = 0;
     {
     {
-        SetFloat4x4(worldToClipMatrix, HandleSrg::m_perViewHandle, offset);
+        ReadFromFloatBuffer(worldToClipMatrix, HandleSrg::m_perViewHandle, offset);
     }
     }
 
 
     const float4 worldPosition = mul(perObject.m_localToWorldMatrix,  float4(vsInput.m_position, 1.0));
     const float4 worldPosition = mul(perObject.m_localToWorldMatrix,  float4(vsInput.m_position, 1.0));
@@ -119,22 +119,22 @@ PixelOutput MainPS(VertexOutput psInput)
     // Read the material index to identify the material type
     // Read the material index to identify the material type
     uint4 materialIndex;
     uint4 materialIndex;
     {
     {
-        SetUint4(materialIndex, HandleSrg::m_materialHandle, offset);
+        ReadFromFloatBuffer(materialIndex, HandleSrg::m_materialHandle, offset);
     }
     }
     
     
     // Read the material data from the FloatBuffer depending ont he material index
     // Read the material data from the FloatBuffer depending ont he material index
     if(materialIndex.x == 0) // Albedo material
     if(materialIndex.x == 0) // Albedo material
     {
     {
         BindlessMaterial0 bindlessMaterial0; 
         BindlessMaterial0 bindlessMaterial0; 
-        SetFloat4(bindlessMaterial0.m_diffuseColor, HandleSrg::m_materialHandle, offset);
+        ReadFromFloatBuffer(bindlessMaterial0.m_diffuseColor, HandleSrg::m_materialHandle, offset);
         
         
         OUT.m_color = float4(bindlessMaterial0.m_diffuseColor.xyz, 1.0);
         OUT.m_color = float4(bindlessMaterial0.m_diffuseColor.xyz, 1.0);
     }
     }
     else if(materialIndex.x == 1) // Texture sample material
     else if(materialIndex.x == 1) // Texture sample material
     {
     {
         BindlessMaterial1 bindlessMaterial1; 
         BindlessMaterial1 bindlessMaterial1; 
-        SetFloat4(bindlessMaterial1.m_diffuseColor, HandleSrg::m_materialHandle, offset);
-        SetUint(bindlessMaterial1.m_diffuseTextureIndex, HandleSrg::m_materialHandle, offset);
+        ReadFromFloatBuffer(bindlessMaterial1.m_diffuseColor, HandleSrg::m_materialHandle, offset);
+        ReadFromFloatBuffer(bindlessMaterial1.m_diffuseTextureIndex, HandleSrg::m_materialHandle, offset);
 
 
         Texture2D texture = ImageSrg::m_textureArray[bindlessMaterial1.m_diffuseTextureIndex % 8]; // % 8 for wrap-around texture index as specified in ImageSrg.m_textureArray
         Texture2D texture = ImageSrg::m_textureArray[bindlessMaterial1.m_diffuseTextureIndex % 8]; // % 8 for wrap-around texture index as specified in ImageSrg.m_textureArray
         OUT.m_color = texture.Sample(ImageSrg::m_sampler, psInput.m_uv);
         OUT.m_color = texture.Sample(ImageSrg::m_sampler, psInput.m_uv);
@@ -143,17 +143,17 @@ PixelOutput MainPS(VertexOutput psInput)
     {
     {
         float4 color;
         float4 color;
         BindlessMaterial2 bindlessMaterial2; 
         BindlessMaterial2 bindlessMaterial2; 
-        SetFloat4(bindlessMaterial2.m_diffuseColor, HandleSrg::m_materialHandle, offset);
-        SetUint(bindlessMaterial2.m_diffuseTextureIndex, HandleSrg::m_materialHandle, offset);
-        SetUint(bindlessMaterial2.m_normalTextureIndex, HandleSrg::m_materialHandle, offset);
-        SetUint(bindlessMaterial2.m_specularTextureIndex, HandleSrg::m_materialHandle, offset);
+        ReadFromFloatBuffer(bindlessMaterial2.m_diffuseColor, HandleSrg::m_materialHandle, offset);
+        ReadFromFloatBuffer(bindlessMaterial2.m_diffuseTextureIndex, HandleSrg::m_materialHandle, offset);
+        ReadFromFloatBuffer(bindlessMaterial2.m_normalTextureIndex, HandleSrg::m_materialHandle, offset);
+        ReadFromFloatBuffer(bindlessMaterial2.m_specularTextureIndex, HandleSrg::m_materialHandle, offset);
 
 
         Texture2D texture = ImageSrg::m_textureArray[bindlessMaterial2.m_diffuseTextureIndex % 8]; // % 8 for wrap-around texture index as specified in ImageSrg.m_textureArray
         Texture2D texture = ImageSrg::m_textureArray[bindlessMaterial2.m_diffuseTextureIndex % 8]; // % 8 for wrap-around texture index as specified in ImageSrg.m_textureArray
         color = texture.Sample(ImageSrg::m_sampler, psInput.m_uv);
         color = texture.Sample(ImageSrg::m_sampler, psInput.m_uv);
 
 
         float3 lightDir;
         float3 lightDir;
         uint lightOffset = 0;
         uint lightOffset = 0;
-        SetFloat3(lightDir, HandleSrg::m_lightHandle, lightOffset); 
+        ReadFromFloatBuffer(lightDir, HandleSrg::m_lightHandle, lightOffset); 
         lightDir = normalize(-lightDir);
         lightDir = normalize(-lightDir);
 
 
         color *= dot(lightDir, psInput.m_normal) * 8.0;
         color *= dot(lightDir, psInput.m_normal) * 8.0;

+ 136 - 0
Shaders/RHI/BindlessPrototypeSrg.azsli

@@ -0,0 +1,136 @@
+/*
+* 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
+
+// NOTE: Nest this array, so Azslc will output a size of the bindingslot to 1
+struct FloatBuffer
+{
+    float buffer;
+};
+
+// Listed on update frequency
+ShaderResourceGroupSemantic FrequencyPerScene 
+{
+    FrequencyId = 6;
+};
+
+ShaderResourceGroupSemantic FloatBufferSemanticId
+{
+    FrequencyId = 7;
+};
+
+ShaderResourceGroup ImageSrg : FrequencyPerScene
+{
+    Sampler m_sampler
+    {
+        MaxAnisotropy = 16;
+        AddressU = Wrap;
+        AddressV = Wrap;
+        AddressW = Wrap;
+    };
+
+    // Array of textures
+    Texture2D m_textureArray[];
+}
+
+ShaderResourceGroup FloatBufferSrg : FloatBufferSemanticId
+{
+    StructuredBuffer<FloatBuffer> m_floatBuffer;
+};
+
+// Helper functions to read data from the FloatBuffer. The FloatBuffer is accessed with an offset and an index.
+// The offset holds the initial offset within the FloatBuffer, and the index is a sub-index, which increments with each property that is being read.
+// The data needs to be read in the same order as it is allocated on the host.
+
+// read floats
+void ReadFromFloatBuffer(out float outFloat, in uint offset, inout uint index)
+{
+    outFloat = FloatBufferSrg::m_floatBuffer[offset + index + 0].buffer;
+    index += 1;
+}
+
+void ReadFromFloatBuffer(out float2 outFloat, in uint offset, inout uint index)
+{
+    outFloat.x = FloatBufferSrg::m_floatBuffer[offset + index + 0].buffer;
+    outFloat.y = FloatBufferSrg::m_floatBuffer[offset + index + 1].buffer;
+    index += 2;
+}
+
+void ReadFromFloatBuffer(out float3 outFloat, in uint offset, inout uint index)
+{
+    outFloat.x = FloatBufferSrg::m_floatBuffer[offset + index + 0].buffer;
+    outFloat.y = FloatBufferSrg::m_floatBuffer[offset + index + 1].buffer;
+    outFloat.z = FloatBufferSrg::m_floatBuffer[offset + index + 2].buffer;
+    index += 3;
+}
+
+void ReadFromFloatBuffer(out float4 outFloat, in uint offset, inout uint index)
+{
+    outFloat.x = FloatBufferSrg::m_floatBuffer[offset + index + 0].buffer;
+    outFloat.y = FloatBufferSrg::m_floatBuffer[offset + index + 1].buffer;
+    outFloat.z = FloatBufferSrg::m_floatBuffer[offset + index + 2].buffer;
+    outFloat.w = FloatBufferSrg::m_floatBuffer[offset + index + 3].buffer;
+    index += 4;
+}
+
+// Read to matrix
+void ReadFromFloatBuffer(out float4x4 outFloat, in uint offset, inout uint index)
+{
+    [unroll(4)] 
+    for(uint i = 0; i < 4; i++)
+    {
+        ReadFromFloatBuffer(outFloat[i], offset, index);
+    }
+}
+
+// read uint
+void ReadFromFloatBuffer(out uint outUInt, in uint offset, inout uint index)
+{
+    outUInt = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 0].buffer);
+    index += 1;
+}
+
+void ReadFromFloatBuffer(out uint2 outUInt, in uint offset, inout uint index)
+{
+    outUInt.x = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 0].buffer);
+    outUInt.y = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 1].buffer);
+    index += 2;
+}
+
+void ReadFromFloatBuffer(out uint3 outUInt, in uint offset, inout uint index)
+{
+    outUInt.x = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 0].buffer);
+    outUInt.y = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 1].buffer);
+    outUInt.z = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 2].buffer);
+    index += 3;
+}
+
+void ReadFromFloatBuffer(out uint4 outUInt, in uint offset, inout uint index)
+{
+    outUInt.x = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 0].buffer);
+    outUInt.y = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 1].buffer);
+    outUInt.z = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 2].buffer);
+    outUInt.w = asuint(FloatBufferSrg::m_floatBuffer[offset + index + 3].buffer);
+    index += 4;
+}
+
+// Read double
+void ReadFromFloatBuffer(out double outDouble, in uint offset, inout uint index)
+{
+    uint lowBits;
+    uint highBits;
+    ReadFromFloatBuffer(highBits, offset, index);
+    ReadFromFloatBuffer(lowBits, offset, index);
+
+    outDouble = asdouble(lowBits, highBits);
+}

+ 4 - 2
Shaders/StaticMesh.azsl

@@ -133,7 +133,9 @@ PixelOutput MainPS(VertexOutput input)
             debugInfo);
             debugInfo);
     }
     }
     
     
-    output.m_diffuse = float4(diffuse.rgb, saturate(baseColor.a));
-    output.m_specular = float4(specular.rgb, saturate(baseColor.a));
+    float clampedAlpha = saturate(baseColor.a);
+    output.m_diffuse = float4(diffuse.rgb, clampedAlpha);
+    output.m_specular = float4(specular.rgb, clampedAlpha);
+
     return output;
     return output;
 }
 }

+ 70 - 0
Standalone/PythonTests/Automated/test_AtomSampleViewer_warp_suite.py

@@ -0,0 +1,70 @@
+"""
+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
+import ly_test_tools.launchers.platforms.base
+
+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]('rhi', ['dx12'])
[email protected]("clean_atomsampleviewer_logs", "atomsampleviewer_log_monitor")
+class TestAutomationWarpSuite:
+
+    def test_AutomatedReviewWARPTestSuite(self, request, workspace, launcher_platform, rhi, project, atomsampleviewer_log_monitor):
+        # Script call setup.
+        test_script = '_AutomatedReviewWARPTestSuite_.bv.lua'
+        test_script_path = os.path.join(workspace.paths.engine_root(), project, 'Scripts', test_script)
+        if not os.path.exists(test_script_path):
+            raise AtomSampleViewerException(f'Test script does not exist in path: {test_script_path}')
+        cmd = os.path.join(workspace.paths.build_directory(),
+                           'AtomSampleViewerStandalone.exe '
+                           '-forceAdapter="Microsoft Basic Render Driver" '
+                           f'--project-path={workspace.paths.project()} '
+                           f'--rhi {rhi} '
+                           f'--runtestsuite scripts/{test_script} '
+                           '--exitontestend')
+
+        def teardown():
+            process_utils.kill_processes_named(['AssetProcessor', 'AtomSampleViewerStandalone'], ignore_extensions=True)
+        request.addfinalizer(teardown)
+
+        # 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.
+                                'Trace::Error']
+            atomsampleviewer_log_monitor.monitor_log_for_lines(
+                unexpected_lines=unexpected_lines, halt_on_unexpected=True, timeout=200)
+        except ly_test_tools.log.log_monitor.LogMonitorException as e:
+            expected_screenshots_path = os.path.join(
+                workspace.paths.engine_root(), "AtomSampleViewer", "Scripts", "ExpectedScreenshots")
+            test_screenshots_path = os.path.join(
+                workspace.paths.project(), "user", "Scripts", "Screenshots")
+            raise AtomSampleViewerException(
+                f"Got error: {e}\n"
+                f"Screenshot comparison check failed using Render Hardware Interface (RHI): '{rhi}'\n"
+                "Please review logs and screenshots at:\n"
+                f"Log file: {atomsampleviewer_log_monitor.file_to_monitor}\n"
+                f"Expected screenshots: {expected_screenshots_path}\n"
+                f"Test screenshots: {test_screenshots_path}\n")

+ 14 - 1
Standalone/PythonTests/CMakeLists.txt

@@ -20,7 +20,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED)
         TEST_REQUIRES gpu
         TEST_REQUIRES gpu
         TEST_SUITE main
         TEST_SUITE main
         TEST_SERIAL
         TEST_SERIAL
-        TIMEOUT 1200
+        TIMEOUT 300
         RUNTIME_DEPENDENCIES
         RUNTIME_DEPENDENCIES
             AssetProcessor
             AssetProcessor
             AssetProcessorBatch
             AssetProcessorBatch
@@ -42,4 +42,17 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED)
             AtomSampleViewer.GameLauncher
             AtomSampleViewer.GameLauncher
             AtomSampleViewer.Assets
             AtomSampleViewer.Assets
     )
     )
+    ly_add_pytest(
+        NAME AtomSampleViewer::PythonWARPTests
+        PATH ${CMAKE_CURRENT_LIST_DIR}/Automated/test_AtomSampleViewer_warp_suite.py
+        TEST_SUITE main
+        TEST_SERIAL
+        TIMEOUT 300
+        RUNTIME_DEPENDENCIES
+            AssetProcessor
+            AssetProcessorBatch
+            AtomSampleViewerStandalone
+            AtomSampleViewer.GameLauncher
+            AtomSampleViewer.Assets
+    )
 endif()
 endif()

+ 7 - 0
Textures/YokohamaCube.license.txt

@@ -0,0 +1,7 @@
+This yokohamCube.dss is modified from the following source
+
+https://www.humus.name/index.php?page=Textures
+https://www.humus.name/index.php?page=Cubemap&item=Yokohama3
+
+These textures are licensed under a Creative Commons Attribution 3.0 Unported License.
+https://creativecommons.org/licenses/by/3.0/