Explorar el Código

Refactor handling of Foveated Image

Add Graphic Binding information to OpenXRVk::XRDeviceDescriptor
Remove Graphic Binding information from OpenXRVk::XRSessionDescriptor
Add FoveatedImage pass class
Add pass mappings to OpenXR
Fix XR builders
Add XRPassRegisterSystemComponent to register FoveatedPass to the Pass System
Add support for injecting the FoveatedImagePass and the Shading Rate Attachment to the OpenXR Pass Templates

Signed-off-by: Akio Gaule <[email protected]>
Akio Gaule hace 1 año
padre
commit
cdd21a6b26

+ 5 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkDevice.h

@@ -11,6 +11,7 @@
 #include <XR/XRDevice.h>
 #include <XR/XRSwapChain.h>
 #include <OpenXRVk_Platform.h>
+#include <Atom/RHI.Reflect/Vulkan/XRVkDescriptors.h>
 
 namespace OpenXRVk
 {
@@ -45,6 +46,9 @@ namespace OpenXRVk
         //! Get the native physical device
         VkPhysicalDevice GetNativePhysicalDevice() const;
 
+        //! Returns the graphic binding for a hardware queue
+        const AZ::Vulkan::XRDeviceDescriptor::GraphicsBinding& GetGraphicsBinding(AZ::RHI::HardwareQueueClass queueClass) const;
+
         //! Reserve space for appropriate number of views 
         void InitXrViews(uint32_t numViews);
 
@@ -69,6 +73,7 @@ namespace OpenXRVk
 
         VkDevice m_xrVkDevice = VK_NULL_HANDLE;
         VkPhysicalDevice m_xrVkPhysicalDevice = VK_NULL_HANDLE;
+        AZStd::array<AZ::Vulkan::XRDeviceDescriptor::GraphicsBinding, AZ::RHI::HardwareQueueClassCount> m_xrQueueBinding;
         XrFrameState m_frameState{ XR_TYPE_FRAME_STATE };
         AZStd::vector<XrCompositionLayerBaseHeader*> m_xrLayers;
         XrCompositionLayerProjection m_xrLayer{ XR_TYPE_COMPOSITION_LAYER_PROJECTION };

+ 2 - 1
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h

@@ -9,6 +9,7 @@
 #pragma once
 
 #include <XR/XRFactory.h>
+#include <OpenXRVk/OpenXRVkInstance.h>
 #include <AzCore/Component/Component.h>
 
 namespace OpenXRVk
@@ -61,6 +62,6 @@ namespace OpenXRVk
         ///////////////////////////////////////////////////////////////////
 
     private:
-        XR::Ptr<XR::Instance> m_instance;
+        XR::Ptr<OpenXRVk::Instance> m_instance;
     };
 }

+ 6 - 1
Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp

@@ -14,7 +14,6 @@
 #include <OpenXRVk/OpenXRVkUtils.h>
 #include <OpenXRVkCommon.h>
 #include <Atom/RHI.Reflect/VkAllocator.h>
-#include <Atom/RHI.Reflect/Vulkan/XRVkDescriptors.h>
 #include <AzCore/Casting/numeric_cast.h>
 
 namespace OpenXRVk
@@ -29,6 +28,7 @@ namespace OpenXRVk
         AZ::Vulkan::XRDeviceDescriptor* xrDeviceDescriptor = static_cast<AZ::Vulkan::XRDeviceDescriptor*>(deviceDescriptor);
         m_xrVkDevice = xrDeviceDescriptor->m_inputData.m_xrVkDevice;
         m_xrVkPhysicalDevice = xrDeviceDescriptor->m_inputData.m_xrVkPhysicalDevice;
+        m_xrQueueBinding = xrDeviceDescriptor->m_inputData.m_xrQueueBinding;
         return AZ::RHI::ResultCode::Success;
     }
 
@@ -182,6 +182,11 @@ namespace OpenXRVk
         return m_xrVkPhysicalDevice;
     }
 
+    const AZ::Vulkan::XRDeviceDescriptor::GraphicsBinding& Device::GetGraphicsBinding(AZ::RHI::HardwareQueueClass queueClass) const
+    {
+        return m_xrQueueBinding[static_cast<uint32_t>(queueClass)];
+    }
+
     AZ::RHI::ResultCode Device::GetViewFov(AZ::u32 viewIndex, AZ::RPI::FovData& outFovData) const
     {
         if(viewIndex < m_projectionLayerViews.size())

+ 4 - 4
Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp

@@ -24,19 +24,19 @@ namespace OpenXRVk
         return aznew Session;
     }
 
-    AZ::RHI::ResultCode Session::InitInternal(AZ::RHI::XRSessionDescriptor* descriptor)
+    AZ::RHI::ResultCode Session::InitInternal([[maybe_unused]] AZ::RHI::XRSessionDescriptor* descriptor)
     {
-        AZ::Vulkan::XRSessionDescriptor* sessionDescriptor = static_cast<AZ::Vulkan::XRSessionDescriptor*>(descriptor);
         Instance* xrVkInstance = static_cast<Instance*>(GetDescriptor().m_instance.get());
         Device* xrVkDevice = static_cast<Device*>(GetDescriptor().m_device.get());
+        auto graphicBinding = xrVkDevice->GetGraphicsBinding(AZ::RHI::HardwareQueueClass::Graphics);
 
         m_xrInstance = xrVkInstance->GetXRInstance();
         AZ_Printf("OpenXRVk", "Creating session...\n");
         m_graphicsBinding.instance = xrVkInstance->GetNativeInstance();
         m_graphicsBinding.physicalDevice = xrVkDevice->GetNativePhysicalDevice();
         m_graphicsBinding.device = xrVkDevice->GetNativeDevice();
-        m_graphicsBinding.queueFamilyIndex = sessionDescriptor->m_inputData.m_graphicsBinding.m_queueFamilyIndex;
-        m_graphicsBinding.queueIndex = sessionDescriptor->m_inputData.m_graphicsBinding.m_queueIndex;
+        m_graphicsBinding.queueFamilyIndex = graphicBinding.m_queueFamilyIndex;
+        m_graphicsBinding.queueIndex = graphicBinding.m_queueIndex;
 
         AZ_Assert(m_xrInstance != XR_NULL_HANDLE, "XR instance is null.");
         AZ_Assert(m_session == XR_NULL_HANDLE, "XR session is already initialized.");

+ 1 - 1
Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp

@@ -84,7 +84,7 @@ namespace OpenXRVk
     {
         if (XR::IsOpenXREnabled())
         {
-            m_instance = CreateInstance();
+            m_instance = AZStd::static_pointer_cast<OpenXRVk::Instance>(CreateInstance());
             //Get the validation mode
             AZ::RHI::ValidationMode validationMode = AZ::RHI::ValidationMode::Disabled;
             AZ::RHI::FactoryManagerBus::BroadcastResult(validationMode, &AZ::RHI::FactoryManagerRequest::DetermineValidationMode);

+ 45 - 0
Gems/XR/Assets/Passes/FoveatedImage.pass

@@ -0,0 +1,45 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "PassAsset",
+    "ClassData": {
+        "PassTemplate": {
+            "Name": "FoveatedImagePassTemplate",
+            "PassClass": "FoveatedImagePass",
+            "Slots": [
+                {
+                    "Name": "FoveatedImageOutput",
+                    "SlotType": "Output",
+                    "ScopeAttachmentUsage": "Copy"
+                }
+            ],
+            "ImageAttachments": [
+                {
+                    "Name": "FoveatedImage",
+                    "Lifetime": "Imported",
+                    "ImageDescriptor": {
+                        "BindFlags": [
+                            "ShadingRate",
+                            "ShaderReadWrite"
+                        ]
+                    },
+                    "SizeSource": {
+                      "Source": {
+                        "Pass": "Parent",
+                        "Attachment": "PipelineOutput"
+                      }
+                    }
+                }
+            ],
+            "Connections": [
+                {
+                  "LocalSlot": "FoveatedImageOutput",
+                  "AttachmentRef": {
+                    "Pass": "This",
+                    "Attachment": "FoveatedImage"
+                  }
+                }
+            ]
+        }
+    }
+}

+ 13 - 0
Gems/XR/Assets/Passes/OpenXRPassTemplates.azasset

@@ -0,0 +1,13 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "AssetAliasesSourceData",
+    "ClassData": {
+        "AssetPaths": [
+            {
+                "Name": "FoveatedImagePassTemplate",
+                "Path": "Passes/FoveatedImage.pass"
+            }
+        ]
+    }
+}

+ 1 - 0
Gems/XR/Code/CMakeLists.txt

@@ -82,6 +82,7 @@ ly_create_alias(NAME XR.Unified NAMESPACE Gem TARGETS Gem::XR)
 
 if(PAL_TRAIT_BUILD_HOST_TOOLS)
     ly_create_alias(NAME XR.Tools NAMESPACE Gem TARGETS Gem::XR)
+    ly_create_alias(NAME XR.Builders NAMESPACE Gem TARGETS Gem::XR)
 endif()
 
 ################################################################################

+ 43 - 0
Gems/XR/Code/Include/XR/XRPassRegisterSystemComponent.h

@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/Component/Component.h>
+
+namespace XR
+{
+    //! This module is in charge of adding the pass classes that are needed for
+    //! managing the shading rate image used on foveated rendering.
+    //! This is separated from the XRSystemComponent because it needs to be initialized
+    //! after the RPI PassSystem.
+    class PassRegisterSystemComponent final
+        : public AZ::Component
+    {
+    public:
+        AZ_COMPONENT(PassRegisterSystemComponent, "{96CCDB81-906A-43EE-A3B5-F955F324D6BF}");
+
+        static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& required);
+        static void Reflect(AZ::ReflectContext* context);
+
+        PassRegisterSystemComponent() = default;
+        ~PassRegisterSystemComponent() override = default;
+
+        //////////////////////////////////////////////////////////////////////////
+        // Component
+        void Activate() override;
+        void Deactivate() override;
+        //////////////////////////////////////////////////////////////////////////
+
+    private:
+        // Load the pass template mappings
+        void LoadPassTemplateMappings() const;
+
+        AZ::RPI::PassSystemInterface::OnReadyLoadTemplatesEvent::Handler m_loadTemplatesHandler;
+    };
+}

+ 14 - 1
Gems/XR/Code/Include/XR/XRSystem.h

@@ -13,7 +13,10 @@
 #include <AzCore/Memory/SystemAllocator.h>
 #include <Atom/RHI/ValidationLayer.h>
 #include <Atom/RHI/XRRenderingInterface.h>
+#include <Atom/RPI.Public/Pass/PassSystemBus.h>
+#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
 #include <Atom/RPI.Public/XR/XRRenderingInterface.h>
+#include <AzFramework/Asset/AssetCatalogBus.h>
 #include <XR/XRDevice.h>
 #include <XR/XRInstance.h>
 #include <XR/XRSwapChain.h>
@@ -28,6 +31,8 @@ namespace XR
         , public AZ::RHI::XRRenderingInterface
         , public AZ::SystemTickBus::Handler
         , public AZStd::intrusive_base
+        , public AZ::RPI::PassSystemTemplateNotificationsBus::MultiHandler
+        , public AzFramework::AssetCatalogEventBus::Handler
     {
     public:
         AZ_CLASS_ALLOCATOR(System, AZ::SystemAllocator);
@@ -80,7 +85,6 @@ namespace XR
         float GetYJoyStickState(AZ::u32 handIndex) const override;
         float GetSqueezeState(AZ::u32 handIndex) const override;
         float GetTriggerState(AZ::u32 handIndex) const override;
-        AZ::Data::Instance<AZ::RPI::AttachmentImage> InitPassFoveatedAttachment(const AZ::RPI::PassTemplate& passTemplate, const AZ::RHI::XRFoveatedLevel* level = nullptr) const override;
         ///////////////////////////////////////////////////////////////////
 
         ///////////////////////////////////////////////////////////////////
@@ -99,14 +103,23 @@ namespace XR
         AZ::RHI::ResultCode InitVariableRateShadingImageContent(AZ::RHI::Image* image, AZ::RHI::XRFoveatedLevel type) const override;
         ///////////////////////////////////////////////////////////////////
 
+        // AzFramework::AssetCatalogEventBus::Handler overrides ...
+        void OnCatalogLoaded(const char*) override;
+
+        // PassSystemTemplateNotificationsBus::Handler overrides ....
+        void OnAddingPassTemplate(const AZStd::shared_ptr<AZ::RPI::PassTemplate>& passTemplate) override;
+
     private:
         Instance* GetInstance();
 
+        bool ConnectToPipelineTemplateListener(const char* pipelineAsset);
+
         Ptr<Instance> m_instance;
         Ptr<Session> m_session;
         Ptr<SwapChain> m_swapChain;
         Ptr<Device> m_device;
         AZ::RHI::ValidationMode m_validationMode = AZ::RHI::ValidationMode::Disabled;
         bool m_isInFrame = false;
+        AZ::RHI::XRFoveatedLevel m_foveatedLevel = AZ::RHI::XRFoveatedLevel::None;
     };
 } // namespace XR

+ 103 - 0
Gems/XR/Code/Source/Passes/FoveatedImagePass.cpp

@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <Passes/FoveatedImagePass.h>
+#include <Atom/RHI/RHISystemInterface.h>
+#include <Atom/RPI.Public/Image/AttachmentImagePool.h>
+#include <Atom/RPI.Public/Image/ImageSystemInterface.h>
+#include <Atom/RPI.Public/Pass/PassUtils.h>
+#include <Atom/RPI.Reflect/Pass/PassName.h>
+#include <AzCore/Settings/SettingsRegistry.h>
+
+namespace XR
+{    
+    AZ::RPI::Ptr<FoveatedImagePass> FoveatedImagePass::Create(const AZ::RPI::PassDescriptor& descriptor)
+    {
+        AZ::RPI::Ptr<FoveatedImagePass> pass = aznew FoveatedImagePass(descriptor);
+        return pass;
+    }
+    
+    FoveatedImagePass::FoveatedImagePass(const AZ::RPI::PassDescriptor& descriptor)
+        : Base(descriptor)
+    {
+        const FoveatedImagePassData* passData = AZ::RPI::PassUtils::GetPassData<FoveatedImagePassData>(descriptor);
+        if (passData)
+        {
+            m_foveatedLevel = passData->m_foveatedLevel;
+        }
+    }
+        
+    void FoveatedImagePass::ResetInternal()
+    {
+        m_foveatedImage.reset();
+        m_foveatedAttachment.reset();
+        Base::ResetInternal();
+    }
+
+    void FoveatedImagePass::BuildInternal()
+    {
+        AZ::RHI::XRRenderingInterface* xrSystem = AZ::RHI::RHISystemInterface::Get()->GetXRSystem();
+        if (xrSystem)
+        {
+            m_foveatedAttachment = FindAttachment(AZ::Name("FoveatedImage"));
+            // Update the image attachment descriptor to sync up size and format
+            m_foveatedAttachment->Update(true);
+            AZ::RHI::ImageDescriptor& imageDesc = m_foveatedAttachment->m_descriptor.m_image;
+
+            // Calculate the size of the shading rate attachment.
+            AZ::RHI::Device* device = AZ::RHI::RHISystemInterface::Get()->GetDevice();
+            const auto& tileSize = device->GetLimits().m_shadingRateTileSize;
+            const uint32_t width = aznumeric_cast<uint32_t>(ceil(static_cast<float>(imageDesc.m_size.m_width) / tileSize.m_width));
+            const uint32_t height = aznumeric_cast<uint32_t>(ceil(static_cast<float>(imageDesc.m_size.m_height) / tileSize.m_height));
+            AZ::RPI::AttachmentImage* currentImage = azrtti_cast<AZ::RPI::AttachmentImage*>(m_foveatedAttachment->m_importedResource.get());
+
+            // If there's a resource already and the size didn't change, just keep using the old AttachmentImage.
+            if (!m_foveatedAttachment->m_importedResource || AZ::RHI::Size(width, height, 1) != currentImage->GetDescriptor().m_size)
+            {
+                AZ::Data::Instance<AZ::RPI::AttachmentImagePool> pool = AZ::RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool();
+
+                imageDesc.m_size.m_width = width;
+                imageDesc.m_size.m_height = height;
+                imageDesc.m_bindFlags |= AZ::RHI::ImageBindFlags::ShadingRate;
+
+                if (imageDesc.m_format == AZ::RHI::Format::Unknown)
+                {
+                    // Find the appropriate format for the image
+                    for (uint32_t i = 0; i < static_cast<uint32_t>(AZ::RHI::Format::Count); ++i)
+                    {
+                        AZ::RHI::Format format = static_cast<AZ::RHI::Format>(i);
+                        AZ::RHI::FormatCapabilities capabilities = device->GetFormatCapabilities(format);
+                        if (AZ::RHI::CheckBitsAll(capabilities, AZ::RHI::FormatCapabilities::ShadingRate))
+                        {
+                            imageDesc.m_format = format;
+                            break;
+                        }
+                    }
+                }
+
+                // The ImageViewDescriptor must be specified to make sure the frame graph compiler doesn't treat this as a transient image.
+                AZ::RHI::ImageViewDescriptor viewDesc = AZ::RHI::ImageViewDescriptor::Create(imageDesc.m_format, 0, 0);
+                viewDesc.m_aspectFlags = AZ::RHI::ImageAspectFlags::Color;
+
+                AZStd::string imageName = AZ::RPI::ConcatPassString(GetPathName(), m_foveatedAttachment->m_path);
+                auto attachmentImage = AZ::RPI::AttachmentImage::Create(*pool.get(), imageDesc, AZ::Name(imageName), nullptr, &viewDesc);
+
+                if (attachmentImage)
+                {
+                    // Fill up the contents of the shading rate image
+                    xrSystem->InitVariableRateShadingImageContent(attachmentImage->GetRHIImage(), m_foveatedLevel);
+
+                    m_foveatedAttachment->m_path = attachmentImage->GetAttachmentId();
+                    m_foveatedAttachment->m_importedResource = attachmentImage;
+                    m_foveatedImage = attachmentImage;
+                }
+            }
+        }
+        Base::BuildInternal();
+    }
+} // namespace XR

+ 62 - 0
Gems/XR/Code/Source/Passes/FoveatedImagePass.h

@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <Atom/RPI.Public/Pass/Pass.h>
+#include <Atom/RHI/XRRenderingInterface.h>
+
+namespace XR
+{
+    static constexpr const char* const FoveatedImagePassClassName = "FoveatedImagePass";
+    static constexpr const char* const FoveatedImagePassTemplateName = "FoveatedImagePassTemplate";
+    static constexpr const char* const FoveatedImageSlotName = "FoveatedImageOutput";
+
+    //! Custom data for the FoveatedImagePass Pass.
+    struct FoveatedImagePassData
+        : public AZ::RPI::PassData
+    {
+        AZ_RTTI(FoveatedImagePassData, "{DA97DB6D-6B41-4285-9266-8A7903887910}", AZ::RPI::PassData);
+        AZ_CLASS_ALLOCATOR(FoveatedImagePassData, AZ::SystemAllocator);
+
+        FoveatedImagePassData() = default;
+        virtual ~FoveatedImagePassData() = default;
+
+        //! Foveated level for the shading rate image
+        AZ::RHI::XRFoveatedLevel m_foveatedLevel = AZ::RHI::XRFoveatedLevel::None;
+    };
+
+    //! This pass handles the initialization of the content of the shading rate image used
+    //! for foveted rendering. This pass doesn't render or compute anything. It just creates the
+    //! shading rate image (based on the pipeline output size and device capabilities) and fills it
+    //! with the proper values depending on the foveated level. It also handles resizes of the
+    //! pipeline output.
+    class FoveatedImagePass : public AZ::RPI::Pass
+    {
+        using Base = AZ::RPI::Pass;
+        AZ_RPI_PASS(FoveatedImagePass);
+        
+    public:
+        AZ_RTTI(FoveatedImagePass, "{C4C33582-21E9-42CE-801B-BE3A1C468A72}", Base);
+        AZ_CLASS_ALLOCATOR(FoveatedImagePass, AZ::SystemAllocator);
+        virtual ~FoveatedImagePass() = default;
+        
+        /// Creates a FoveatedImagePass
+        static AZ::RPI::Ptr<FoveatedImagePass> Create(const AZ::RPI::PassDescriptor& descriptor);
+        
+    private:
+        FoveatedImagePass(const AZ::RPI::PassDescriptor& descriptor);
+        
+        // Pass behavior overrides...
+        void ResetInternal() override;
+        void BuildInternal() override;
+
+        AZ::RPI::Ptr<AZ::RPI::PassAttachment> m_foveatedAttachment;
+        AZ::Data::Instance<AZ::RPI::AttachmentImage> m_foveatedImage;
+        AZ::RHI::XRFoveatedLevel m_foveatedLevel = AZ::RHI::XRFoveatedLevel::None;
+    };
+} // namespace XR

+ 3 - 1
Gems/XR/Code/Source/XRModule.cpp

@@ -9,6 +9,7 @@
 #include <AzCore/Memory/SystemAllocator.h>
 #include <AzCore/Module/Module.h>
 #include <XR/XRSystemComponent.h>
+#include <XR/XRPassRegisterSystemComponent.h>
 
 namespace XR
 {
@@ -25,12 +26,13 @@ namespace XR
                 m_descriptors.end(),
                 {
                     SystemComponent::CreateDescriptor(),
+                    PassRegisterSystemComponent::CreateDescriptor()
                 });
         }
 
         AZ::ComponentTypeList GetRequiredSystemComponents() const override
         {
-            return { azrtti_typeid<XR::SystemComponent>() };
+            return { azrtti_typeid<XR::SystemComponent>(), azrtti_typeid<XR::PassRegisterSystemComponent>() };
         }
     };
 }

+ 51 - 0
Gems/XR/Code/Source/XRPassRegisterSystemComponent.cpp

@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <XR/XRPassRegisterSystemComponent.h>
+#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
+#include <Passes/FoveatedImagePass.h>
+#include <AzCore/Serialization/SerializeContext.h>
+
+namespace XR
+{
+    void PassRegisterSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& required)
+    {
+        // This module uses the PassSystem that is part of the RPI System Module.
+        required.push_back(AZ_CRC_CE("RPISystem"));
+    }
+
+    void PassRegisterSystemComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<PassRegisterSystemComponent, AZ::Component>()->Version(1);
+        }
+    }
+
+    void PassRegisterSystemComponent::Activate()
+    {
+        auto* passSystem = AZ::RPI::PassSystemInterface::Get();
+        AZ_Assert(passSystem, "Cannot get the pass system.");
+        passSystem->AddPassCreator(AZ::Name(FoveatedImagePassClassName), &FoveatedImagePass::Create);
+
+        // Setup handler for load pass template mappings
+        m_loadTemplatesHandler = AZ::RPI::PassSystemInterface::OnReadyLoadTemplatesEvent::Handler([this]() { this->LoadPassTemplateMappings(); });
+        passSystem->ConnectEvent(m_loadTemplatesHandler);
+    }
+
+    void PassRegisterSystemComponent::Deactivate()
+    {
+        m_loadTemplatesHandler.Disconnect();
+    }
+
+    void PassRegisterSystemComponent::LoadPassTemplateMappings() const
+    {
+        const char* passTemplatesFile = "Passes/OpenXRPassTemplates.azasset";
+        AZ::RPI::PassSystemInterface::Get()->LoadPassTemplateMappings(passTemplatesFile);
+    }
+}

+ 103 - 76
Gems/XR/Code/Source/XRSystem.cpp

@@ -13,6 +13,7 @@
 #include <XR/XRFactory.h>
 #include <XR/XRSystem.h>
 #include <XR/XRUtils.h>
+#include <Passes/FoveatedImagePass.h>
 #include <Atom/RHI/Image.h>
 #include <Atom/RHI/ImagePool.h>
 #include <Atom/RHI/RHISystemInterface.h>
@@ -23,10 +24,13 @@
 #include <Atom/RPI.Reflect/Image/AttachmentImageAsset.h>
 #include <Atom/RPI.Reflect/Image/AttachmentImageAssetCreator.h>
 #include <Atom/RPI.Reflect/Pass/PassTemplate.h>
+#include <Atom/RPI.Reflect/System/RenderPipelineDescriptor.h>
 
 
 namespace XR
 {
+    static constexpr const char* const FoveatedAttachmentSlotName = "FoveatedImageInput";
+
 #if AZ_TRAIT_OS_IS_HOST_OS_PLATFORM
     AZ_CVAR(
         bool,
@@ -37,11 +41,26 @@ namespace XR
         "When an XR system is present in a host platform, this will enable the regular render pipeline on the host PC as well "
         "(true by default).");
 #endif
+    AZ_CVAR(AZ::CVarFixedString, r_default_openxr_foveated_pass_template, "MultiViewForwardPassTemplate", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Pass template to add foveated rendering");
 
     void System::Init(const System::Descriptor& descriptor)
     {
         m_validationMode = descriptor.m_validationMode;
         AZ::SystemTickBus::Handler::BusConnect();
+
+        // Check settings registry for the foveated level
+        if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get())
+        {
+            if (AZ::u64 foveatedLevel; settingsRegistry->Get(foveatedLevel, AZ::RHI::XRFoveatedLevelKey))
+            {
+                AZ_Assert(
+                    foveatedLevel <= static_cast<AZ::u64>(AZ::RHI::XRFoveatedLevel::High),
+                    "Invalid foveated level %d", static_cast<int>(foveatedLevel));
+                m_foveatedLevel = static_cast<AZ::RHI::XRFoveatedLevel>(foveatedLevel);
+            }
+        }
+
+        AzFramework::AssetCatalogEventBus::Handler::BusConnect();
     }
 
     AZ::RHI::ResultCode System::InitInstance()
@@ -289,82 +308,6 @@ namespace XR
         return 0.0f;
     }
 
-    AZ::Data::Instance<AZ::RPI::AttachmentImage> System::InitPassFoveatedAttachment(const AZ::RPI::PassTemplate& passTemplate, const AZ::RHI::XRFoveatedLevel* level) const
-    {
-        // Need to fill the contents of the Variable shade rating image.
-        // Find the Shading Rate Attachment
-        AZ::Data::Asset<AZ::RPI::AttachmentImageAsset> vrsImageAsset;
-        for (const auto& imageAttachment : passTemplate.m_imageAttachments)
-        {
-            if (AZ::RHI::CheckBitsAll(imageAttachment.m_imageDescriptor.m_bindFlags, AZ::RHI::ImageBindFlags::ShadingRate))
-            {
-                vrsImageAsset = AZ::RPI::AssetUtils::LoadAssetById<AZ::RPI::AttachmentImageAsset>(imageAttachment.m_assetRef.m_assetId, AZ::RPI::AssetUtils::TraceLevel::Error);
-                break;
-            }
-        }
-
-        AZ::Data::Instance<AZ::RPI::AttachmentImage> textureAsset;
-        if (vrsImageAsset && vrsImageAsset.IsReady())
-        {
-            AZ::RHI::Device* device = AZ::RHI::RHISystemInterface::Get()->GetDevice();
-            // Resize the image to match the proper tile size
-            AZ::u32 outputWidth = GetSwapChainWidth(1);
-            AZ::u32 outputHeight = GetSwapChainHeight(1);
-
-            const auto& tileSize = device->GetLimits().m_shadingRateTileSize;
-            AZ::RHI::ImageDescriptor imageDescriptor = vrsImageAsset->GetImageDescriptor();
-            imageDescriptor.m_size.m_width = aznumeric_cast<uint32_t>(ceil(static_cast<float>(outputWidth) / tileSize.m_width));
-            imageDescriptor.m_size.m_height = aznumeric_cast<uint32_t>(ceil(static_cast<float>(outputHeight) / tileSize.m_height));
-
-            // Find the appropriate format for the image
-            for (uint32_t i = 0; i < static_cast<uint32_t>(AZ::RHI::Format::Count); ++i)
-            {
-                AZ::RHI::Format format = static_cast<AZ::RHI::Format>(i);
-                AZ::RHI::FormatCapabilities capabilities = device->GetFormatCapabilities(format);
-                if (AZ::RHI::CheckBitsAll(capabilities, AZ::RHI::FormatCapabilities::ShadingRate))
-                {
-                    imageDescriptor.m_format = format;
-                    break;
-                }
-            }
-
-            // Create the new asset with the proper size and format and register it in the AZ::RPI::AttachmentImage database
-            AZ::RPI::AttachmentImageAssetCreator imageAssetCreator;
-            imageAssetCreator.Begin(vrsImageAsset->GetId());
-            imageAssetCreator.SetImageDescriptor(imageDescriptor);
-            imageAssetCreator.SetName(vrsImageAsset->GetName(), vrsImageAsset->HasUniqueName());
-
-            AZ::Data::Asset<AZ::RPI::AttachmentImageAsset> asset;
-            if (imageAssetCreator.End(asset))
-            {
-                textureAsset = AZ::RPI::AttachmentImage::FindOrCreate(asset);
-                AZ::RHI::XRFoveatedLevel foveatedType = AZ::RHI::XRFoveatedLevel::None;
-                if (level)
-                {
-                    foveatedType = *level;
-                }
-                else
-                {
-                    // Check settings registry for the foveated level
-                    if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get())
-                    {
-                        if (AZ::u64 foveatedLevel; settingsRegistry->Get(foveatedLevel, AZ::RHI::XRFoveatedLevelKey))
-                        {
-                            AZ_Assert(
-                                foveatedLevel <= static_cast<AZ::u64>(AZ::RHI::XRFoveatedLevel::High),
-                                "Invalid foveated level %d", static_cast<int>(foveatedLevel));
-                            foveatedType = static_cast<AZ::RHI::XRFoveatedLevel>(foveatedLevel);
-                        }
-                    }
-                }
-                // Fill up the contents of the shading rate image
-                InitVariableRateShadingImageContent(textureAsset->GetRHIImage(), foveatedType);
-            }
-        }
-
-        return textureAsset;
-    }
-
     float System::GetXButtonState() const
     {
         if (m_session->IsSessionRunning())
@@ -433,6 +376,8 @@ namespace XR
 
     void System::Shutdown()
     {
+        AZ::RPI::PassSystemTemplateNotificationsBus::MultiHandler::BusDisconnect();
+        AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
         AZ::SystemTickBus::Handler::BusDisconnect();
         m_instance = nullptr;
         m_device = nullptr;
@@ -599,6 +544,64 @@ namespace XR
         return imagePool->UpdateImageContents(request);
     }
 
+    void System::OnCatalogLoaded(const char*)
+    {        
+        // Check device features first
+        if (AZ::RHI::Device* device = AZ::RHI::RHISystemInterface::Get()->GetDevice(); 
+            m_foveatedLevel != AZ::RHI::XRFoveatedLevel::None &&
+            AZ::RHI::CheckBitsAll(device->GetFeatures().m_shadingRateTypeMask, AZ::RHI::ShadingRateTypeFlags::PerRegion))
+        {
+            // Start listening for the openxr pipeline and the r_default_openxr_foveated_pass_template so we can add the shading rate attachment.
+            if (ConnectToPipelineTemplateListener("r_default_openxr_left_pipeline_name") &&
+                ConnectToPipelineTemplateListener("r_default_openxr_right_pipeline_name"))
+            {
+                AZ::RPI::PassSystemTemplateNotificationsBus::MultiHandler::BusConnect(AZ::Name(static_cast<AZ::CVarFixedString>(r_default_openxr_foveated_pass_template)));
+            }
+        }
+    }
+
+    void System::OnAddingPassTemplate(const AZStd::shared_ptr<AZ::RPI::PassTemplate>& passTemplate)
+    {
+        AZ::Name foveatedPassTemplate = AZ::Name(static_cast<AZ::CVarFixedString>(r_default_openxr_foveated_pass_template));
+        if (passTemplate->m_name == foveatedPassTemplate)
+        {
+            // Add the Shading Rate Attachment to the r_default_openxr_foveated_pass_template Pass
+            AZ::RPI::PassSlot slot;
+            slot.m_name = AZ::Name(FoveatedAttachmentSlotName);
+            slot.m_slotType = AZ::RPI::PassSlotType::Input;
+            slot.m_scopeAttachmentUsage = AZ::RHI::ScopeAttachmentUsage::ShadingRate;
+            slot.m_loadStoreAction.m_storeAction = AZ::RHI::AttachmentStoreAction::DontCare;
+            passTemplate->AddSlot(slot);
+        }
+        else
+        {
+            // Need to add the FoveatedImagePass that is in charge of initialization of the foveated image
+            auto findIt = AZStd::find_if(passTemplate->m_passRequests.begin(), passTemplate->m_passRequests.end(), [&](AZ::RPI::PassRequest& request)
+                {
+                    return request.m_templateName == foveatedPassTemplate;
+                });
+
+            if (findIt != passTemplate->m_passRequests.end())
+            {
+                // Add the connection between the FoveatedImagePass and the r_default_openxr_foveated_pass_template
+                AZ::RPI::PassConnection connection;
+                connection.m_localSlot = AZ::Name(FoveatedAttachmentSlotName);
+                connection.m_attachmentRef.m_pass = "FoveatedImagePass";
+                connection.m_attachmentRef.m_attachment = AZ::Name(FoveatedImageSlotName);
+                findIt->AddInputConnection(connection);
+
+                // Add the FoveatedImagePass to the OpenXR pipeline
+                AZ::RPI::PassRequest request;
+                request.m_passName = connection.m_attachmentRef.m_pass;
+                request.m_templateName = AZ::Name(FoveatedImagePassTemplateName);
+                AZStd::shared_ptr<FoveatedImagePassData> passData = AZStd::make_shared<FoveatedImagePassData>();
+                passData->m_foveatedLevel = m_foveatedLevel;
+                request.m_passData = passData;
+                passTemplate->m_passRequests.insert(findIt, request);
+            }
+        }
+    }
+
     Instance* System::GetInstance()
     {
         if (!m_instance)
@@ -607,4 +610,28 @@ namespace XR
         }
         return m_instance.get();
     }
+    bool System::ConnectToPipelineTemplateListener(const char* pipelineAsseCvar)
+    {
+        // Get the pipeline asset so we can get the root template.
+        // We will add the FoveatedImagePass to the root template of the pipeline.
+        auto* console = AZ::Interface<AZ::IConsole>::Get();
+        AZ::CVarFixedString pipelineName;
+        auto result = console->GetCvarValue(pipelineAsseCvar, pipelineName);
+        if (result != AZ::GetValueResult::Success)
+        {
+            return false;
+        }
+        AZ::Data::Asset<AZ::RPI::AnyAsset> pipelineAsset =
+            AZ::RPI::AssetUtils::LoadCriticalAsset<AZ::RPI::AnyAsset>(pipelineName.data(), AZ::RPI::AssetUtils::TraceLevel::Error);
+        if (!pipelineAsset)
+        {
+            return false;
+        }
+
+        AZ::RPI::RenderPipelineDescriptor renderPipelineDescriptor =
+            *AZ::RPI::GetDataFromAnyAsset<AZ::RPI::RenderPipelineDescriptor>(pipelineAsset); // Copy descriptor from asset
+        pipelineAsset.Release();
+        AZ::RPI::PassSystemTemplateNotificationsBus::MultiHandler::BusConnect(AZ::Name(renderPipelineDescriptor.m_rootPassTemplate));
+        return true;
+    }
 }

+ 4 - 0
Gems/XR/Code/xr_private_common_files.cmake

@@ -19,6 +19,7 @@ set(FILES
     Include/XR/XRSystemComponent.h
     Include/XR/XRUtils.h
     Include/XR/XRObject.h
+    Include/XR/XRPassRegisterSystemComponent.h
     Source/XRDevice.cpp
     Source/XRFactory.cpp
     Source/XRInput.cpp
@@ -29,4 +30,7 @@ set(FILES
     Source/XRSystem.cpp
     Source/XRSystemComponent.cpp
     Source/XRUtils.cpp
+    Source/XRPassRegisterSystemComponent.cpp
+    Source/Passes/FoveatedImagePass.h
+    Source/Passes/FoveatedImagePass.cpp
 )