Browse Source

Next set of changes for OpenXr Vulkan support. (#6)

* Next set of changes for OpenXr Vulkan support.
 - Support for multiple swapchains and swapchain images creation + synchronization with gpu/cpu
 - Support for Session creation
 - Support for Input as as polling events. Amongst other things it will track controller input/view input, orientation, scale
 - Support for tracking multiple spaces
 - Support for creating VR specific projection matrix
 - Support for implementing the newly introduced rendering interface exposed to Atom
 - Other various misc cleanup work

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

* Added some comments

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

* Addressed feedback plus other cleanup

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

* Address minor feedback

Signed-off-by: moudgils <[email protected]>
moudgils 3 years ago
parent
commit
10bd2a49ff

+ 33 - 1
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkDevice.h

@@ -9,6 +9,7 @@
 #pragma once
 
 #include <XR/XRDevice.h>
+#include <XR/XRSwapChain.h>
 #include <OpenXRVk_Platform.h>
 
 namespace OpenXRVk
@@ -16,7 +17,7 @@ namespace OpenXRVk
     //! Vulkan specific XR device back-end class that will help manage 
     //! xr specific vulkan native objects related to device.
     class Device final
-    : public XR::Device
+        : public XR::Device
     {
     public:
         AZ_CLASS_ALLOCATOR(Device, AZ::SystemAllocator, 0);
@@ -28,13 +29,44 @@ namespace OpenXRVk
         // XR::Device overrides
         // Create the xr specific native device object and populate the XRDeviceDescriptor with it.
         AZ::RHI::ResultCode InitDeviceInternal(AZ::RHI::XRDeviceDescriptor* instanceDescriptor) override;
+        //! Get the Fov data  of the view specified by view index
+        AZ::RPI::FovData GetViewFov(AZ::u32 viewIndex) const override;
+        //! Get the Pose data  of the view specified by view index
+        AZ::RPI::PoseData GetViewPose(AZ::u32 viewIndex) const override;
         //////////////////////////////////////////////////////////////////////////
 
+        //! Returns true if rendering data is valid for the current frame.
+        bool ShouldRender() const;
+
+        //! Get the native device
+        VkDevice GetNativeDevice() const;
+
+        //! Reserve space for appropriate number of views 
+        void InitXrViews(uint32_t numViews);
+
+        //! Get the anticipated display XrTime for the next application-generated frame.
+        XrTime GetPredictedDisplayTime() const;
+
     private:
 
+        //////////////////////////////////////////////////////////////////////////
+        // XR::Device overrides
         //! Clean native objects.
         void ShutdownInternal() override;
+        //! Inform the drivers that the frame is beginning
+        bool BeginFrameInternal() override;
+        //! Release the oldest swapchain image and inform the drivers that the frame is ending 
+        void EndFrameInternal(XR::Ptr<XR::SwapChain>) override;
+        //! Locate views, acquire swapchain image and synchronize gpu with cpu
+        bool AcquireSwapChainImageInternal(AZ::u32 viewIndex, XR::SwapChain* baseSwapChain) override;
+        //////////////////////////////////////////////////////////////////////////
 
         VkDevice m_xrVkDevice = VK_NULL_HANDLE;
+        XrFrameState m_frameState{ XR_TYPE_FRAME_STATE };
+        AZStd::vector<XrCompositionLayerBaseHeader*> m_xrLayers;
+        XrCompositionLayerProjection m_xrLayer{ XR_TYPE_COMPOSITION_LAYER_PROJECTION };
+        AZStd::vector<XrCompositionLayerProjectionView> m_projectionLayerViews;
+        AZStd::vector<XrView> m_views;
+        uint32_t m_viewCountOutput = 0;
     };
 }

+ 0 - 37
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkGraphicsBinding.h

@@ -1,37 +0,0 @@
-/*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
-
-#pragma once
-
-#include <XR/XRGraphicsBinding.h>
-#include <OpenXRVk_Platform.h>
-
-namespace OpenXRVk
-{
-    //todo:: Pull this in if needed or remove
-    /*
-    class GraphicsBindingDescriptor final
-        : public XR::GraphicsBindingDescriptor
-    {
-    public:
-        AZ_CLASS_ALLOCATOR(GraphicsBindingDescriptor, AZ::SystemAllocator, 0);
-        AZ_RTTI(GraphicsBindingDescriptor, "{1083C93E-FB2B-4441-B705-5C44427F2961}", XR::GraphicsBindingDescriptor);
-
-        XrGraphicsBindingVulkan2KHR m_graphicsBinding{ XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR };
-    };
-    
-    class GraphicsBinding final
-        : public XR::GraphicsBinding
-    {
-    public:
-        AZ_CLASS_ALLOCATOR(GraphicsBinding, AZ::SystemAllocator, 0);
-        AZ_RTTI(GraphicsBinding, "{1001E681-EA2E-4898-AC08-B93AA5B63508}", XR::GraphicsBinding);
-
-    };
-    */
-}

+ 59 - 37
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInput.h

@@ -13,21 +13,6 @@
 
 namespace OpenXRVk
 {
-    //Todo: Pull this in when needed or remove
-    /*
-    class InputDescriptor final
-        : public XR::InputDescriptor
-    {
-    public:
-        AZ_CLASS_ALLOCATOR(InputDescriptor, AZ::SystemAllocator, 0);
-        AZ_RTTI(InputDescriptor, "{5CE5E693-775B-42A5-9B32-7C1006C69975}", XR::InputDescriptor);
-
-        InputDescriptor() = default;
-        virtual ~InputDescriptor() = default;
-
-        //any extra info for a generic xr InputDescriptor
-    };
-
     // Class that will help manage XrActionSet/XrAction
     class Input final
         : public XR::Input
@@ -36,31 +21,68 @@ namespace OpenXRVk
         AZ_CLASS_ALLOCATOR(Input, AZ::SystemAllocator, 0);
         AZ_RTTI(Input, "{97ADD1FE-27DF-4F36-9F61-683F881F9477}", XR::Input);
 
-        Input() = default;
-        virtual ~Input() = default;
-
-        static AZStd::intrusive_ptr<XR::Input> Create();
-
-        AZ::RHI::ResultCode Init() override;
-        void InitializeActions() override;
+        static XR::Ptr<Input> Create();
+    
+        //! Sync all the actions and update controller
+        //! as well as various tracked space poses 
         void PollActions() override;
-        void PollEvents() override;
+
+        //! Initialize various actions/actions sets and add support for Oculus touch bindings
         AZ::RHI::ResultCode InitInternal() override;
 
+        //! Create controller action spaces
+        AZ::RHI::ResultCode InitializeActionSpace(XrSession xrSession);
+
+        //! Attach action sets
+        AZ::RHI::ResultCode InitializeActionSets(XrSession xrSession);
+
+        //! Update Controller space information
+        void LocateControllerSpace(XrTime predictedDisplayTime, XrSpace baseSpace, AZ::u32 handIndex);
+
+        //! Update information for a specific tracked space type (i.e visualizedSpaceType)
+        void LocateVisualizedSpace(XrTime predictedDisplayTime, XrSpace space, XrSpace baseSpace, OpenXRVk::SpaceType visualizedSpaceType);
+
+        //! Return Pose data for a controller attached to a view index
+        AZ::RPI::PoseData GetControllerPose(AZ::u32 viewIndex) const;
+
+        //! Return scale for a controller attached to a view index
+        float GetControllerScale(AZ::u32 viewIndex) const;
+
+        //! Return Pose data for a tracked space type (i.e visualizedSpaceType)
+        AZ::RPI::PoseData GetVisualizedSpacePose(OpenXRVk::SpaceType visualizedSpaceType) const;
+
+        //! Get the Grab action
+        XrAction GetGrabAction() const;
+
+        //! Get the Pose action
+        XrAction GetPoseAction() const;
+
+        //! Get the Vibration action
+        XrAction GetVibrationAction() const;
+
+        //! Get the Quit action
+        XrAction GetQuitAction() const;
     private:
-        struct InputState
-        {
-            XrActionSet actionSet{ XR_NULL_HANDLE };
-            XrAction grabAction{ XR_NULL_HANDLE };
-            XrAction poseAction{ XR_NULL_HANDLE };
-            XrAction vibrateAction{ XR_NULL_HANDLE };
-            XrAction quitAction{ XR_NULL_HANDLE };
-            AZStd::array<XrPath, 2> handSubactionPath;
-            AZStd::array<XrSpace, 2> handSpace;
-            AZStd::array<float, 2> handScale = { { 1.0f, 1.0f } };
-            AZStd::array<XrBool32, 2> handActive;
-        };
-        InputState m_input;
+
+        //! Create a XrAction
+        void CreateAction(XrAction& action, XrActionType actionType,
+                          const char* actionName, const char* localizedActionName,
+                          uint32_t countSubactionPathCount, const XrPath* subActionPaths);
+
+        //! Destroy native objects
+        void ShutdownInternal() override;
+
+        XrActionSet m_actionSet{ XR_NULL_HANDLE };
+        XrAction m_grabAction{ XR_NULL_HANDLE };
+        XrAction m_poseAction{ XR_NULL_HANDLE };
+        XrAction m_vibrateAction{ XR_NULL_HANDLE };
+        XrAction m_quitAction{ XR_NULL_HANDLE };
+        AZStd::array<XrPath, 2> m_handSubactionPath;
+        AZStd::array<XrSpace, 2> m_handSpace;
+        AZStd::array<float, 2> m_handScale = { { 1.0f, 1.0f } };
+        AZStd::array<XrBool32, 2> m_handActive;
+
+        AZStd::array<XrSpaceLocation, 2> m_handSpaceLocation;
+        AZStd::array<XrSpaceLocation, SpaceType::Count> m_xrVisualizedSpaceLocations;
     };
-    */
 }

+ 12 - 15
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInstance.h

@@ -26,16 +26,13 @@ namespace OpenXRVk
         AZ_CLASS_ALLOCATOR(Instance, AZ::SystemAllocator, 0);
         AZ_RTTI(Instance, "{1A62DF32-2909-431C-A938-B1E841A50768}", XR::Instance);
 
-        Instance() = default;
-        ~Instance() = default;
-
         static XR::Ptr<Instance> Create();
 
         //////////////////////////////////////////////////////////////////////////
         // XR::Instance overrides
         AZ::RHI::ResultCode InitInstanceInternal(AZ::RHI::ValidationMode m_validationMode) override;
         AZ::RHI::ResultCode InitNativeInstance(AZ::RHI::XRInstanceDescriptor* instanceDescriptor) override;
-        AZ::u32 GetNumPhysicalDevices() override;
+        AZ::u32 GetNumPhysicalDevices() const override;
         AZ::RHI::ResultCode GetXRPhysicalDevice(AZ::RHI::XRPhysicalDeviceDescriptor* physicalDeviceDescriptor, int32_t index) override;
         //////////////////////////////////////////////////////////////////////////
 		
@@ -52,34 +49,34 @@ namespace OpenXRVk
         void LogEnvironmentBlendMode(XrViewConfigurationType type);
 
         //! Get the XRInstance.
-        XrInstance GetXRInstance();
+        XrInstance GetXRInstance() const;
 
         //! Get System id.
-        XrSystemId GetXRSystemId();
+        XrSystemId GetXRSystemId() const;
 
         //! Get native VkInstance.
-        VkInstance GetVkInstance();
+        VkInstance GetNativeInstance() const;
 
         //! Get XR environment blend mode.
-        XrEnvironmentBlendMode GetEnvironmentBlendMode();
+        XrEnvironmentBlendMode GetEnvironmentBlendMode() const;
 
         //! Get XR configuration type.
-        XrViewConfigurationType GetViewConfigType();
+        XrViewConfigurationType GetViewConfigType() const;
 
         //! Ge the active VkPhysicalDevice.
-        VkPhysicalDevice GetActivePhysicalDevice();
+        VkPhysicalDevice GetActivePhysicalDevice() const;
 
     private:
 
         //! Clean native objects. 
         void ShutdownInternal() override;
 
-        XrInstance m_xrInstance{ XR_NULL_HANDLE };
+        XrInstance m_xrInstance = XR_NULL_HANDLE;
         VkInstance m_xrVkInstance = VK_NULL_HANDLE;
-        XrFormFactor m_formFactor{ XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY };
-        XrViewConfigurationType m_viewConfigType{ XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO };
-        XrEnvironmentBlendMode m_environmentBlendMode{ XR_ENVIRONMENT_BLEND_MODE_OPAQUE };
-        XrSystemId m_xrSystemId{ XR_NULL_SYSTEM_ID };
+        XrFormFactor m_formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
+        XrViewConfigurationType m_viewConfigType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
+        XrEnvironmentBlendMode m_environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
+        XrSystemId m_xrSystemId = XR_NULL_SYSTEM_ID;
         XR::RawStringList m_requiredLayers;
         XR::RawStringList m_requiredExtensions;
         AZStd::unique_ptr<FunctionLoader> m_functionLoader;

+ 43 - 30
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h

@@ -8,27 +8,13 @@
 
 #pragma once
 
-#include <XR/XRSession.h>
 #include <AzCore/std/smart_ptr/intrusive_ptr.h>
 #include <OpenXRVk_Platform.h>
+#include <OpenXRVk/OpenXRVkSpace.h>
+#include <XR/XRSession.h>
 
 namespace OpenXRVk
 {
-    //Todo: Pull this in when needed or remove
-    /*
-    class SessionDescriptor final
-        : public XR::SessionDescriptor 
-    {
-    public:
-        AZ_CLASS_ALLOCATOR(SessionDescriptor, AZ::SystemAllocator, 0);
-        AZ_RTTI(SessionDescriptor, "{775CCED3-9676-4F48-B419-BDADE0F7F447}", XR::SessionDescriptor);
-
-        SessionDescriptor() = default;
-        virtual ~SessionDescriptor() = default;
-
-        //any openxr specific session descriptor data
-    };
-
     // Class that will help manage XrSession
     class Session final
         : public XR::Session
@@ -37,23 +23,50 @@ namespace OpenXRVk
         AZ_CLASS_ALLOCATOR(Session, AZ::SystemAllocator, 0);
         AZ_RTTI(Session, "{6C899F0C-9A3D-4D79-8E4F-92AFB67E5EB1}", XR::Session);
 
-        static AZStd::intrusive_ptr<Session> Create();
+        static XR::Ptr<Session> Create();
 
+        //! Print out all the supported Reference space
         void LogReferenceSpaces();
-        void HandleSessionStateChangedEvent(
-            const XrEventDataSessionStateChanged& stateChangedEvent,
-            bool* exitRenderLoop,
-            bool* requestRestart);
-        XrSession GetXrSession();
-        virtual bool IsSessionRunning() const override;
-        virtual bool IsSessionFocused() const override;
-        virtual AZ::RHI::ResultCode InitInternal();
+
+        //! Process session state when it is updated
+        void HandleSessionStateChangedEvent(const XrEventDataSessionStateChanged& stateChangedEvent);
+
+        //! Try and poll the next event 
+        const XrEventDataBaseHeader* TryReadNextEvent();
+
+        //! Return the native session
+        XrSession GetXrSession() const;
+
+        //! Return the Xrspace related to the SpaceType enum
+        XrSpace GetXrSpace(SpaceType spaceType) const;
+
+        //////////////////////////////////////////////////////////////////////////
+        // XR::Session overrides
+        AZ::RHI::ResultCode InitInternal(AZ::RHI::XRSessionDescriptor* descriptor) override;
+        bool IsSessionRunning() const override;
+        bool IsSessionFocused() const override;
+        bool IsRestartRequested() const override;
+        bool IsExitRenderLoopRequested() const override;
+        void PollEvents() override;
+        void LocateControllerSpace(AZ::u32 handIndex) override;
+        AZ::RPI::PoseData GetControllerPose(AZ::u32 handIndex) const override;
+        AZ::RPI::PoseData GetViewFrontPose() const override;
+        float GetControllerScale(AZ::u32 handIndex) const override;
+        //////////////////////////////////////////////////////////////////////////
 
     private:
-        XrSession m_session{ XR_NULL_HANDLE };
-        // Application's current lifecycle state according to the runtime
-        XrSessionState m_sessionState{ XR_SESSION_STATE_UNKNOWN };
-        XrFrameState m_frameState{ XR_TYPE_FRAME_STATE };
+
+        void ShutdownInternal() override;
+        void LogActionSourceName(XrAction action, const AZStd::string_view actionName) const;
+        
+        XrSession m_session = XR_NULL_HANDLE;
+        XrSessionState m_sessionState = XR_SESSION_STATE_UNKNOWN;
+        XrEventDataBuffer m_eventDataBuffer;
+        XrInstance m_xrInstance = XR_NULL_HANDLE;
+        XrGraphicsBindingVulkan2KHR m_graphicsBinding{ XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR };
+
+        bool m_sessionRunning = false;
+        bool m_exitRenderLoop = false;
+        bool m_requestRestart = false;        
     };
-    */
 }

+ 32 - 4
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSpace.h

@@ -10,11 +10,24 @@
 
 
 #include <XR/XRSpace.h>
+#include <XR/XRBase.h>
 #include <OpenXRVk_Platform.h>
+#include <AzCore/Preprocessor/Enum.h>
 
 namespace OpenXRVk
 {
-    // Class that will help manage XrSpaces
+    AZ_ENUM(SpaceType,
+        View,
+        ViewFront,
+        Local,
+        Stage,
+        StageLeft,
+        StageRight,
+        StageLeftRotated,
+        StageRightRotated,
+        Count);
+
+    //!This class is responsible for managing specific space coordinates tracked by the device
     class Space final
         : public XR::Space
     {
@@ -22,11 +35,26 @@ namespace OpenXRVk
         AZ_CLASS_ALLOCATOR(Space, AZ::SystemAllocator, 0);
         AZ_RTTI(Space, "{E99557D0-9061-4691-9524-CE0ACC3A14FA}", XR::Space);
         
-        static AZStd::intrusive_ptr<Space> Create();
+        static XR::Ptr<Space> Create();           
+        AZ::RHI::ResultCode InitInternal() override;
+        void ShutdownInternal() override;
+    
+        //!Initialize XrSpace per SpaceType we want to track
+        void CreateVisualizedSpaces(XrSession xrSession);
+
+        //! Return the XrReferenceSpaceCreateInfo associated with each SpaceType
+        XrReferenceSpaceCreateInfo GetXrReferenceSpaceCreateInfo(SpaceType spaceType);
 
-        //XrSpaceLocation GetSpace(XrSpace space);
+        //! Get the XrSpace for a given SpaceType
+        XrSpace GetXrSpace(SpaceType spaceType) const;
 
     private:
-        XrSpace m_baseSpace{ XR_NULL_HANDLE };
+
+        //! XrPose specific matrix translation, Rotation functions
+        XrPosef Identity();
+        XrPosef Translation(const XrVector3f& translation);
+        XrPosef RotateCCWAboutYAxis(float radians, XrVector3f translation);
+
+        AZStd::vector<XrSpace> m_xrSpaces;
     };
 }

+ 50 - 45
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSwapChain.h

@@ -13,35 +13,7 @@
 
 namespace OpenXRVk
 {
-    //Pull this in if needed or remove
-    /*
-    class SwapChainDescriptor final
-        : public XR::SwapChainDescriptor
-    {
-    public:
-        AZ_CLASS_ALLOCATOR(SwapChainDescriptor, AZ::SystemAllocator, 0);
-        AZ_RTTI(SwapChainDescriptor, "{0C6214B3-9271-4972-B6B0-13C4A23D9155}", XR::SwapChainDescriptor);
-
-        SwapChainDescriptor() = default;
-        virtual ~SwapChainDescriptor() = default;
-
-        //any extra info for a openxr swap chain descriptor
-    };
-
-    class SwapChainImageDescriptor final
-        : public XR::SwapChainImageDescriptor
-    {
-    public:
-        AZ_CLASS_ALLOCATOR(SwapChainImageDescriptor, AZ::SystemAllocator, 0);
-        AZ_RTTI(SwapChainImageDescriptor, "{056D30CF-4B1E-4EC3-9990-A7D9C38C895B}", XR::SwapChainImageDescriptor);
-
-        SwapChainImageDescriptor() = default;
-        virtual ~SwapChainImageDescriptor() = default;
-
-        //any extra info for a openxr swap chain image descriptor
-    };
-
-    // Class that will help manage XrSwapchain
+    //! Class that will help manage native xr swapchains and swapchain images
     class SwapChain final
         : public XR::SwapChain
     {
@@ -49,11 +21,9 @@ namespace OpenXRVk
         AZ_CLASS_ALLOCATOR(SwapChain, AZ::SystemAllocator, 0);
         AZ_RTTI(SwapChain, "{3DD88236-8C9F-4864-86F5-018C198BC07E}", XR::SwapChain);
 
-        SwapChain() = default;
-        virtual ~SwapChain() = default;
-
-        static AZStd::intrusive_ptr<SwapChain> Create();
+        static XR::Ptr<SwapChain> Create();
 
+        //! This class helps manage the native swapchain image. 
         class Image final
             : public XR::SwapChain::Image
         {
@@ -61,13 +31,15 @@ namespace OpenXRVk
             AZ_CLASS_ALLOCATOR(Image, AZ::SystemAllocator, 0);
             AZ_RTTI(Image, "{717ABDD4-C050-4FDF-8E93-3784F81FE315}", XR::SwapChain::Image);
 
-            static AZStd::intrusive_ptr<Image> Create();
+            static XR::Ptr<Image> Create();
 
+            AZ::RHI::ResultCode Init(XrSwapchainImageVulkan2KHR swapchainImage);
+            VkImage GetNativeImage();
         private:
-            VkImage m_image;
-            XrSwapchainImageBaseHeader* m_swapChainImageHeader;
+            XrSwapchainImageVulkan2KHR m_swapchainImage;
         };
 
+        //! This class helps manage the native swapchain for a given view. 
         class View final
             : public XR::SwapChain::View
         {
@@ -75,22 +47,55 @@ namespace OpenXRVk
             AZ_CLASS_ALLOCATOR(View, AZ::SystemAllocator, 0);
             AZ_RTTI(View, "{F8312427-AC2D-4737-9A8F-A16ADA5319D0}", XR::SwapChain::View);
 
-            static AZStd::intrusive_ptr<View> Create();
+            static XR::Ptr<View> Create();
             
             AZ::RHI::ResultCode Init(XrSwapchain handle, AZ::u32 width, AZ::u32 height);
-
+            XrSwapchain GetSwapChainHandle() const;
+            //! Destroy native swapchain
+            void Shutdown() override;
+
+            //! swapchain specific accessor functions
+            AZ::u32 GetWidth() const;
+            AZ::u32 GetHeight() const;
+            AZ::u32 GetCurrentImageIndex() const override;
+
+            //! Native swapChain image data
+            AZStd::vector<XrSwapchainImageBaseHeader*> m_swapChainImageHeaders;
+            AZStd::vector<XrSwapchainImageVulkan2KHR> m_swapchainImages;
         private:
-            XrSwapchain m_handle;
-            AZ::u32 m_width;
-            AZ::u32 m_height;
+            XrSwapchain m_handle = XR_NULL_HANDLE;
         };
 
-        AZ::RHI::ResultCode InitInternal() override;
+        //! Assign the correct native Swapchain image based on the swapchain index and swapchain image index
+        AZ::RHI::ResultCode GetSwapChainImage(AZ::RHI::XRSwapChainDescriptor* swapchainDescriptor) const override;
+
+        //! Return the recommended swapchain width
+        AZ::u32 GetSwapChainWidth(AZ::u32 viewIndex) const override;
+
+        //! Return the recommended swapchain height
+        AZ::u32 GetSwapChainHeight(AZ::u32 viewIndex) const override;
 
+        //! Get the view configurations supported by the drivers
+        AZStd::vector<XrViewConfigurationView> GetViewConfigs() const;
+        
     private:
+
+        //! Initialize all the native SwapChain and SwapChain images per view.
+        AZ::RHI::ResultCode InitInternal() override;
+
+        //! Destroy native objects
+        void ShutdownInternal() override;
+
+        //! Return supported swapchain image format
+        AZ::s64 SelectColorSwapChainFormat(const AZStd::vector<int64_t>& runtimeFormats) const;
+        
         AZStd::vector<XrViewConfigurationView> m_configViews;
-        AZStd::vector<XrView> m_views;
-        int64_t m_colorSwapchainFormat{ -1 };
+        AZ::s64 m_colorSwapChainFormat{ -1 };
+        AZ::u32 m_mipCount = 1;
+        AZ::u32 m_faceCount = 1;
+        AZ::u32 m_arraySize = 1;
+
+        //Todo: Add support up higher sample counts in case on MSAA pipeline
+        VkSampleCountFlagBits m_sampleCount = VK_SAMPLE_COUNT_1_BIT; 
     };
-    */
 }

+ 24 - 6
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h

@@ -15,8 +15,8 @@ namespace OpenXRVk
 {
     //! This class is the component related to the vulkan backend of XR.
     class SystemComponent final
-    : public AZ::Component
-    , public XR::Factory
+        : public AZ::Component
+        , public XR::Factory
     {
     public:
         AZ_COMPONENT(SystemComponent, "{C0ABD1CE-FD3C-48C3-8AE8-C098BCCFC604}");
@@ -35,11 +35,29 @@ namespace OpenXRVk
 
         ///////////////////////////////////////////////////////////////////
         // XR::Factory overrides
-        // Create OpenXRVk::Instance object
-        virtual XR::Ptr<XR::Instance> CreateInstance();
+        //! Create OpenXRVk::Instance object
+        XR::Ptr<XR::Instance> CreateInstance() override;
 
-        // Create OpenXRVk::Device object
-        virtual XR::Ptr<XR::Device> CreateDevice();
+        //! Create OpenXRVk::Device object
+        XR::Ptr<XR::Device> CreateDevice() override;
+
+        //! Create XR::Session object.
+        XR::Ptr<XR::Session> CreateSession() override;
+
+        //! Create XR::Input object.
+        XR::Ptr<XR::Input> CreateInput() override;
+
+        //! Create XR::Space object.
+        XR::Ptr<XR::Space> CreateSpace() override;
+
+        //! Create XR::Swapchain object.
+        XR::Ptr<XR::SwapChain> CreateSwapChain() override;
+
+        //! Create XR::Swapchain::View object.
+        XR::Ptr<XR::SwapChain::View> CreateSwapChainView() override;
+
+        //! Create XR::Swapchain::Image object.
+        XR::Ptr<XR::SwapChain::Image> CreateSwapChainImage() override;
         ///////////////////////////////////////////////////////////////////
     };
 }

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

@@ -42,11 +42,6 @@ namespace OpenXRVk
     if (result != XR_SUCCESS) {\
         return;\
     }
-
-#define RETURN_RESULTCODE_IF_UNSUCCESSFUL(result) \
-    if (result != AZ::RHI::ResultCode::SUCCESS) {\
-        return result;\
-    }
     
 #define WARN_IF_UNSUCCESSFUL(result) \
     if (result != XR_SUCCESS) {\

+ 190 - 3
Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp

@@ -8,6 +8,9 @@
 
 #include <OpenXRVk/OpenXRVkDevice.h>
 #include <OpenXRVk/OpenXRVkInstance.h>
+#include <OpenXRVk/OpenXRVkSession.h>
+#include <OpenXRVk/OpenXRVkSwapChain.h>
+#include <OpenXRVk/OpenXRVkSpace.h>
 #include <OpenXRVk/OpenXRVkUtils.h>
 #include <Atom/RHI.Reflect/Vulkan/XRVkDescriptors.h>
 #include <AzCore/Casting/numeric_cast.h>
@@ -22,7 +25,7 @@ namespace OpenXRVk
     AZ::RHI::ResultCode Device::InitDeviceInternal(AZ::RHI::XRDeviceDescriptor* deviceDescriptor)
     {
         AZ::Vulkan::XRDeviceDescriptor* xrDeviceDescriptor = static_cast<AZ::Vulkan::XRDeviceDescriptor*>(deviceDescriptor);
-        Instance* xrVkInstance = static_cast<Instance*>(GetInstance().get());
+        Instance* xrVkInstance = static_cast<Instance*>(GetDescriptor().m_instance.get());
         XrVulkanDeviceCreateInfoKHR xrDeviceCreateInfo{ XR_TYPE_VULKAN_DEVICE_CREATE_INFO_KHR };
         xrDeviceCreateInfo.systemId = xrVkInstance->GetXRSystemId();
         xrDeviceCreateInfo.pfnGetInstanceProcAddr = vkGetInstanceProcAddr;
@@ -53,6 +56,14 @@ namespace OpenXRVk
         VkPhysicalDeviceFeatures features{};
         memcpy(&features, xrDeviceCreateInfo.vulkanCreateInfo->pEnabledFeatures, sizeof(features));
 
+        VkPhysicalDeviceFeatures availableFeatures{};
+        vkGetPhysicalDeviceFeatures(xrVkInstance->GetActivePhysicalDevice(), &availableFeatures);
+        if (availableFeatures.shaderStorageImageMultisample == VK_TRUE)
+        {
+            // Setting this quiets down a validation error triggered by the Oculus runtime
+            features.shaderStorageImageMultisample = VK_TRUE;
+        }
+
         VkDeviceCreateInfo deviceInfo{ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
         memcpy(&deviceInfo, xrDeviceCreateInfo.vulkanCreateInfo, sizeof(deviceInfo));
         deviceInfo.pEnabledFeatures = &features;
@@ -60,7 +71,7 @@ namespace OpenXRVk
         deviceInfo.ppEnabledExtensionNames = extensions.empty() ? nullptr : extensions.data();
 
         //Create VkDevice
-        auto pfnCreateDevice = (PFN_vkCreateDevice)xrDeviceCreateInfo.pfnGetInstanceProcAddr(xrVkInstance->GetVkInstance(), "vkCreateDevice");
+        auto pfnCreateDevice = (PFN_vkCreateDevice)xrDeviceCreateInfo.pfnGetInstanceProcAddr(xrVkInstance->GetNativeInstance(), "vkCreateDevice");
         VkResult vulkanResult = pfnCreateDevice(xrDeviceCreateInfo.vulkanPhysicalDevice, &deviceInfo, xrDeviceCreateInfo.vulkanAllocator, &m_xrVkDevice);
         if (vulkanResult != VK_SUCCESS)
         {
@@ -73,12 +84,188 @@ namespace OpenXRVk
         return AZ::RHI::ResultCode::Success;
     }
 
+    bool Device::BeginFrameInternal()
+    {
+        Session* session = static_cast<Session*>(GetSession().get());
+        XrSession xrSession = session->GetXrSession();
+
+        m_xrLayers.clear();
+        m_projectionLayerViews.clear();
+        XrFrameWaitInfo frameWaitInfo{ XR_TYPE_FRAME_WAIT_INFO };
+        XrResult result = xrWaitFrame(xrSession, &frameWaitInfo, &m_frameState);
+        WARN_IF_UNSUCCESSFUL(result);
+
+        XrFrameBeginInfo frameBeginInfo{ XR_TYPE_FRAME_BEGIN_INFO };
+        result = xrBeginFrame(xrSession, &frameBeginInfo);
+        //The XR_FRAME_DISCARDED can sometimes spam harmlessly so filter it out
+        if (result != XR_FRAME_DISCARDED)
+        {
+            WARN_IF_UNSUCCESSFUL(result);
+        }
+        //Always return true as we want EndFrame to always be called. 
+        return true;
+    }
+
+    void Device::EndFrameInternal(XR::Ptr<XR::SwapChain> baseSwapChain)
+    {
+        Session* session = static_cast<Session*>(GetSession().get());
+        Instance* instance = static_cast<Instance*>(GetDescriptor().m_instance.get());
+        SwapChain* swapChain = static_cast<SwapChain*>(baseSwapChain.get());
+        Space* xrSpace = static_cast<Space*>(GetSession()->GetSpace());
+        XrSession xrSession = session->GetXrSession();
+
+        for(uint32_t i = 0; i < swapChain->GetNumViews(); i++)
+        { 
+            XR::SwapChain::View* baseSwapChainView = baseSwapChain->GetView(i);
+            SwapChain::View* viewSwapChain = static_cast<SwapChain::View*>(baseSwapChainView);
+
+            if (baseSwapChainView->m_isImageAcquired)
+            {
+                XrSwapchainImageReleaseInfo releaseInfo{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO };
+                XrResult result = xrReleaseSwapchainImage(viewSwapChain->GetSwapChainHandle(), &releaseInfo);
+                ASSERT_IF_UNSUCCESSFUL(result);
+
+                baseSwapChainView->m_isImageAcquired = false;
+            }
+        }
+
+        m_xrLayer.space = xrSpace->GetXrSpace(OpenXRVk::SpaceType::View);
+        m_xrLayer.viewCount = aznumeric_cast<uint32_t>(m_projectionLayerViews.size());
+        m_xrLayer.views = m_projectionLayerViews.data();
+
+        m_xrLayers.push_back(reinterpret_cast<XrCompositionLayerBaseHeader*>(&m_xrLayer));
+
+        XrFrameEndInfo frameEndInfo{ XR_TYPE_FRAME_END_INFO };
+        frameEndInfo.displayTime = m_frameState.predictedDisplayTime;
+
+        frameEndInfo.environmentBlendMode = instance->GetEnvironmentBlendMode();
+        frameEndInfo.layerCount = aznumeric_cast<uint32_t>(m_xrLayers.size());
+        frameEndInfo.layers = m_xrLayers.data();
+        XrResult result = xrEndFrame(xrSession, &frameEndInfo);
+       
+        //The XR_ERROR_VALIDATION_FAILURE can sometimes spam harmlessly so filter it out.
+        //It usually happens when xrBeginFrame yields XR_FRAME_DISCARDED 
+        if (result != XR_ERROR_VALIDATION_FAILURE)
+        {
+            WARN_IF_UNSUCCESSFUL(result);
+        }
+    }
+
+    bool Device::AcquireSwapChainImageInternal(AZ::u32 viewIndex, XR::SwapChain* baseSwapChain)
+    {
+        SwapChain* swapChain = static_cast<SwapChain*>(baseSwapChain);
+        XR::SwapChain::View* baseSwapChainView = baseSwapChain->GetView(viewIndex);
+        SwapChain::View* swapChainView = static_cast<SwapChain::View*>(baseSwapChainView);
+        Space* xrSpace = static_cast<Space*>(GetSession()->GetSpace());
+        Instance* instance = static_cast<Instance*>(GetDescriptor().m_instance.get());
+        Session* session = static_cast<Session*>(GetSession().get());
+        XrSession xrSession = session->GetXrSession();
+        XrSwapchain swapChainHandle = swapChainView->GetSwapChainHandle();
+
+        XrViewState viewState{ XR_TYPE_VIEW_STATE };
+        uint32_t viewCapacityInput = aznumeric_cast<uint32_t>(m_views.size());
+
+        XrViewLocateInfo viewLocateInfo{ XR_TYPE_VIEW_LOCATE_INFO };
+        viewLocateInfo.viewConfigurationType = instance->GetViewConfigType(); 
+        viewLocateInfo.displayTime = m_frameState.predictedDisplayTime;
+        viewLocateInfo.space = xrSpace->GetXrSpace(OpenXRVk::SpaceType::View);
+
+        XrResult result = xrLocateViews(xrSession, &viewLocateInfo, &viewState, viewCapacityInput, &m_viewCountOutput, m_views.data());
+        ASSERT_IF_UNSUCCESSFUL(result);
+        
+        if ((viewState.viewStateFlags & XR_VIEW_STATE_POSITION_VALID_BIT) == 0 ||
+            (viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) == 0)
+        {
+            //There is no valid tracking poses for the views
+            return false;
+        }
+
+        AZ_Assert(m_viewCountOutput == viewCapacityInput, "Size mismatch between xrLocateViews %i and xrEnumerateViewConfigurationViews %i", m_viewCountOutput, viewCapacityInput);
+        AZ_Assert(m_viewCountOutput == swapChain->GetViewConfigs().size(), "Size mismatch between xrLocateViews %i and xrEnumerateViewConfigurationViews %i", m_viewCountOutput, swapChain->GetViewConfigs().size());
+
+        m_projectionLayerViews.resize(m_viewCountOutput);
+        XrSwapchainImageAcquireInfo acquireInfo{ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO };
+        result = xrAcquireSwapchainImage(swapChainHandle, &acquireInfo, &baseSwapChainView->m_activeImageIndex);
+        baseSwapChainView->m_isImageAcquired = (result == XR_SUCCESS);
+        WARN_IF_UNSUCCESSFUL(result);
+        
+        XrSwapchainImageWaitInfo waitInfo{ XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO };
+        waitInfo.timeout = XR_INFINITE_DURATION;
+        result = xrWaitSwapchainImage(swapChainHandle, &waitInfo);
+        ASSERT_IF_UNSUCCESSFUL(result);
+
+        m_projectionLayerViews[viewIndex] = { XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW };
+        m_projectionLayerViews[viewIndex].pose = m_views[viewIndex].pose;
+        m_projectionLayerViews[viewIndex].fov = m_views[viewIndex].fov;
+        m_projectionLayerViews[viewIndex].subImage.swapchain = swapChainHandle;
+        m_projectionLayerViews[viewIndex].subImage.imageRect.offset = { 0, 0 };
+        m_projectionLayerViews[viewIndex].subImage.imageRect.extent = { static_cast<int>(swapChainView->GetWidth()),
+                                                                        static_cast<int>(swapChainView->GetHeight()) };
+        return true;
+    }
+
+    bool Device::ShouldRender() const
+    {
+        return m_frameState.shouldRender == XR_TRUE;
+    }
+
+    void Device::InitXrViews(uint32_t numViews)
+    {
+        // Create and cache view buffer for xrLocateViews later.
+        m_views.clear();
+        m_views.resize(numViews, { XR_TYPE_VIEW });
+    }
+
+    VkDevice Device::GetNativeDevice() const
+    {
+        return m_xrVkDevice;
+    }
+
+    AZ::RPI::FovData Device::GetViewFov(AZ::u32 viewIndex) const
+    {
+        AZ::RPI::FovData viewFov;
+        if(viewIndex < m_projectionLayerViews.size())
+        { 
+            viewFov.m_angleLeft = m_projectionLayerViews[viewIndex].fov.angleLeft;
+            viewFov.m_angleRight = m_projectionLayerViews[viewIndex].fov.angleRight;
+            viewFov.m_angleUp = m_projectionLayerViews[viewIndex].fov.angleUp;
+            viewFov.m_angleDown = m_projectionLayerViews[viewIndex].fov.angleDown;     
+        }
+        return viewFov;
+    }
+
+    AZ::RPI::PoseData Device::GetViewPose(AZ::u32 viewIndex) const
+    {
+        AZ::RPI::PoseData viewPose;
+        if (viewIndex < m_projectionLayerViews.size())
+        {
+            const XrQuaternionf& orientation = m_projectionLayerViews[viewIndex].pose.orientation;
+            const XrVector3f& position = m_projectionLayerViews[viewIndex].pose.position;
+            viewPose.orientation = AZ::Quaternion(orientation.x,
+                                                  orientation.y, 
+                                                  orientation.z, 
+                                                  orientation.w);
+            viewPose.position = AZ::Vector3(position.x, 
+                                            position.y, 
+                                            position.z);
+        }        
+        return viewPose;
+    }
+
+    XrTime Device::GetPredictedDisplayTime() const
+    {
+        return m_frameState.predictedDisplayTime;
+    }
+
     void Device::ShutdownInternal()
     {
+        m_projectionLayerViews.clear();
+        m_views.clear();
+        m_xrLayers.clear();
         if (m_xrVkDevice != VK_NULL_HANDLE)
         {
             vkDestroyDevice(m_xrVkDevice, nullptr);
             m_xrVkDevice = VK_NULL_HANDLE;
         }
     }
-}
+}

+ 2 - 2
Gems/OpenXRVk/Code/Source/OpenXRVkGladFunctionLoader.cpp

@@ -32,8 +32,8 @@ namespace OpenXRVk
 
     bool GladFunctionLoader::InitInternal()
     {
-        // Since que don't have the vulkan instance or device yet, we just load the function pointers from the loader
-        // using dlsym or similar.
+        // Since we don't have the vulkan instance or device yet, we just load the function pointers from the loader
+        // using dlsym or something similar.
         return gladLoadVulkanUserPtr(VK_NULL_HANDLE, &LoadFunctionFromLibrary, m_moduleHandle.get()) != 0;
     }
 

+ 0 - 14
Gems/OpenXRVk/Code/Source/OpenXRVkGraphicsBinding.cpp

@@ -1,14 +0,0 @@
-/*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
-
-#include <OpenXRVk/OpenXRVkGraphicsBinding.h>
-
-namespace OpenXRVk
-{
-
-}

+ 261 - 21
Gems/OpenXRVk/Code/Source/OpenXRVkInput.cpp

@@ -7,48 +7,288 @@
  */
 
 #include <OpenXRVk/OpenXRVkInput.h>
+#include <OpenXRVk/OpenXRVkInstance.h>
+#include <OpenXRVk/OpenXRVkSession.h>
+#include <OpenXRVk/OpenXRVkDevice.h>
+#include <OpenXRVk/OpenXRVkSpace.h>
+#include <OpenXRVk/OpenXRVkUtils.h>
+#include <AzCore/Casting/numeric_cast.h>
 
 namespace OpenXRVk
 {
-    //Todo: Pull this in when needed or remove
-    /*
-    AZStd::intrusive_ptr<XR::Input> Input::Create()
+    XR::Ptr<Input> Input::Create()
     {
-        return nullptr;
+        return aznew Input;
     }
 
-    AZ::RHI::ResultCode Input::Init()
+    AZ::RHI::ResultCode Input::InitInternal()
+    {
+        Instance* xrVkInstance = static_cast<Instance*>(GetDescriptor().m_instance.get());
+        XrInstance xrInstance = xrVkInstance->GetXRInstance();
+
+        // Create an action set.
+        XrActionSetCreateInfo actionSetInfo{ XR_TYPE_ACTION_SET_CREATE_INFO };
+        azstrcpy(actionSetInfo.actionSetName, sizeof(actionSetInfo.actionSetName), "gameplay");
+        azstrcpy(actionSetInfo.localizedActionSetName, sizeof(actionSetInfo.localizedActionSetName), "Gameplay");
+        actionSetInfo.priority = 0;
+        XrResult result = xrCreateActionSet(xrInstance, &actionSetInfo, &m_actionSet);
+        WARN_IF_UNSUCCESSFUL(result);
+
+        // Get the XrPath for the left and right hands - we will use them as subaction paths.
+        result = xrStringToPath(xrInstance, "/user/hand/left", &m_handSubactionPath[static_cast<uint32_t>(XR::Side::Left)]);
+        WARN_IF_UNSUCCESSFUL(result);
+        result = xrStringToPath(xrInstance, "/user/hand/right", &m_handSubactionPath[static_cast<uint32_t>(XR::Side::Right)]);
+        WARN_IF_UNSUCCESSFUL(result);
+
+        // Create actions.   
+        // Create an input action for grabbing objects with the left and right hands.
+        CreateAction(m_grabAction, XR_ACTION_TYPE_FLOAT_INPUT, "grab_object", "Grab Object",
+                     aznumeric_cast<uint32_t>(m_handSubactionPath.size()), m_handSubactionPath.data());
+
+        CreateAction(m_poseAction, XR_ACTION_TYPE_POSE_INPUT, "hand_pose", "Hand Pose",
+            aznumeric_cast<uint32_t>(m_handSubactionPath.size()), m_handSubactionPath.data());
+
+        CreateAction(m_vibrateAction, XR_ACTION_TYPE_VIBRATION_OUTPUT, "vibrate_hand", "Vibrate Hand",
+            aznumeric_cast<uint32_t>(m_handSubactionPath.size()), m_handSubactionPath.data());
+
+        CreateAction(m_quitAction, XR_ACTION_TYPE_BOOLEAN_INPUT, "quit_session", "Quit Session", 0, nullptr);
+
+        AZStd::array<XrPath, static_cast<uint32_t>(XR::Side::Count)> squeezeValuePath;
+        AZStd::array<XrPath, static_cast<uint32_t>(XR::Side::Count)> posePath;
+        AZStd::array<XrPath, static_cast<uint32_t>(XR::Side::Count)> hapticPath;
+        AZStd::array<XrPath, static_cast<uint32_t>(XR::Side::Count)> menuClickPath;
+
+        result = xrStringToPath(xrInstance, "/user/hand/left/input/squeeze/value", &squeezeValuePath[static_cast<uint32_t>(XR::Side::Left)]);
+        result = xrStringToPath(xrInstance, "/user/hand/right/input/squeeze/value", &squeezeValuePath[static_cast<uint32_t>(XR::Side::Right)]);
+        result = xrStringToPath(xrInstance, "/user/hand/left/input/grip/pose", &posePath[static_cast<uint32_t>(XR::Side::Left)]);
+        result = xrStringToPath(xrInstance, "/user/hand/right/input/grip/pose", &posePath[static_cast<uint32_t>(XR::Side::Right)]);
+        result = xrStringToPath(xrInstance, "/user/hand/left/output/haptic", &hapticPath[static_cast<uint32_t>(XR::Side::Left)]);
+        result = xrStringToPath(xrInstance, "/user/hand/right/output/haptic", &hapticPath[static_cast<uint32_t>(XR::Side::Right)]);
+        result = xrStringToPath(xrInstance, "/user/hand/left/input/menu/click", &menuClickPath[static_cast<uint32_t>(XR::Side::Left)]);
+        result = xrStringToPath(xrInstance, "/user/hand/right/input/menu/click", &menuClickPath[static_cast<uint32_t>(XR::Side::Right)]);
+        
+        // Bindings for the Occulus Touch.
+        XrPath oculusTouchInteractionProfilePath;
+        result = xrStringToPath(xrInstance, "/interaction_profiles/oculus/touch_controller", &oculusTouchInteractionProfilePath);
+        AZStd::vector<XrActionSuggestedBinding> bindings{ { { m_grabAction, squeezeValuePath[static_cast<uint32_t>(XR::Side::Left)] },
+                                                            { m_grabAction, squeezeValuePath[static_cast<uint32_t>(XR::Side::Right)] },
+                                                            { m_poseAction, posePath[static_cast<uint32_t>(XR::Side::Left)] },
+                                                            { m_poseAction, posePath[static_cast<uint32_t>(XR::Side::Right)] },
+                                                            { m_quitAction, menuClickPath[static_cast<uint32_t>(XR::Side::Left)] },
+                                                            { m_vibrateAction, hapticPath[static_cast<uint32_t>(XR::Side::Left)] },
+                                                            { m_vibrateAction, hapticPath[static_cast<uint32_t>(XR::Side::Right)] } } };
+        XrInteractionProfileSuggestedBinding suggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
+        suggestedBindings.interactionProfile = oculusTouchInteractionProfilePath;
+        suggestedBindings.suggestedBindings = bindings.data();
+        suggestedBindings.countSuggestedBindings = aznumeric_cast<uint32_t>(bindings.size());
+        result = xrSuggestInteractionProfileBindings(xrInstance, &suggestedBindings);
+        WARN_IF_UNSUCCESSFUL(result);
+        
+        return ConvertResult(result);
+    }
+
+    void Input::CreateAction(XrAction& action, XrActionType actionType,
+                                  const char* actionName, const char* localizedActionName,
+                                  uint32_t countSubactionPathCount, const XrPath* subActionPaths)
+    {
+        XrActionCreateInfo actionInfo{ XR_TYPE_ACTION_CREATE_INFO };
+        actionInfo.actionType = actionType;
+        azstrcpy(actionInfo.actionName, sizeof(actionInfo.actionName), actionName);
+        azstrcpy(actionInfo.localizedActionName, sizeof(actionInfo.localizedActionName), localizedActionName);
+        actionInfo.countSubactionPaths = countSubactionPathCount;
+        actionInfo.subactionPaths = subActionPaths;
+        [[maybe_unused]] XrResult result = xrCreateAction(m_actionSet, &actionInfo, &action);
+        WARN_IF_UNSUCCESSFUL(result);
+    }
+
+    AZ::RHI::ResultCode Input::InitializeActionSpace(XrSession xrSession)
+    {
+        XrActionSpaceCreateInfo actionSpaceInfo{ XR_TYPE_ACTION_SPACE_CREATE_INFO };
+        actionSpaceInfo.action = m_poseAction;
+        actionSpaceInfo.poseInActionSpace.orientation.w = 1.f;
+        actionSpaceInfo.subactionPath = m_handSubactionPath[static_cast<uint32_t>(XR::Side::Left)];
+        XrResult result = xrCreateActionSpace(xrSession, &actionSpaceInfo, &m_handSpace[static_cast<uint32_t>(XR::Side::Left)]);
+        WARN_IF_UNSUCCESSFUL(result);
+        RETURN_RESULTCODE_IF_UNSUCCESSFUL(ConvertResult(result));
+        actionSpaceInfo.subactionPath = m_handSubactionPath[static_cast<uint32_t>(XR::Side::Right)];
+        result = xrCreateActionSpace(xrSession, &actionSpaceInfo, &m_handSpace[static_cast<uint32_t>(XR::Side::Right)]);
+        WARN_IF_UNSUCCESSFUL(result);
+
+        return ConvertResult(result);
+    }
+
+    AZ::RHI::ResultCode Input::InitializeActionSets(XrSession xrSession)
+    {
+        XrSessionActionSetsAttachInfo attachInfo{ XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO };
+        attachInfo.countActionSets = 1;
+        attachInfo.actionSets = &m_actionSet;
+        XrResult result = xrAttachSessionActionSets(xrSession, &attachInfo);
+        WARN_IF_UNSUCCESSFUL(result);
+
+        return ConvertResult(result);
+    }
+
+    void Input::ShutdownInternal()
+    {
+        if (m_actionSet != XR_NULL_HANDLE) 
+        {
+            for (auto hand : { XR::Side::Left, XR::Side::Right }) 
+            {
+                xrDestroySpace(m_handSpace[static_cast<uint32_t>(hand)]);
+            }
+            xrDestroyActionSet(m_actionSet);
+        }
+    }
+
+    void Input::PollActions()
+    {
+        Session* session = static_cast<Session*>(GetDescriptor().m_session.get());
+        XrSession xrSession = session->GetXrSession();
+        Device* device = static_cast<Device*>(GetDescriptor().m_device.get());
+        m_handActive = { XR_FALSE, XR_FALSE };
+
+        // Sync actions
+        const XrActiveActionSet activeActionSet{ m_actionSet, XR_NULL_PATH };
+        XrActionsSyncInfo syncInfo{ XR_TYPE_ACTIONS_SYNC_INFO };
+        syncInfo.countActiveActionSets = 1;
+        syncInfo.activeActionSets = &activeActionSet;
+        XrResult result = xrSyncActions(xrSession, &syncInfo);
+
+        // Get pose and grab action state and start haptic vibrate when hand is 90% squeezed for testing purposes
+        for (auto hand : { XR::Side::Left, XR::Side::Right })
+        {
+            XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
+            getInfo.action = m_grabAction;
+            getInfo.subactionPath = m_handSubactionPath[static_cast<uint32_t>(hand)];
+
+            XrActionStateFloat grabValue{ XR_TYPE_ACTION_STATE_FLOAT };
+            result = xrGetActionStateFloat(xrSession, &getInfo, &grabValue);
+            WARN_IF_UNSUCCESSFUL(result);
+            if (grabValue.isActive == XR_TRUE)
+            {
+                // Scale the rendered hand by 1.0f (open) to 0.5f (fully squeezed).
+                m_handScale[static_cast<uint32_t>(hand)] = 1.0f - 0.5f * grabValue.currentState;
+                if (grabValue.currentState > 0.9f)
+                {
+                    XrHapticVibration vibration{ XR_TYPE_HAPTIC_VIBRATION };
+                    vibration.amplitude = 0.5;
+                    vibration.duration = XR_MIN_HAPTIC_DURATION;
+                    vibration.frequency = XR_FREQUENCY_UNSPECIFIED;
+
+                    XrHapticActionInfo hapticActionInfo{ XR_TYPE_HAPTIC_ACTION_INFO };
+                    hapticActionInfo.action = m_vibrateAction;
+                    hapticActionInfo.subactionPath = m_handSubactionPath[static_cast<uint32_t>(hand)];
+                    result = xrApplyHapticFeedback(xrSession, &hapticActionInfo, (XrHapticBaseHeader*)&vibration);
+                    WARN_IF_UNSUCCESSFUL(result);
+                }
+            }
+
+            getInfo.action = m_poseAction;
+            XrActionStatePose poseState{ XR_TYPE_ACTION_STATE_POSE };
+            result = xrGetActionStatePose(xrSession, &getInfo, &poseState);
+            WARN_IF_UNSUCCESSFUL(result);
+            m_handActive[static_cast<uint32_t>(hand)] = poseState.isActive;
+
+            LocateControllerSpace(device->GetPredictedDisplayTime(), session->GetXrSpace(OpenXRVk::SpaceType::View), static_cast<uint32_t>(hand));
+        }  
+
+        //Cache 3d location information
+        for (uint32_t i = 0; i < static_cast<uint32_t>(SpaceType::Count); i++)
+        {
+            SpaceType spaceType = static_cast<SpaceType>(i);
+            LocateVisualizedSpace(device->GetPredictedDisplayTime(), session->GetXrSpace(spaceType),
+                                    session->GetXrSpace(OpenXRVk::SpaceType::View), spaceType);
+        }
+
+        // There were no subaction paths specified for the quit action, because we don't care which hand did it.
+        XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO, nullptr, m_quitAction, XR_NULL_PATH };
+        XrActionStateBoolean quitValue{ XR_TYPE_ACTION_STATE_BOOLEAN };
+        result = xrGetActionStateBoolean(xrSession, &getInfo, &quitValue);
+        WARN_IF_UNSUCCESSFUL(result);
+        if ((quitValue.isActive == XR_TRUE) && (quitValue.changedSinceLastSync == XR_TRUE) && (quitValue.currentState == XR_TRUE))
+        {
+            result = xrRequestExitSession(xrSession);
+            WARN_IF_UNSUCCESSFUL(result);
+        }
+    }
+
+    void Input::LocateControllerSpace(XrTime predictedDisplayTime, XrSpace baseSpace, uint32_t handIndex)
+    {
+        XrSpaceLocation spaceLocation{ XR_TYPE_SPACE_LOCATION };
+        XrResult result = xrLocateSpace(m_handSpace[handIndex], baseSpace, predictedDisplayTime, &spaceLocation);
+        if (result== XR_SUCCESS)
+        {
+            if ((spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 &&
+                (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0) 
+            {
+                m_handSpaceLocation[handIndex] = spaceLocation;
+            }
+        }
+    }
+
+    void Input::LocateVisualizedSpace(XrTime predictedDisplayTime, XrSpace space, XrSpace baseSpace, OpenXRVk::SpaceType visualizedSpaceType)
     {
-        if(XR::Input::Init() == AZ::RHI::ResultCode::Success)
+        XrSpaceLocation spaceLocation{ XR_TYPE_SPACE_LOCATION };
+        XrResult result = xrLocateSpace(space, baseSpace, predictedDisplayTime, &spaceLocation);
+        if (result == XR_SUCCESS)
         {
-            InitializeActions();
+            if ((spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 &&
+                (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0)
+            {
+                m_xrVisualizedSpaceLocations[static_cast<uint32_t>(visualizedSpaceType)] = spaceLocation;
+            }
         }
+    }
 
-        return AZ::RHI::ResultCode::Success;
+    AZ::RPI::PoseData Input::GetControllerPose(AZ::u32 viewIndex) const
+    {
+        AZ::RPI::PoseData viewPose;
+        if (viewIndex < m_handSpaceLocation.size())
+        {
+            const XrQuaternionf& orientation = m_handSpaceLocation[viewIndex].pose.orientation;
+            const XrVector3f& position = m_handSpaceLocation[viewIndex].pose.position;
+            viewPose.orientation = AZ::Quaternion(orientation.x, orientation.y, orientation.z, orientation.w);
+            viewPose.position = AZ::Vector3(position.x, position.y, position.z);
+        }
+        return viewPose;
     }
 
-    AZ::RHI::ResultCode Input::InitInternal()
+    AZ::RPI::PoseData Input::GetVisualizedSpacePose(OpenXRVk::SpaceType visualizedSpaceType) const
+    {
+        AZ::RPI::PoseData viewPose;
+        uint32_t spaceIndex = static_cast<uint32_t>(visualizedSpaceType);
+        if (spaceIndex < m_xrVisualizedSpaceLocations.size())
+        {
+            const XrQuaternionf& orientation = m_xrVisualizedSpaceLocations[spaceIndex].pose.orientation;
+            const XrVector3f& position = m_xrVisualizedSpaceLocations[spaceIndex].pose.position;
+            viewPose.orientation = AZ::Quaternion(orientation.x, orientation.y, orientation.z, orientation.w);
+            viewPose.position = AZ::Vector3(position.x, position.y, position.z);
+        }
+        return viewPose;
+    }
+
+    float Input::GetControllerScale(AZ::u32 viewIndex) const
     {
-        return AZ::RHI::ResultCode::Success;
+        return m_handScale[viewIndex];
     }
 
-    void Input::InitializeActions()
+    XrAction Input::GetGrabAction() const
     {
-        // Code to populate m_input
-        // xrCreateActionSet
-        // xrCreateAction
-        // xrCreateActionSpace
-        // xrAttachSessionActionSets
+        return m_grabAction;
     }
 
-    void Input::PollActions()
+    XrAction Input::GetPoseAction() const
+    {
+        return m_poseAction;
+    }
+
+    XrAction Input::GetVibrationAction() const
     {
-        // xrSyncActions
+        return m_vibrateAction;
     }
 
-    void Input::PollEvents()
+    XrAction Input::GetQuitAction() const
     {
-        // m_session->HandleSessionStateChangedEvent
+        return m_quitAction;
     }
-    */
 }

+ 8 - 8
Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp

@@ -13,7 +13,7 @@
 
 namespace OpenXRVk
 {
-    AZStd::intrusive_ptr<Instance> Instance::Create()
+    XR::Ptr<Instance> Instance::Create()
     {
         return aznew Instance;
     }
@@ -362,38 +362,38 @@ namespace OpenXRVk
         return AZ::RHI::ResultCode::Fail;
     }
 
-    AZ::u32 Instance::GetNumPhysicalDevices()
+    AZ::u32 Instance::GetNumPhysicalDevices() const
     {
         return aznumeric_cast<AZ::u32>(m_supportedXRDevices.size());
     }
 
-    VkPhysicalDevice Instance::GetActivePhysicalDevice()
+    VkPhysicalDevice Instance::GetActivePhysicalDevice() const
     {
         AZ_Assert(m_physicalDeviceActiveIndex < m_supportedXRDevices.size(), "Index out of range");
         return m_supportedXRDevices[m_physicalDeviceActiveIndex];
     }
 
-    XrInstance Instance::GetXRInstance()
+    XrInstance Instance::GetXRInstance() const
     {
         return m_xrInstance;
     }
 
-    XrSystemId Instance::GetXRSystemId()
+    XrSystemId Instance::GetXRSystemId() const
     {
         return m_xrSystemId;
     }
 
-    VkInstance Instance::GetVkInstance()
+    VkInstance Instance::GetNativeInstance() const
     {
         return m_xrVkInstance;
     }
 
-    XrEnvironmentBlendMode Instance::GetEnvironmentBlendMode()
+    XrEnvironmentBlendMode Instance::GetEnvironmentBlendMode() const
     {
         return m_environmentBlendMode;
     }
 
-    XrViewConfigurationType Instance::GetViewConfigType()
+    XrViewConfigurationType Instance::GetViewConfigType() const
     {
         return m_viewConfigType;
     }

+ 292 - 17
Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp

@@ -7,49 +7,324 @@
  */
 
 #include <OpenXRVk/OpenXRVkSession.h>
+#include <OpenXRVk/OpenXRVkDevice.h>
+#include <OpenXRVk/OpenXRVkInput.h>
+#include <OpenXRVk/OpenXRVkInstance.h>
+#include <OpenXRVk/OpenXRVkSpace.h>
+#include <OpenXRVk/OpenXRVkUtils.h>
+#include <AzCore/Debug/Trace.h>
+#include <AzCore/Casting/numeric_cast.h>
+#include <XR/XRBase.h>
 
 namespace OpenXRVk
 {
-    //Todo: Pull this in when needed or remove
-    /*
-    AZStd::intrusive_ptr<Session> Session::Create()
+    XR::Ptr<Session> Session::Create()
     {
-        return nullptr;
+        return aznew Session;
     }
 
-    AZ::RHI::ResultCode Session::InitInternal()
+    AZ::RHI::ResultCode Session::InitInternal(AZ::RHI::XRSessionDescriptor* descriptor)
     {
-        // AZStd::intrusive_ptr<GraphicsBinding> gBinding = static_cast<GraphicsBinding>(descriptor.m_graphicsBinding);
-        // xrCreateSession(..m_session,gBinding,..)
-        return AZ::RHI::ResultCode::Success;
+        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());
+
+        m_xrInstance = xrVkInstance->GetXRInstance();
+        AZ_Printf("OpenXrVk", "Creating session...\n");
+        m_graphicsBinding.instance = xrVkInstance->GetNativeInstance();
+        m_graphicsBinding.physicalDevice = xrVkInstance->GetActivePhysicalDevice();
+        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;
+
+        AZ_Assert(m_xrInstance != XR_NULL_HANDLE, "XR instance is null.");
+        AZ_Assert(m_session == XR_NULL_HANDLE, "XR session is already initialized.");
+
+        XrSessionCreateInfo createInfo{ XR_TYPE_SESSION_CREATE_INFO };
+        createInfo.next = reinterpret_cast<const XrBaseInStructure*>(&m_graphicsBinding);
+        createInfo.systemId = xrVkInstance->GetXRSystemId();
+        XrResult result = xrCreateSession(m_xrInstance, &createInfo, &m_session);
+        ASSERT_IF_UNSUCCESSFUL(result);
+        
+        LogReferenceSpaces();
+        Input* xrVkInput = static_cast<Input*>(GetInput());
+        xrVkInput->InitializeActionSpace(m_session);
+        xrVkInput->InitializeActionSets(m_session);
+
+        Space* xrVkSpace = static_cast<Space*>(GetSpace());
+        xrVkSpace->CreateVisualizedSpaces(m_session);
+        return ConvertResult(result);
     }
 
     void Session::LogReferenceSpaces()
     {
-        //..xrEnumerateReferenceSpaces/
+        if (GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled)
+        {
+            AZ_Warning("OpenXrVK", m_session != XR_NULL_HANDLE, "Session is not initialized");
+
+            if (m_session == XR_NULL_HANDLE)
+            {
+                return;
+            }
+
+            uint32_t spaceCount = 0;
+            XrResult result = xrEnumerateReferenceSpaces(m_session, 0, &spaceCount, nullptr);
+            WARN_IF_UNSUCCESSFUL(result);
+            AZStd::vector<XrReferenceSpaceType> spaces(spaceCount);
+            result = xrEnumerateReferenceSpaces(m_session, spaceCount, &spaceCount, spaces.data());
+
+            AZ_Printf("OpenXrVk", "Available reference spaces: %d\n", spaceCount);
+            for (XrReferenceSpaceType space : spaces)
+            {
+                AZ_Printf("OpenXrVk", "  Name: %s\n", to_string(space));
+            }
+        }
+    }
+
+    void Session::HandleSessionStateChangedEvent(const XrEventDataSessionStateChanged& stateChangedEvent)
+    {
+        Instance* xrVkInstance = static_cast<Instance*>(GetDescriptor().m_instance.get());
+        const XrSessionState oldState = m_sessionState;
+        m_sessionState = stateChangedEvent.state;
+
+        if (GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled)
+        {
+            AZ_Printf(
+                "OpenXRVk",
+                "XrEventDataSessionStateChanged: state %s->%s session=%lld time=%lld\n", to_string(oldState), to_string(m_sessionState),
+                    stateChangedEvent.session, stateChangedEvent.time);
+        }
+
+        if ((stateChangedEvent.session != XR_NULL_HANDLE) && (stateChangedEvent.session != m_session))
+        {
+            AZ_Printf("OpenXRVk", "XrEventDataSessionStateChanged for unknown session\n");
+            return;
+        }
+
+        switch (m_sessionState)
+        {
+            case XR_SESSION_STATE_READY:
+            {
+                AZ_Assert(m_session != XR_NULL_HANDLE, "Session is null");
+                XrSessionBeginInfo sessionBeginInfo{ XR_TYPE_SESSION_BEGIN_INFO };
+                sessionBeginInfo.primaryViewConfigurationType = xrVkInstance->GetViewConfigType();
+                XrResult result = xrBeginSession(m_session, &sessionBeginInfo);
+                WARN_IF_UNSUCCESSFUL(result);
+                m_sessionRunning = true;
+                break;
+            }
+            case XR_SESSION_STATE_STOPPING:
+            {
+                AZ_Assert(m_session != XR_NULL_HANDLE, "Session is null");
+                m_sessionRunning = false;
+                XrResult result = xrEndSession(m_session);
+                WARN_IF_UNSUCCESSFUL(result);
+                break;
+            }
+            case XR_SESSION_STATE_EXITING:
+            {
+                m_exitRenderLoop = true;
+                // Do not attempt to restart because user closed this session.
+                m_requestRestart = false;
+                break;
+            }
+            case XR_SESSION_STATE_LOSS_PENDING:
+            {
+                m_exitRenderLoop = true;
+                // Poll for a new instance.
+                m_requestRestart = true;
+                break;
+            }
+            default:
+            {
+                break;
+            }
+        }
+    }
+    
+    const XrEventDataBaseHeader* Session::TryReadNextEvent()
+    {
+        XrEventDataBaseHeader* baseHeader = reinterpret_cast<XrEventDataBaseHeader*>(&m_eventDataBuffer);
+        *baseHeader = { XR_TYPE_EVENT_DATA_BUFFER };
+        const XrResult result = xrPollEvent(m_xrInstance, &m_eventDataBuffer);
+        if (result == XR_SUCCESS)
+        {
+            if (baseHeader->type == XR_TYPE_EVENT_DATA_EVENTS_LOST)
+            {
+                [[maybe_unused]] const XrEventDataEventsLost* const eventsLost = reinterpret_cast<const XrEventDataEventsLost*>(baseHeader);
+                if (GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled)
+                {
+                    AZ_Printf("OpenXrVK", "%d events lost\n", eventsLost->lostEventCount);
+                }
+            }
+            return baseHeader;
+        }
+        if (result == XR_EVENT_UNAVAILABLE)
+        {
+            return nullptr;
+        }
+        WARN_IF_UNSUCCESSFUL(result);
+        return nullptr;
+    }
+
+    void Session::PollEvents()
+    {
+        m_exitRenderLoop = m_requestRestart = false;
+
+        // Process all pending messages.
+        while (const XrEventDataBaseHeader* event = TryReadNextEvent())
+        {
+            switch (event->type)
+            {
+                case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING:
+                {
+                    if (GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled)
+                    {
+                        [[maybe_unused]] const auto& instanceLossPending = *reinterpret_cast<const XrEventDataInstanceLossPending*>(event);
+                        AZ_Printf("OpenXrVk", "XrEventDataInstanceLossPending by %lld\n", instanceLossPending.lossTime);
+                    }
+                    m_exitRenderLoop = true;
+                    m_requestRestart = true;
+                    return;
+                }
+                case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:
+                {
+                    auto sessionStateChangedEvent = *reinterpret_cast<const XrEventDataSessionStateChanged*>(event);
+                    HandleSessionStateChangedEvent(sessionStateChangedEvent);
+                    break;
+                }
+                case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED:
+                {
+                    if (GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled)
+                    {
+                        Input* xrVkInput = static_cast<Input*>(GetInput());
+                        LogActionSourceName(xrVkInput->GetGrabAction(), "Grab");
+                        LogActionSourceName(xrVkInput->GetQuitAction(), "Quit");
+                        LogActionSourceName(xrVkInput->GetPoseAction(), "Pose");
+                        LogActionSourceName(xrVkInput->GetVibrationAction(), "Vibrate");
+                    }
+                    break;
+                }
+                case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING:
+                    [[fallthrough]];
+                default:
+                {
+                    if (GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled)
+                    {
+                        AZ_Printf("OpenXrVk", "Ignoring event type %d\n", event->type);
+                    }
+                    break;
+                }
+            }
+        }
     }
 
-    void Session::HandleSessionStateChangedEvent(
-        const XrEventDataSessionStateChanged&,
-        bool*,
-        bool*)
+    void Session::LogActionSourceName(XrAction action, const AZStd::string_view actionName) const
     {
-        // Handle Session state changes
+        XrBoundSourcesForActionEnumerateInfo getInfo = { XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO };
+        getInfo.action = action;
+        uint32_t pathCount = 0;
+        XrResult result = xrEnumerateBoundSourcesForAction(m_session, &getInfo, 0, &pathCount, nullptr);
+        WARN_IF_UNSUCCESSFUL(result);
+        AZStd::vector<XrPath> paths(pathCount);
+        result = xrEnumerateBoundSourcesForAction(m_session, &getInfo, aznumeric_cast<uint32_t>(paths.size()), &pathCount, paths.data());
+        WARN_IF_UNSUCCESSFUL(result);
+
+        AZStd::string sourceName;
+        for (uint32_t i = 0; i < pathCount; ++i)
+        {
+            constexpr XrInputSourceLocalizedNameFlags all = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT |
+                XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT | XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT;
+
+            XrInputSourceLocalizedNameGetInfo nameInfo = { XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO };
+            nameInfo.sourcePath = paths[i];
+            nameInfo.whichComponents = all;
+
+            uint32_t size = 0;
+            result = xrGetInputSourceLocalizedName(m_session, &nameInfo, 0, &size, nullptr);
+            WARN_IF_UNSUCCESSFUL(result);
+            if (size < 1)
+            {
+                continue;
+            }
+            AZStd::vector<char> grabSource(size);
+            result = xrGetInputSourceLocalizedName(m_session, &nameInfo, uint32_t(grabSource.size()), &size, grabSource.data());
+            WARN_IF_UNSUCCESSFUL(result);
+            if (!sourceName.empty())
+            {
+                sourceName += " and ";
+            }
+            sourceName += "'";
+            sourceName += AZStd::string(grabSource.data(), size - 1);
+            sourceName += "'";
+        }
+        
+        AZ_Printf("OpenXrVK",
+            "%s action is bound to %s\n", actionName.data(), ((!sourceName.empty()) ? sourceName.c_str() : "nothing"));
     }
 
-    XrSession Session::GetXrSession()
+    void Session::LocateControllerSpace(AZ::u32 handIndex)
+    {
+        Input* xrInput = static_cast<Input*>(GetInput());
+        Device* device = static_cast<Device*>(GetDescriptor().m_device.get());
+        Space* space = static_cast<Space*>(GetSpace());
+        xrInput->LocateControllerSpace(device->GetPredictedDisplayTime(), space->GetXrSpace(OpenXRVk::SpaceType::View), handIndex);
+    }
+
+    AZ::RPI::PoseData Session::GetControllerPose(AZ::u32 handIndex) const
+    {
+        Input* xrInput = static_cast<Input*>(GetInput());
+        return xrInput->GetControllerPose(handIndex);
+    }
+    
+    float Session::GetControllerScale(AZ::u32 handIndex) const
+    {
+        Input* xrInput = static_cast<Input*>(GetInput());
+        return xrInput->GetControllerScale(handIndex);
+    }
+
+    AZ::RPI::PoseData Session::GetViewFrontPose() const
+    {
+        Input* xrInput = static_cast<Input*>(GetInput());
+        return xrInput->GetVisualizedSpacePose(OpenXRVk::SpaceType::ViewFront);
+    }
+
+    XrSession Session::GetXrSession() const
     {
         return m_session;
     }
 
+    XrSpace Session::GetXrSpace(SpaceType spaceType) const
+    {
+        Space* space = static_cast<Space*>(GetSpace());
+        return space->GetXrSpace(spaceType);
+    }
+
     bool Session::IsSessionRunning() const
     {
-        return true;
+        return m_sessionRunning;
     }
 
     bool Session::IsSessionFocused() const
     {
         return m_sessionState == XR_SESSION_STATE_FOCUSED;
     }
-    */
+
+    bool Session::IsRestartRequested() const
+    {
+        return m_requestRestart;
+    }
+
+    bool Session::IsExitRenderLoopRequested() const
+    {
+        return m_exitRenderLoop;
+    }
+
+    void Session::ShutdownInternal()
+    {
+        if (m_session != XR_NULL_HANDLE) 
+        {
+            xrDestroySession(m_session);
+        }
+    }
 }

+ 139 - 6
Gems/OpenXRVk/Code/Source/OpenXRVkSpace.cpp

@@ -7,16 +7,149 @@
  */
 
 #include <OpenXRVk/OpenXRVkSpace.h>
+#include <OpenXRVk/OpenXRVkUtils.h>
+#include <AzCore/Debug/Trace.h>
 
 namespace OpenXRVk
 {
-    AZStd::intrusive_ptr<Space> Space::Create()
+    XR::Ptr<Space> Space::Create()
     {
-        return nullptr;
+        return aznew Space;
     }
 
-    //XrSpaceLocation GetSpace(XrSpace space)
-    //{
-     //   return XrSpaceLocation()
-    //}
+    AZ::RHI::ResultCode Space::InitInternal()
+    {
+        return AZ::RHI::ResultCode::Success;
+    }
+
+    void Space::CreateVisualizedSpaces(XrSession xrSession)
+    {
+        AZ_Assert(xrSession != XR_NULL_HANDLE, "XR session is null");
+  
+        for (uint32_t i = 0; i < static_cast<uint32_t>(SpaceType::Count); i++)
+        {
+            XrReferenceSpaceCreateInfo referenceSpaceCreateInfo = GetXrReferenceSpaceCreateInfo(static_cast<SpaceType>(i));
+            XrSpace space;
+            XrResult result = xrCreateReferenceSpace(xrSession, &referenceSpaceCreateInfo, &space);
+            if (IsSuccess(result))
+            {
+                m_xrSpaces.push_back(space);
+            }
+            else
+            {
+                AZ_Warning("OpenXrVK", false, "Failed to create reference space %s with error %d", ToString(static_cast<SpaceType>(i)).data(), result);
+            }
+        }
+    }
+
+    XrReferenceSpaceCreateInfo Space::GetXrReferenceSpaceCreateInfo(SpaceType spaceType)
+    {
+        XrReferenceSpaceCreateInfo referenceSpaceCreateInfo{ XR_TYPE_REFERENCE_SPACE_CREATE_INFO };
+        referenceSpaceCreateInfo.poseInReferenceSpace = Identity();
+        switch (spaceType)
+        {
+            case SpaceType::View:
+            {
+                //Track the view origin used to generate view transforms for the primary viewer (or centroid of 
+                //view origins if stereo), with +Y up, +X to the right, and -Z forward.
+                referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW;
+                break;
+            }
+            case SpaceType::ViewFront:
+            {
+                // Track view head-locked 5m in front of device.
+                referenceSpaceCreateInfo.poseInReferenceSpace = Translation({ 0.f, 0.f, -5.f }),
+                    referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW;
+                break;
+            }
+            case SpaceType::Local:
+            {
+                //Track center Local space which is world-locked origin, gravity-aligned to exclude 
+                //pitch and roll, with +Y up, +X to the right, and -Z forward.
+                referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
+                break;
+            }
+            case SpaceType::Stage:
+            {
+                //Track center Stage space which is defined flat, rectangular space that is empty and can be walked around on.
+                referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE;
+                break;
+            }
+            case SpaceType::StageLeft:
+            {
+                //Track Left Stage space which is basically the center stage translated to the left and down by 5m. 
+                referenceSpaceCreateInfo.poseInReferenceSpace = RotateCCWAboutYAxis(0.f, { -5.f, 0.f, -5.f });
+                referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE;
+                break;
+            }
+            case SpaceType::StageRight:
+            {
+                //Track Right Stage space which is basically the center stage translated to the right and down by 5m. 
+                referenceSpaceCreateInfo.poseInReferenceSpace = RotateCCWAboutYAxis(0.f, { 5.f, 0.f, -5.f });
+                referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE;
+                break;
+            }
+            case SpaceType::StageLeftRotated:
+            {
+                //Track Left Rotated Stage space which is basically the center stage translated and Rotated by 60 deg (i.e pi/3). Remove if not used in future
+                referenceSpaceCreateInfo.poseInReferenceSpace = RotateCCWAboutYAxis(AZ::Constants::Pi / 3.f, { -5.f, 0.5f, -5.f });
+                referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE;
+                break;
+            }
+            case SpaceType::StageRightRotated:
+            {
+                //Track Right Rotated Stage space which is basically the center stage translated and Rotated by 60 deg (i.e pi/3). Remove if not used in future
+                referenceSpaceCreateInfo.poseInReferenceSpace = RotateCCWAboutYAxis(-AZ::Constants::Pi / 3.f, { 5.f, 0.5f, -5.f });
+                referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE;
+                break;
+            }
+            default:
+            {
+                AZ_Assert(false, "Unknown reference space type '%s'", ToString(static_cast<SpaceType>(spaceType)).data());
+            }
+        }
+        return referenceSpaceCreateInfo;
+    }
+
+    XrPosef Space::Identity()
+    {
+        XrPosef t{};
+        t.orientation.w = 1;
+        return t;
+    }
+
+    XrPosef Space::Translation(const XrVector3f& translation)
+    {
+        XrPosef t = Identity();
+        t.position = translation;
+        return t;
+    }
+
+    XrPosef Space::RotateCCWAboutYAxis(float radians, XrVector3f translation)
+    {
+        XrPosef t = Identity();
+        t.orientation.x = 0.f;
+        t.orientation.y = AZStd::sin(radians * 0.5f);
+        t.orientation.z = 0.f;
+        t.orientation.w = AZStd::cos(radians * 0.5f);
+        t.position = translation;
+        return t;
+    }
+
+    XrSpace Space::GetXrSpace(SpaceType spaceType) const
+    {
+        return m_xrSpaces[static_cast<uint32_t>(spaceType)];
+    }
+
+    void Space::ShutdownInternal()
+    {
+        for (XrSpace& space : m_xrSpaces)
+        {
+            if (space != XR_NULL_HANDLE)
+            {
+                xrDestroySpace(space);
+            }
+        }
+        m_xrSpaces.clear();
+    }
 }

+ 240 - 16
Gems/OpenXRVk/Code/Source/OpenXRVkSwapChain.cpp

@@ -6,47 +6,271 @@
  *
  */
 
+#include <Atom/RHI.Reflect/Vulkan/XRVkDescriptors.h>
+#include <AzCore/Casting/numeric_cast.h>
+#include <AzCore/std/containers/set.h>
+#include <AzCore/std/containers/vector.h>
+#include <OpenXRVk/OpenXRVkInstance.h>
+#include <OpenXRVk/OpenXRVkSession.h>
 #include <OpenXRVk/OpenXRVkSwapChain.h>
+#include <OpenXRVk/OpenXRVkUtils.h>
+#include <XR/XRFactory.h>
 
 namespace OpenXRVk
 {
-    //Pull this in if needed or remove
-    /*
-    AZStd::intrusive_ptr<SwapChain> SwapChain::Create()
+
+    XR::Ptr<SwapChain> SwapChain::Create()
     {
         return aznew SwapChain;
     }
 
-    AZStd::intrusive_ptr<SwapChain::Image> SwapChain::Image::Create()
+    XR::Ptr<SwapChain::Image> SwapChain::Image::Create()
     {
         return aznew SwapChain::Image;
     }
 
-    AZStd::intrusive_ptr<SwapChain::View> SwapChain::View::Create()
+    XR::Ptr<SwapChain::View> SwapChain::View::Create()
     {
         return aznew SwapChain::View;
     }
 
     AZ::RHI::ResultCode SwapChain::View::Init(XrSwapchain handle, AZ::u32 width, AZ::u32 height)
     {
+        m_handle = handle;
+        m_width = width;
+        m_height = height;
         return AZ::RHI::ResultCode::Success;
     }
+    
+    AZ::u32 SwapChain::View::GetCurrentImageIndex() const
+    {
+        return m_activeImageIndex;
+    }
+
+    AZ::RHI::ResultCode SwapChain::Image::Init(XrSwapchainImageVulkan2KHR swapchainImage)
+    {
+        m_swapchainImage = swapchainImage;
+        return AZ::RHI::ResultCode::Success;
+    }
+
+    VkImage SwapChain::Image::GetNativeImage()
+    {
+        return m_swapchainImage.image;
+    }
+
+    XrSwapchain SwapChain::View::GetSwapChainHandle() const
+    {
+        return m_handle;
+    }
+        
+    AZ::u32 SwapChain::View::GetWidth() const
+    {
+        return m_width;
+    }
+
+    AZ::u32 SwapChain::View::GetHeight() const
+    {
+        return m_height;
+    }
+
+    void SwapChain::View::Shutdown()
+    {
+        xrDestroySwapchain(m_handle);
+    }
 
     AZ::RHI::ResultCode SwapChain::InitInternal()
     {
-        // xrEnumerateViewConfigurationViews
-        for (int i = 0; i < m_views.size(); i++)
+        Instance* xrVkInstance = static_cast<Instance*>(GetDescriptor().m_instance.get());
+        Session* xrVkSession = static_cast<Session*>(GetDescriptor().m_session.get());
+        Device* xrDevice = static_cast<Device*>(GetDescriptor().m_device.get());
+        XrInstance xrInstance = xrVkInstance->GetXRInstance();
+        XrSystemId xrSystemId = xrVkInstance->GetXRSystemId();
+        XrSession xrSession = xrVkSession->GetXrSession();
+
+        // Read graphics properties for preferred swapchain length and logging.
+        XrSystemProperties systemProperties{ XR_TYPE_SYSTEM_PROPERTIES };
+        XrResult result = xrGetSystemProperties(xrInstance, xrSystemId, &systemProperties);
+        WARN_IF_UNSUCCESSFUL(result);
+
+        if(GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled)
         {
-            // xrCreateSwapchain
-            //AZStd::intrusive_ptr<SwapChain::View> vSwapChain = OpenXRVk::Factory::Get()->ViewSwapChain();
-
-            //if (vSwapChain)
-            //{
-                //xrCreateSwapchain(.., xrSwapchainHandle, .) vSwapChain->Init(xrSwapchainHandle, ..);
-                //m_viewSwapchains.push_back(vSwapChain);
-            //}
+            // Log system properties.
+            AZ_Printf("OpenXrVk", "System Properties: Name=%s VendorId=%d\n", systemProperties.systemName, systemProperties.vendorId);
+            AZ_Printf("OpenXrVk",
+                "System Graphics Properties: MaxWidth=%d MaxHeight=%d MaxLayers=%d\n",
+                    systemProperties.graphicsProperties.maxSwapchainImageWidth, systemProperties.graphicsProperties.maxSwapchainImageHeight,
+                    systemProperties.graphicsProperties.maxLayerCount);
+            AZ_Printf("OpenXrVk",
+                "System Tracking Properties: OrientationTracking=%s PositionTracking=%s\n",
+                    systemProperties.trackingProperties.orientationTracking == XR_TRUE ? "True" : "False",
+                    systemProperties.trackingProperties.positionTracking == XR_TRUE ? "True" : "False");
         }
+
+        //Only supporting XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO for now
+        XrViewConfigurationType viewConfigType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
+
+        result = xrEnumerateViewConfigurationViews(xrInstance, xrSystemId, 
+                                                            viewConfigType, 0, &m_numViews, nullptr);
+        WARN_IF_UNSUCCESSFUL(result);
+
+        m_configViews.resize(m_numViews, { XR_TYPE_VIEW_CONFIGURATION_VIEW });
+        result = xrEnumerateViewConfigurationViews(xrInstance, xrSystemId, 
+                                                viewConfigType, m_numViews, &m_numViews, m_configViews.data());
+        WARN_IF_UNSUCCESSFUL(result);
+
+        // Create and cache view buffer for xrLocateViews later.
+        xrDevice->InitXrViews(m_numViews);
+
+        // Create the swapchain and get the images.
+        if (m_numViews > 0)
+        {
+            // Select a swapchain format.
+            uint32_t swapchainFormatCount = 0;
+            result = xrEnumerateSwapchainFormats(xrSession, 0, &swapchainFormatCount, nullptr);
+            AZStd::vector<int64_t> swapChainFormats(swapchainFormatCount);
+            result = xrEnumerateSwapchainFormats(xrSession, aznumeric_cast<uint32_t>(swapChainFormats.size()),
+                                                &swapchainFormatCount, swapChainFormats.data());
+            WARN_IF_UNSUCCESSFUL(result);
+            AZ_Assert(swapchainFormatCount == swapChainFormats.size(), "Size mismatch swapchainFormatCount %i swapChainFormats size %i", swapchainFormatCount, swapChainFormats.size());
+
+            m_colorSwapChainFormat = SelectColorSwapChainFormat(swapChainFormats);
+
+            // Print swapchain formats and the selected one.
+            if (GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled)
+            {
+                AZStd::string swapchainFormatsString;
+                for (int64_t format : swapChainFormats)
+                {
+                    const bool selected = format == m_colorSwapChainFormat;
+                    swapchainFormatsString += " ";
+                    if (selected)
+                    {
+                        swapchainFormatsString += "[";
+                    }
+                    swapchainFormatsString += AZStd::string::format("%i", format);
+                    if (selected)
+                    {
+                        swapchainFormatsString += "]";
+                    }
+                }
+                AZ_Printf("OpenXrVk", "Swapchain Formats: %s\n", swapchainFormatsString.c_str());
+            }
+
+            // Create a swapchain for each view.
+            for (uint32_t i = 0; i < m_numViews; i++)
+            {
+                const XrViewConfigurationView& configView = m_configViews[i];
+
+                if (GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled)
+                {
+                    AZ_Printf("OpenXrVk",
+                          "Creating swapchain for view %d with dimensions Width=%d Height=%d SampleCount=%d\n", i,
+                        configView.recommendedImageRectWidth, configView.recommendedImageRectHeight, configView.recommendedSwapchainSampleCount);
+                }
+
+                XR::Ptr<XR::SwapChain::View> baseViewSwapChain = XR::Factory::Get().CreateSwapChainView();
+                SwapChain::View* viewSwapChain = static_cast<SwapChain::View*>(baseViewSwapChain.get());
+                if (viewSwapChain)
+                {
+                    // Create the xr swapchain.
+                    XrSwapchainCreateInfo swapchainCreateInfo{ XR_TYPE_SWAPCHAIN_CREATE_INFO };
+                    swapchainCreateInfo.arraySize = m_arraySize;
+                    swapchainCreateInfo.format = m_colorSwapChainFormat;
+                    swapchainCreateInfo.width = configView.recommendedImageRectWidth;
+                    swapchainCreateInfo.height = configView.recommendedImageRectHeight;
+                    swapchainCreateInfo.mipCount = m_mipCount;
+                    swapchainCreateInfo.faceCount = m_faceCount;
+                    swapchainCreateInfo.sampleCount = m_sampleCount;
+                    swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
+
+                    XrSwapchain handle = XR_NULL_HANDLE;
+                    result = xrCreateSwapchain(xrSession, &swapchainCreateInfo, &handle);
+                    WARN_IF_UNSUCCESSFUL(result);
+
+                    AZ::RHI::ResultCode resultCode = viewSwapChain->Init(handle, swapchainCreateInfo.width, swapchainCreateInfo.height);
+                    if(resultCode == AZ::RHI::ResultCode::Success)
+                    { 
+                        m_viewSwapchains.push_back(viewSwapChain);
+                    }
+                }
+
+                result = xrEnumerateSwapchainImages(viewSwapChain->GetSwapChainHandle(), 0, &viewSwapChain->m_numImages, nullptr);
+                WARN_IF_UNSUCCESSFUL(result);
+                
+                viewSwapChain->m_swapChainImageHeaders.resize(viewSwapChain->m_numImages);
+                viewSwapChain->m_swapchainImages.resize(viewSwapChain->m_numImages);
+                for (AZ::u32 j = 0; j < viewSwapChain->m_numImages; ++j)
+                {
+                    viewSwapChain->m_swapchainImages[j] = { XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR };
+                    viewSwapChain->m_swapChainImageHeaders[j] = reinterpret_cast<XrSwapchainImageBaseHeader*>(&viewSwapChain->m_swapchainImages[j]);
+                }
+
+                result = xrEnumerateSwapchainImages(viewSwapChain->GetSwapChainHandle(), viewSwapChain->m_numImages, &viewSwapChain->m_numImages, viewSwapChain->m_swapChainImageHeaders[0]);
+                WARN_IF_UNSUCCESSFUL(result);
+                for (uint32_t j = 0; j < viewSwapChain->m_numImages; ++j)
+                {
+                    XR::Ptr<XR::SwapChain::Image> baseViewSwapChainImage = XR::Factory::Get().CreateSwapChainImage();
+                    SwapChain::Image* viewSwapChainImage = static_cast<SwapChain::Image*>(baseViewSwapChainImage.get());
+                    AZ::RHI::ResultCode resultCode = viewSwapChainImage->Init(viewSwapChain->m_swapchainImages[j]);
+                    if (resultCode == AZ::RHI::ResultCode::Success)
+                    {
+                        viewSwapChain->m_images.push_back(baseViewSwapChainImage);
+                    }
+                }
+            }
+        }
+
+        return AZ::RHI::ResultCode::Success;
+    }
+
+    AZ::s64 SwapChain::SelectColorSwapChainFormat(const AZStd::vector<int64_t>& runtimeFormats) const
+    {
+        // List of supported color swapchain formats.
+        constexpr AZ::s64 SupportedColorSwapchainFormats[] = { VK_FORMAT_B8G8R8A8_UNORM };
+
+        auto swapchainFormatIt =
+            AZStd::find_first_of(runtimeFormats.begin(), runtimeFormats.end(), AZStd::begin(SupportedColorSwapchainFormats),
+                AZStd::end(SupportedColorSwapchainFormats));
+        if (swapchainFormatIt == runtimeFormats.end()) 
+        {
+            AZ_Error("OpenXrVk", false, "No runtime swapchain format supported for color swapchain");
+        }
+
+        return *swapchainFormatIt;
+    }
+
+    AZStd::vector<XrViewConfigurationView> SwapChain::GetViewConfigs() const
+    {
+        return m_configViews;
+    }
+
+    AZ::RHI::ResultCode SwapChain::GetSwapChainImage(AZ::RHI::XRSwapChainDescriptor* swapchainDescriptor) const
+    {
+        AZ::Vulkan::XRSwapChainDescriptor* xrSwapChainDescriptor = static_cast<AZ::Vulkan::XRSwapChainDescriptor*>(swapchainDescriptor);
+        uint32_t swapChainIndex = xrSwapChainDescriptor->m_inputData.m_swapChainIndex;
+        uint32_t swapChainImageIndex = xrSwapChainDescriptor->m_inputData.m_swapChainImageIndex;
+        
+        XR::SwapChain::View* viewSwapChain = GetView(swapChainIndex);
+        SwapChain::Image* swapchainImage = static_cast<SwapChain::Image*>(viewSwapChain->m_images[swapChainImageIndex].get());
+        xrSwapChainDescriptor->m_outputData.m_nativeImage = swapchainImage->GetNativeImage();
         return AZ::RHI::ResultCode::Success;
     }
-    */
+
+    AZ::u32 SwapChain::GetSwapChainWidth(AZ::u32 viewIndex) const
+    {
+        return m_configViews[viewIndex].recommendedImageRectWidth;
+    }
+
+    AZ::u32 SwapChain::GetSwapChainHeight(AZ::u32 viewIndex) const
+    {
+        return m_configViews[viewIndex].recommendedImageRectHeight;
+    }
+
+    void SwapChain::ShutdownInternal()
+    {
+        for(XR::Ptr<XR::SwapChain::View> viewSwapChain : m_viewSwapchains)
+        {
+            viewSwapChain->Shutdown();
+        }
+    }
 }

+ 37 - 3
Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp

@@ -6,10 +6,14 @@
  *
  */
 
-#include <OpenXrVk/OpenXrVkSystemComponent.h>
-#include <OpenXrVk/OpenXrVkInstance.h>
-#include <OpenXrVk/OpenXrVkDevice.h>
 #include <AzCore/Serialization/SerializeContext.h>
+#include <OpenXrVk/OpenXrVkDevice.h>
+#include <OpenXrVk/OpenXrVkInput.h>
+#include <OpenXrVk/OpenXrVkInstance.h>
+#include <OpenXrVk/OpenXrVkSession.h>
+#include <OpenXrVk/OpenXrVkSpace.h>
+#include <OpenXrVk/OpenXrVkSwapchain.h>
+#include <OpenXrVk/OpenXrVkSystemComponent.h>
 
 namespace OpenXRVk
 {
@@ -48,6 +52,36 @@ namespace OpenXRVk
         return Device::Create();
     }
 
+    XR::Ptr<XR::Session> SystemComponent::CreateSession()
+    {
+        return Session::Create();
+    }
+    
+    XR::Ptr<XR::Input> SystemComponent::CreateInput()
+    {
+        return Input::Create();
+    }
+
+    XR::Ptr<XR::Space> SystemComponent::CreateSpace()
+    {
+        return Space::Create();
+    }
+
+    XR::Ptr<XR::SwapChain> SystemComponent::CreateSwapChain()
+    {
+        return SwapChain::Create();
+    }
+
+    XR::Ptr<XR::SwapChain::View> SystemComponent::CreateSwapChainView()
+    {
+        return SwapChain::View::Create();
+    }
+
+    XR::Ptr<XR::SwapChain::Image> SystemComponent::CreateSwapChainImage()
+    {
+        return SwapChain::Image::Create();
+    }
+
     void SystemComponent::Activate()
     {
     }

+ 0 - 2
Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake

@@ -8,7 +8,6 @@
 
 set(FILES
     Include/OpenXRVk/OpenXRVkDevice.h
-    Include/OpenXRVk/OpenXRVkGraphicsBinding.h
     Include/OpenXRVk/OpenXRVkInput.h
     Include/OpenXRVk/OpenXRVkInstance.h
     Include/OpenXRVk/OpenXRVkPhysicalDevice.h
@@ -20,7 +19,6 @@ set(FILES
     Include/OpenXRVk/OpenXRVkFunctionLoader.h
     Include/OpenXRVk/OpenXRVkGladFunctionLoader.h
     Source/OpenXRVkDevice.cpp
-    Source/OpenXRVkGraphicsBinding.cpp
     Source/OpenXRVkInput.cpp
     Source/OpenXRVkInstance.cpp
     Source/OpenXRVkPhysicalDevice.cpp