Преглед на файлове

Merge remote-tracking branch 'OpenXRVk/main' into initial-xr

byrcolin преди 3 години
родител
ревизия
cdc1eb91fe
променени са 69 файла, в които са добавени 3407 реда и са изтрити 0 реда
  1. 12 0
      Gems/OpenXRVk/3rdParty/Platform/Android/BuiltInPackages_android.cmake
  2. 12 0
      Gems/OpenXRVk/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake
  3. 10 0
      Gems/OpenXRVk/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake
  4. 12 0
      Gems/OpenXRVk/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake
  5. 10 0
      Gems/OpenXRVk/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake
  6. 17 0
      Gems/OpenXRVk/CMakeLists.txt
  7. 81 0
      Gems/OpenXRVk/Code/CMakeLists.txt
  8. 72 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkDevice.h
  9. 38 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkFunctionLoader.h
  10. 88 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInput.h
  11. 86 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInstance.h
  12. 19 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkPhysicalDevice.h
  13. 72 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h
  14. 60 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSpace.h
  15. 101 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSwapChain.h
  16. 63 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h
  17. 66 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkUtils.h
  18. 32 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXrVkGladFunctionLoader.h
  19. 17 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Android/OpenXRVk_Android.h
  20. 9 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Android/OpenXRVk_Platform.h
  21. 12 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Android/platform_private_android_files.cmake
  22. 17 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Linux/OpenXRVk_Linux.h
  23. 10 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Linux/OpenXRVk_Platform.h
  24. 11 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Linux/platform_private_linux_files.cmake
  25. 17 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Mac/Atom_RHI_Vulkan_Mac.h
  26. 10 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Mac/Atom_RHI_Vulkan_Platform.h
  27. 10 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Mac/platform_private_mac_files.cmake
  28. 9 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Windows/OpenXRVk_Platform.h
  29. 25 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Windows/OpenXRVk_Windows.h
  30. 12 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Windows/platform_private_windows_files.cmake
  31. 10 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/iOS/Atom_RHI_Vulkan_Platform.h
  32. 11 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/iOS/platform_builders_ios_files.cmake
  33. 10 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/iOS/platform_private_ios_files.cmake
  34. 271 0
      Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp
  35. 59 0
      Gems/OpenXRVk/Code/Source/OpenXRVkFunctionLoader.cpp
  36. 44 0
      Gems/OpenXRVk/Code/Source/OpenXRVkGladFunctionLoader.cpp
  37. 294 0
      Gems/OpenXRVk/Code/Source/OpenXRVkInput.cpp
  38. 400 0
      Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp
  39. 45 0
      Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp
  40. 34 0
      Gems/OpenXRVk/Code/Source/OpenXRVkPhysicalDevice.cpp
  41. 330 0
      Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp
  42. 155 0
      Gems/OpenXRVk/Code/Source/OpenXRVkSpace.cpp
  43. 276 0
      Gems/OpenXRVk/Code/Source/OpenXRVkSwapChain.cpp
  44. 92 0
      Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp
  45. 77 0
      Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp
  46. 11 0
      Gems/OpenXRVk/Code/Source/Platform/Android/OpenXRVk_Traits_Android.h
  47. 10 0
      Gems/OpenXRVk/Code/Source/Platform/Android/OpenXRVk_Traits_Platform.h
  48. 9 0
      Gems/OpenXRVk/Code/Source/Platform/Android/PAL_android.cmake
  49. 12 0
      Gems/OpenXRVk/Code/Source/Platform/Android/platform_private_android_files.cmake
  50. 8 0
      Gems/OpenXRVk/Code/Source/Platform/Android/platform_private_static_android.cmake
  51. 9 0
      Gems/OpenXRVk/Code/Source/Platform/Common/Unimplemented/Empty_Unimplemented.cpp
  52. 31 0
      Gems/OpenXRVk/Code/Source/Platform/Common/Unimplemented/ModuleStub_Unimplemented.cpp
  53. 11 0
      Gems/OpenXRVk/Code/Source/Platform/Linux/OpenXRVk_Traits_Linux.h
  54. 10 0
      Gems/OpenXRVk/Code/Source/Platform/Linux/OpenXRVk_Traits_Platform.h
  55. 9 0
      Gems/OpenXRVk/Code/Source/Platform/Linux/PAL_linux.cmake
  56. 12 0
      Gems/OpenXRVk/Code/Source/Platform/Linux/platform_private_linux_files.cmake
  57. 8 0
      Gems/OpenXRVk/Code/Source/Platform/Linux/platform_private_static_linux.cmake
  58. 9 0
      Gems/OpenXRVk/Code/Source/Platform/Mac/PAL_mac.cmake
  59. 10 0
      Gems/OpenXRVk/Code/Source/Platform/Windows/OpenXRVk_Traits_Platform.h
  60. 11 0
      Gems/OpenXRVk/Code/Source/Platform/Windows/OpenXRVk_Traits_Windows.h
  61. 10 0
      Gems/OpenXRVk/Code/Source/Platform/Windows/PAL_windows.cmake
  62. 8 0
      Gems/OpenXRVk/Code/Source/Platform/Windows/platform_private_static_windows.cmake
  63. 12 0
      Gems/OpenXRVk/Code/Source/Platform/Windows/platform_private_windows_files.cmake
  64. 9 0
      Gems/OpenXRVk/Code/Source/Platform/iOS/PAL_ios.cmake
  65. 39 0
      Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake
  66. 11 0
      Gems/OpenXRVk/Code/openxrvk_private_common_shared_files.cmake
  67. 11 0
      Gems/OpenXRVk/Code/openxrvk_stub_module.cmake
  68. 19 0
      Gems/OpenXRVk/gem.json
  69. BIN
      Gems/OpenXRVk/preview.png

+ 12 - 0
Gems/OpenXRVk/3rdParty/Platform/Android/BuiltInPackages_android.cmake

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

+ 12 - 0
Gems/OpenXRVk/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake

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

+ 10 - 0
Gems/OpenXRVk/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake

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

+ 12 - 0
Gems/OpenXRVk/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake

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

+ 10 - 0
Gems/OpenXRVk/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake

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

+ 17 - 0
Gems/OpenXRVk/CMakeLists.txt

@@ -0,0 +1,17 @@
+#
+# 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
+#
+#
+
+set(gem_path ${CMAKE_CURRENT_LIST_DIR})
+set(gem_json ${gem_path}/gem.json)
+o3de_restricted_path(${gem_json} gem_restricted_path gem_parent_relative_path)
+
+o3de_pal_dir(pal_3rdparty_dir ${CMAKE_CURRENT_LIST_DIR}/3rdParty/Platform/${PAL_PLATFORM_NAME} "${gem_restricted_path}" "${gem_path}" "${gem_parent_relative_path}")
+
+include(${pal_3rdparty_dir}/BuiltInPackages_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
+
+add_subdirectory(Code)

+ 81 - 0
Gems/OpenXRVk/Code/CMakeLists.txt

@@ -0,0 +1,81 @@
+#
+# 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
+#
+#
+
+o3de_pal_dir(pal_include_dir ${CMAKE_CURRENT_LIST_DIR}/Include/OpenXRVk/Platform/${PAL_PLATFORM_NAME} "${gem_restricted_path}" "${gem_path}" "${gem_parent_relative_path}")
+o3de_pal_dir(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME} "${gem_restricted_path}" "${gem_path}" "${gem_parent_relative_path}")
+
+include(${pal_source_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
+
+if(PAL_TRAIT_OPENXRVK_TARGETS_ALREADY_DEFINED)
+    return() # OpenXRVk targets already defined in PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
+endif()
+
+if(NOT PAL_TRAIT_OPENXRVK_SUPPORTED)
+
+    # Create stub modules. Once we support gem loading configuration, we can remove this stubbed targets
+    ly_add_target(
+        NAME OpenXRVk ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
+        NAMESPACE Gem
+        FILES_CMAKE
+            openxrvk_stub_module.cmake
+        INCLUDE_DIRECTORIES
+            PRIVATE
+                Source
+        BUILD_DEPENDENCIES
+            PRIVATE
+                AZ::AzCore
+    )
+
+    return() # Do not create the rest of the targets
+
+endif()
+
+ly_add_target(
+    NAME OpenXRVk.Static STATIC
+    NAMESPACE Gem
+    FILES_CMAKE
+        openxrvk_private_common_files.cmake
+        ${pal_source_dir}/platform_private_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
+        ${pal_include_dir}/platform_private_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
+    PLATFORM_INCLUDE_FILES
+        ${pal_source_dir}/platform_private_static_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
+    INCLUDE_DIRECTORIES
+        PRIVATE
+            Source
+            ${pal_source_dir}
+        PUBLIC
+            Include
+            ${pal_include_dir}
+    BUILD_DEPENDENCIES
+        PUBLIC
+            AZ::AzCore
+            AZ::AzFramework
+            3rdParty::OpenXR
+            3rdParty::glad_vulkan
+            AZ::AtomCore
+	    Gem::Atom_RHI_Vulkan.Reflect
+            Gem::XR.Static
+)
+
+ly_add_target(
+    NAME OpenXRVk ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
+    NAMESPACE Gem
+    FILES_CMAKE
+        openxrvk_private_common_shared_files.cmake
+    INCLUDE_DIRECTORIES
+        PRIVATE
+            Source
+            ${pal_source_dir}
+        PUBLIC
+            Include
+            ${pal_include_dir}
+    BUILD_DEPENDENCIES
+        PRIVATE
+            Gem::OpenXRVk.Static
+)
+

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

@@ -0,0 +1,72 @@
+/*
+ * 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/XRDevice.h>
+#include <XR/XRSwapChain.h>
+#include <OpenXRVk_Platform.h>
+
+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:
+        AZ_CLASS_ALLOCATOR(Device, AZ::SystemAllocator, 0);
+        AZ_RTTI(Device, "{81FD9B99-EDA5-4381-90EC-335073554379}", XR::Device);
+
+        static XR::Ptr<Device> Create();
+
+        //////////////////////////////////////////////////////////////////////////
+        // 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;
+    };
+}

+ 38 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkFunctionLoader.h

@@ -0,0 +1,38 @@
+/*
+ * 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/Memory/SystemAllocator.h>
+#include <AzCore/std/smart_ptr/unique_ptr.h>
+#include <AzCore/Module/DynamicModuleHandle.h>
+
+namespace OpenXRVk
+{
+    class FunctionLoader
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(FunctionLoader, AZ::SystemAllocator, 0);
+
+        static AZStd::unique_ptr<FunctionLoader> Create();
+
+        //! Load vulkan specific dlls.
+        bool Init();
+
+        //! Unload the dlls.
+        void Shutdown();
+
+        virtual ~FunctionLoader();
+    protected:
+
+        virtual bool InitInternal() = 0;
+        virtual void ShutdownInternal() = 0;
+
+        AZStd::unique_ptr<AZ::DynamicModuleHandle> m_moduleHandle;
+    };
+}

+ 88 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInput.h

@@ -0,0 +1,88 @@
+/*
+ * 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/XRinput.h>
+#include <OpenXRVk_Platform.h>
+
+namespace OpenXRVk
+{
+    // Class that will help manage XrActionSet/XrAction
+    class Input final
+        : public XR::Input
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(Input, AZ::SystemAllocator, 0);
+        AZ_RTTI(Input, "{97ADD1FE-27DF-4F36-9F61-683F881F9477}", XR::Input);
+
+        static XR::Ptr<Input> Create();
+    
+        //! Sync all the actions and update controller
+        //! as well as various tracked space poses 
+        void PollActions() 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:
+
+        //! 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;
+    };
+}

+ 86 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInstance.h

@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/std/containers/vector.h>
+#include <Atom/RHI/ValidationLayer.h>
+#include <OpenXRVk_Platform.h>
+#include <OpenXRVk/OpenXRVkFunctionLoader.h>
+#include <OpenXRVk/OpenXRVkPhysicalDevice.h>
+#include <XR/XRInstance.h>
+
+namespace OpenXRVk
+{
+    //! Vulkan specific XR instance back-end class that will help manage 
+    //! XR specific vulkan native objects
+    class Instance final
+    : public XR::Instance
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(Instance, AZ::SystemAllocator, 0);
+        AZ_RTTI(Instance, "{1A62DF32-2909-431C-A938-B1E841A50768}", XR::Instance);
+
+        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() const override;
+        AZ::RHI::ResultCode GetXRPhysicalDevice(AZ::RHI::XRPhysicalDeviceDescriptor* physicalDeviceDescriptor, int32_t index) override;
+        //////////////////////////////////////////////////////////////////////////
+		
+        //! Enumerate supported extension names.
+        XR::StringList GetInstanceExtensionNames(const char* layerName = nullptr) const;
+		
+        //! Enumerate supported layer names.
+        XR::StringList GetInstanceLayerNames() const;
+
+        //! Enumerate and log view configurations.
+        void LogViewConfigurations();
+
+        //! Enumerate and log environment blend mode.
+        void LogEnvironmentBlendMode(XrViewConfigurationType type);
+
+        //! Get the XRInstance.
+        XrInstance GetXRInstance() const;
+
+        //! Get System id.
+        XrSystemId GetXRSystemId() const;
+
+        //! Get native VkInstance.
+        VkInstance GetNativeInstance() const;
+
+        //! Get XR environment blend mode.
+        XrEnvironmentBlendMode GetEnvironmentBlendMode() const;
+
+        //! Get XR configuration type.
+        XrViewConfigurationType GetViewConfigType() const;
+
+        //! Ge the active VkPhysicalDevice.
+        VkPhysicalDevice GetActivePhysicalDevice() const;
+
+    private:
+
+        //! Clean native objects. 
+        void ShutdownInternal() override;
+
+        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;
+        XR::RawStringList m_requiredLayers;
+        XR::RawStringList m_requiredExtensions;
+        AZStd::unique_ptr<FunctionLoader> m_functionLoader;
+        AZStd::vector<VkPhysicalDevice> m_supportedXRDevices;
+        AZ::u32 m_physicalDeviceActiveIndex = 0;
+    };
+}

+ 19 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkPhysicalDevice.h

@@ -0,0 +1,19 @@
+/*
+ * 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/std/containers/vector.h>
+#include <OpenXRVk_Platform.h>
+
+namespace OpenXRVk::PhysicalDevice
+{
+    //! API to enumerate and return physical devices.
+    static AZStd::vector<VkPhysicalDevice> EnumerateDeviceList(XrSystemId xrSystemId, XrInstance xrInstance, VkInstance vkInstance); 
+}
+

+ 72 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h

@@ -0,0 +1,72 @@
+/*
+ * 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/std/smart_ptr/intrusive_ptr.h>
+#include <OpenXRVk_Platform.h>
+#include <OpenXRVk/OpenXRVkSpace.h>
+#include <XR/XRSession.h>
+
+namespace OpenXRVk
+{
+    // Class that will help manage XrSession
+    class Session final
+        : public XR::Session
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(Session, AZ::SystemAllocator, 0);
+        AZ_RTTI(Session, "{6C899F0C-9A3D-4D79-8E4F-92AFB67E5EB1}", XR::Session);
+
+        static XR::Ptr<Session> Create();
+
+        //! Print out all the supported Reference space
+        void LogReferenceSpaces();
+
+        //! 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:
+
+        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;        
+    };
+}

+ 60 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSpace.h

@@ -0,0 +1,60 @@
+/*
+ * 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/XRSpace.h>
+#include <XR/XRBase.h>
+#include <OpenXRVk_Platform.h>
+#include <AzCore/Preprocessor/Enum.h>
+
+namespace OpenXRVk
+{
+    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
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(Space, AZ::SystemAllocator, 0);
+        AZ_RTTI(Space, "{E99557D0-9061-4691-9524-CE0ACC3A14FA}", XR::Space);
+        
+        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);
+
+        //! Get the XrSpace for a given SpaceType
+        XrSpace GetXrSpace(SpaceType spaceType) const;
+
+    private:
+
+        //! XrPose specific matrix translation, Rotation functions
+        XrPosef Identity();
+        XrPosef Translation(const XrVector3f& translation);
+        XrPosef RotateCCWAboutYAxis(float radians, XrVector3f translation);
+
+        AZStd::vector<XrSpace> m_xrSpaces;
+    };
+}

+ 101 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSwapChain.h

@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <XR/XRSwapChain.h>
+#include <OpenXRVk_Platform.h>
+
+namespace OpenXRVk
+{
+    //! Class that will help manage native xr swapchains and swapchain images
+    class SwapChain final
+        : public XR::SwapChain
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(SwapChain, AZ::SystemAllocator, 0);
+        AZ_RTTI(SwapChain, "{3DD88236-8C9F-4864-86F5-018C198BC07E}", XR::SwapChain);
+
+        static XR::Ptr<SwapChain> Create();
+
+        //! This class helps manage the native swapchain image. 
+        class Image final
+            : public XR::SwapChain::Image
+        {
+        public:
+            AZ_CLASS_ALLOCATOR(Image, AZ::SystemAllocator, 0);
+            AZ_RTTI(Image, "{717ABDD4-C050-4FDF-8E93-3784F81FE315}", XR::SwapChain::Image);
+
+            static XR::Ptr<Image> Create();
+
+            AZ::RHI::ResultCode Init(XrSwapchainImageVulkan2KHR swapchainImage);
+            VkImage GetNativeImage();
+        private:
+            XrSwapchainImageVulkan2KHR m_swapchainImage;
+        };
+
+        //! This class helps manage the native swapchain for a given view. 
+        class View final
+            : public XR::SwapChain::View
+        {
+        public:
+            AZ_CLASS_ALLOCATOR(View, AZ::SystemAllocator, 0);
+            AZ_RTTI(View, "{F8312427-AC2D-4737-9A8F-A16ADA5319D0}", XR::SwapChain::View);
+
+            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 = XR_NULL_HANDLE;
+        };
+
+        //! 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;
+        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; 
+    };
+}

+ 63 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h

@@ -0,0 +1,63 @@
+/*
+ * 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/XRFactory.h>
+#include <AzCore/Component/Component.h>
+
+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(SystemComponent, "{C0ABD1CE-FD3C-48C3-8AE8-C098BCCFC604}");
+
+        static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
+        static void Reflect(AZ::ReflectContext* context);
+
+        SystemComponent();
+        ~SystemComponent();
+
+        //////////////////////////////////////////////////////////////////////////
+        // Component
+        void Activate() override;
+        void Deactivate() override;
+        //////////////////////////////////////////////////////////////////////////
+
+        ///////////////////////////////////////////////////////////////////
+        // XR::Factory overrides
+        //! Create OpenXRVk::Instance object
+        XR::Ptr<XR::Instance> CreateInstance() override;
+
+        //! 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;
+        ///////////////////////////////////////////////////////////////////
+    };
+}

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

@@ -0,0 +1,66 @@
+/*
+ * 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/RHI.Reflect/Base.h>
+#include <OpenXRVk_Platform.h>
+#include <XR/XRBase.h>
+
+// Macro to generate stringify functions for OpenXR enumerations based data provided in openxr_reflection.h
+#define ENUM_CASE_STR(name, val) case name: return #name;
+#define MAKE_XR_TO_STRING_FUNC(enumType)                  \
+    inline const char* to_string(enumType e) {         \
+        switch (e) {                                   \
+            XR_LIST_ENUM_##enumType(ENUM_CASE_STR)     \
+            default: return "Unknown " #enumType;      \
+        }                                              \
+    }
+
+
+MAKE_XR_TO_STRING_FUNC(XrReferenceSpaceType);
+MAKE_XR_TO_STRING_FUNC(XrViewConfigurationType);
+MAKE_XR_TO_STRING_FUNC(XrEnvironmentBlendMode);
+MAKE_XR_TO_STRING_FUNC(XrSessionState);
+MAKE_XR_TO_STRING_FUNC(XrResult);
+MAKE_XR_TO_STRING_FUNC(XrFormFactor);
+
+namespace OpenXRVk
+{
+
+#define RETURN_XR_RESULT_IF_UNSUCCESSFUL(result) \
+    if (result != XR_SUCCESS) {\
+        return result;\
+    }
+
+#define RETURN_IF_UNSUCCESSFUL(result) \
+    if (result != XR_SUCCESS) {\
+        return;\
+    }
+    
+#define WARN_IF_UNSUCCESSFUL(result) \
+    if (result != XR_SUCCESS) {\
+        AZ_Warning("OpenXrVk", false, "Warning error code: %s", to_string(result));\
+    }
+
+#define ASSERT_IF_UNSUCCESSFUL(result) \
+    if (result != XR_SUCCESS) {\
+        AZ_Assert(false, "Assert error code: %s", to_string(result));\
+    }
+
+    AZ::RHI::ResultCode ConvertResult(XrResult xrResult);
+    bool IsSuccess(XrResult result);
+    bool IsError(XrResult result);
+    const char* GetResultString(const XrResult result);
+    XR::RawStringList FilterList(const XR::RawStringList& source, const XR::StringList& filter);
+
+    //! Input is an array of chars with multiple ' ' char embedded in it, indicating the start of a new string. 
+    //! Iterate through the characters while caching the starting pointer to a string 
+    //! and every time ' ' is encountered replace it with '\0' to indicate the end of a string.
+    AZStd::vector<const char*> ParseExtensionString(char* names);
+}

+ 32 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXrVkGladFunctionLoader.h

@@ -0,0 +1,32 @@
+/*
+ * 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 <OpenXRVk/OpenXRVkFunctionLoader.h>
+#include <AzCore/Memory/SystemAllocator.h>
+
+namespace OpenXRVk
+{
+    class GladFunctionLoader:
+        public FunctionLoader
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(GladFunctionLoader, AZ::SystemAllocator, 0);
+
+        GladFunctionLoader() = default;
+        virtual ~GladFunctionLoader() = default;
+
+    private:
+        //////////////////////////////////////////////////////////////////////////
+        // FunctionLoader overrrides
+        //! Load the the function pointers from the vulkan dll.
+        bool InitInternal() override;
+        //! Unload appropriate data.
+        void ShutdownInternal() override;
+    };
+}

+ 17 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Android/OpenXRVk_Android.h

@@ -0,0 +1,17 @@
+/*
+ * 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/base.h>
+#include <AzCore/PlatformIncl.h>
+#include <AzCore/std/algorithm.h>
+#include <vulkan/vulkan.h>
+#include <limits.h>
+#include <RHI/Vulkan.h>
+
+#define AZ_VULKAN_SURFACE_EXTENSION_NAME VK_KHR_ANDROID_SURFACE_EXTENSION_NAME

+ 9 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Android/OpenXRVk_Platform.h

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

+ 12 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Android/platform_private_android_files.cmake

@@ -0,0 +1,12 @@
+#
+# 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
+#
+#
+
+set(FILES
+    OpenXRVk_Platform.h
+    OpenXRVk_Android.h
+)

+ 17 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Linux/OpenXRVk_Linux.h

@@ -0,0 +1,17 @@
+/*
+ * 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/base.h>
+#include <AzCore/PlatformIncl.h>
+#include <AzCore/std/algorithm.h>
+#include <vulkan/vulkan.h>
+#include <limits.h>
+#include <RHI/Vulkan.h>
+
+#define AZ_VULKAN_SURFACE_EXTENSION_NAME VK_KHR_XCB_SURFACE_EXTENSION_NAME

+ 10 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Linux/OpenXRVk_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * 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 <OpenXRVk_Linux.h>

+ 11 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Linux/platform_private_linux_files.cmake

@@ -0,0 +1,11 @@
+#
+# 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
+#
+#
+
+set(FILES
+    OpenXRVk_Platform.h
+)

+ 17 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Mac/Atom_RHI_Vulkan_Mac.h

@@ -0,0 +1,17 @@
+/*
+ * 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/base.h>
+#include <AzCore/PlatformIncl.h>
+#include <AzCore/std/algorithm.h>
+#include <vulkan/vulkan.h>
+#include <limits.h>
+#include <RHI/Vulkan.h>
+
+#define AZ_VULKAN_SURFACE_EXTENSION_NAME VK_MVK_MACOS_SURFACE_EXTENSION_NAME

+ 10 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Mac/Atom_RHI_Vulkan_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * 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_RHI_Vulkan_Mac.h>

+ 10 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Mac/platform_private_mac_files.cmake

@@ -0,0 +1,10 @@
+#
+# 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
+#
+#
+
+set(FILES
+)

+ 9 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Windows/OpenXRVk_Platform.h

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

+ 25 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Windows/OpenXRVk_Windows.h

@@ -0,0 +1,25 @@
+/*
+ * 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/base.h>
+#include <AzCore/PlatformIncl.h>
+#include <AzCore/std/algorithm.h>
+#include <limits.h>
+
+#include <Unknwn.h>
+
+#include <glad/vulkan.h>
+
+ // Tell OpenXR what platform code we'll be using
+#define XR_USE_PLATFORM_WIN32
+#define XR_USE_GRAPHICS_API_VULKAN
+
+#include <openxr/openxr.h>
+#include <openxr/openxr_platform.h>
+#include <openxr/openxr_reflection.h>

+ 12 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Windows/platform_private_windows_files.cmake

@@ -0,0 +1,12 @@
+#
+# 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
+#
+#
+
+set(FILES
+    OpenXRVk_Platform.h
+    OpenXRVk_Windows.h
+)

+ 10 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/iOS/Atom_RHI_Vulkan_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * 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
+
+

+ 11 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/iOS/platform_builders_ios_files.cmake

@@ -0,0 +1,11 @@
+#
+# 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
+#
+#
+
+set(FILES
+    Atom_RHI_Vulkan_Platform.h
+)

+ 10 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/iOS/platform_private_ios_files.cmake

@@ -0,0 +1,10 @@
+#
+# 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
+#
+#
+
+set(FILES
+)

+ 271 - 0
Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp

@@ -0,0 +1,271 @@
+/*
+ * 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/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>
+
+namespace OpenXRVk
+{
+    XR::Ptr<Device> Device::Create()
+    {
+        return aznew Device;
+    }
+
+    AZ::RHI::ResultCode Device::InitDeviceInternal(AZ::RHI::XRDeviceDescriptor* deviceDescriptor)
+    {
+        AZ::Vulkan::XRDeviceDescriptor* xrDeviceDescriptor = static_cast<AZ::Vulkan::XRDeviceDescriptor*>(deviceDescriptor);
+        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;
+        xrDeviceCreateInfo.vulkanCreateInfo = xrDeviceDescriptor->m_inputData.m_deviceCreateInfo;
+        xrDeviceCreateInfo.vulkanPhysicalDevice = xrVkInstance->GetActivePhysicalDevice();
+        xrDeviceCreateInfo.vulkanAllocator = nullptr;
+
+        PFN_xrGetVulkanDeviceExtensionsKHR pfnGetVulkanDeviceExtensionsKHR = nullptr;
+        XrResult result = xrGetInstanceProcAddr(
+            xrVkInstance->GetXRInstance(), "xrGetVulkanDeviceExtensionsKHR", reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetVulkanDeviceExtensionsKHR));
+        ASSERT_IF_UNSUCCESSFUL(result);
+
+        AZ::u32 deviceExtensionNamesSize = 0;
+        result = pfnGetVulkanDeviceExtensionsKHR(xrVkInstance->GetXRInstance(), xrDeviceCreateInfo.systemId, 0, &deviceExtensionNamesSize, nullptr);
+        ASSERT_IF_UNSUCCESSFUL(result);
+
+        AZStd::vector<char> deviceExtensionNames(deviceExtensionNamesSize);
+        result = pfnGetVulkanDeviceExtensionsKHR(
+	        xrVkInstance->GetXRInstance(), xrDeviceCreateInfo.systemId, deviceExtensionNamesSize, &deviceExtensionNamesSize, &deviceExtensionNames[0]);
+        ASSERT_IF_UNSUCCESSFUL(result);
+
+        AZStd::vector<const char*> extensions = ParseExtensionString(&deviceExtensionNames[0]);
+        for (uint32_t i = 0; i < xrDeviceCreateInfo.vulkanCreateInfo->enabledExtensionCount; ++i)
+        {
+            extensions.push_back(xrDeviceCreateInfo.vulkanCreateInfo->ppEnabledExtensionNames[i]);
+        }
+
+        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;
+        deviceInfo.enabledExtensionCount = aznumeric_cast<AZ::u32>(extensions.size());
+        deviceInfo.ppEnabledExtensionNames = extensions.empty() ? nullptr : extensions.data();
+
+        //Create VkDevice
+        auto pfnCreateDevice = (PFN_vkCreateDevice)xrDeviceCreateInfo.pfnGetInstanceProcAddr(xrVkInstance->GetNativeInstance(), "vkCreateDevice");
+        VkResult vulkanResult = pfnCreateDevice(xrDeviceCreateInfo.vulkanPhysicalDevice, &deviceInfo, xrDeviceCreateInfo.vulkanAllocator, &m_xrVkDevice);
+        if (vulkanResult != VK_SUCCESS)
+        {
+            return AZ::RHI::ResultCode::Fail;
+        }
+		
+        //Populate the output data of the descriptor
+        xrDeviceDescriptor->m_outputData.m_xrVkDevice = m_xrVkDevice;
+
+        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;
+        }
+    }
+}

+ 59 - 0
Gems/OpenXRVk/Code/Source/OpenXRVkFunctionLoader.cpp

@@ -0,0 +1,59 @@
+/*
+ * 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 <AzCore/std/containers/vector.h>
+#include <OpenXRVk/OpenXRVkFunctionLoader.h>
+#include <OpenXRVk_Platform.h>
+#include <OpenXRVk_Traits_Platform.h>
+
+namespace OpenXRVk
+{
+    FunctionLoader::~FunctionLoader()
+    {
+        AZ_Assert(!m_moduleHandle, "Shutdown was not called before destroying this FunctionLoader");
+    }
+
+    bool FunctionLoader::Init()
+    {
+        const AZStd::vector<const char*> libs = {
+            VULKAN_DLL,
+            VULKAN_1_DLL
+        };
+
+        for (const char* libName : libs)
+        {
+            m_moduleHandle = AZ::DynamicModuleHandle::Create(libName);
+            if (m_moduleHandle->Load(false))
+            {
+                break;
+            }
+            else
+            {
+                m_moduleHandle = nullptr;
+            }
+        }
+
+        if (!m_moduleHandle)
+        {
+            AZ_Warning("Vulkan", false, "Could not find Vulkan library.");
+            return false;
+        }
+            
+        return InitInternal();
+    }
+
+    void FunctionLoader::Shutdown()
+    {
+        ShutdownInternal();
+        if (m_moduleHandle)
+        {
+            m_moduleHandle->Unload();
+        }
+        m_moduleHandle = nullptr;
+    }
+}

+ 44 - 0
Gems/OpenXRVk/Code/Source/OpenXRVkGladFunctionLoader.cpp

@@ -0,0 +1,44 @@
+/*
+ * 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 <AzCore/PlatformIncl.h>
+#define GLAD_VULKAN_IMPLEMENTATION
+#include <OpenXRVk_Platform.h>
+
+#include <OpenXRVk/OpenXRVkGladFunctionLoader.h>
+#include <AzCore/Module/DynamicModuleHandle.h>
+
+namespace
+{
+    GLADapiproc LoadFunctionFromLibrary(void* userPtr, const char *name)
+    {
+        AZ::DynamicModuleHandle* moduleHandle = reinterpret_cast<AZ::DynamicModuleHandle*>(userPtr);
+        AZ_Assert(moduleHandle, "Invalid module handle");
+        return moduleHandle->GetFunction<GLADapiproc>(name);
+    }
+}
+
+namespace OpenXRVk
+{
+    AZStd::unique_ptr<FunctionLoader> FunctionLoader::Create()
+    {
+        return AZStd::make_unique<GladFunctionLoader>();
+    }
+
+    bool GladFunctionLoader::InitInternal()
+    {
+        // 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;
+    }
+
+    void GladFunctionLoader::ShutdownInternal()
+    {
+        gladLoaderUnloadVulkan();
+    }
+}

+ 294 - 0
Gems/OpenXRVk/Code/Source/OpenXRVkInput.cpp

@@ -0,0 +1,294 @@
+/*
+ * 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/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
+{
+    XR::Ptr<Input> Input::Create()
+    {
+        return aznew Input;
+    }
+
+    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)
+    {
+        XrSpaceLocation spaceLocation{ XR_TYPE_SPACE_LOCATION };
+        XrResult result = xrLocateSpace(space, 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_xrVisualizedSpaceLocations[static_cast<uint32_t>(visualizedSpaceType)] = spaceLocation;
+            }
+        }
+    }
+
+    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::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 m_handScale[viewIndex];
+    }
+
+    XrAction Input::GetGrabAction() const
+    {
+        return m_grabAction;
+    }
+
+    XrAction Input::GetPoseAction() const
+    {
+        return m_poseAction;
+    }
+
+    XrAction Input::GetVibrationAction() const
+    {
+        return m_vibrateAction;
+    }
+
+    XrAction Input::GetQuitAction() const
+    {
+        return m_quitAction;
+    }
+}

+ 400 - 0
Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp

@@ -0,0 +1,400 @@
+/*
+ * 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/OpenXRVkInstance.h>
+#include <OpenXRVk/OpenXRVkUtils.h>
+#include <Atom/RHI.Reflect/Vulkan/XRVkDescriptors.h>
+#include <AzCore/Casting/numeric_cast.h>
+
+namespace OpenXRVk
+{
+    XR::Ptr<Instance> Instance::Create()
+    {
+        return aznew Instance;
+    }
+
+    XR::StringList Instance::GetInstanceExtensionNames(const char* layerName /*= nullptr*/) const
+    {
+        XR::StringList extensionNames;
+        AZ::u32 extPropertyCount = 0;
+        XrResult result = xrEnumerateInstanceExtensionProperties(layerName, 0, &extPropertyCount, nullptr);
+        if (IsError(result) || extPropertyCount == 0)
+        {
+            return extensionNames;
+        }
+
+        AZStd::vector<XrExtensionProperties> extProperties;
+        extProperties.resize(extPropertyCount);
+
+        for (XrExtensionProperties& extension : extProperties)
+        {
+            extension.type = XR_TYPE_EXTENSION_PROPERTIES;
+        }
+
+        result = xrEnumerateInstanceExtensionProperties(layerName, aznumeric_cast<AZ::u32>(extProperties.size()), &extPropertyCount, extProperties.data());
+        if (IsError(result))
+        {
+            return extensionNames;
+        }
+
+        extensionNames.reserve(extensionNames.size() + extProperties.size());
+        for (uint32_t extPropertyIndex = 0; extPropertyIndex < extPropertyCount; extPropertyIndex++)
+        {
+            extensionNames.emplace_back(extProperties[extPropertyIndex].extensionName);
+        }
+
+        return extensionNames;
+    }
+
+    XR::StringList Instance::GetInstanceLayerNames() const
+    {
+        XR::StringList layerNames;
+        AZ::u32 layerPropertyCount = 0;
+        XrResult result = xrEnumerateApiLayerProperties(0, &layerPropertyCount, nullptr);
+        if (IsError(result) || layerPropertyCount == 0)
+        {
+            return layerNames;
+        }
+
+        AZStd::vector<XrApiLayerProperties> layerProperties(layerPropertyCount);
+        for (XrApiLayerProperties& layer : layerProperties)
+        {
+            layer.type = XR_TYPE_API_LAYER_PROPERTIES;
+        }
+
+        result = xrEnumerateApiLayerProperties(aznumeric_cast<AZ::u32>(layerProperties.size()), &layerPropertyCount, layerProperties.data());
+        if (IsError(result))
+        {
+            return layerNames;
+        }
+
+        layerNames.reserve(layerNames.size() + layerProperties.size());
+        for (uint32_t layerPropertyIndex = 0; layerPropertyIndex < layerPropertyCount; ++layerPropertyIndex)
+        {
+            layerNames.emplace_back(layerProperties[layerPropertyIndex].layerName);
+        }
+
+        return layerNames;
+    }
+
+    AZ::RHI::ResultCode Instance::InitInstanceInternal(AZ::RHI::ValidationMode validationMode)
+    {
+        XR::RawStringList optionalLayers = XR::RawStringList{};
+        XR::RawStringList optionalExtensions = XR::RawStringList{ { XR_KHR_VULKAN_ENABLE_EXTENSION_NAME } };
+
+        XR::StringList instanceLayerNames = GetInstanceLayerNames();
+        XR::RawStringList supportedLayers = FilterList(optionalLayers, instanceLayerNames);
+        m_requiredLayers.insert(m_requiredLayers.end(), supportedLayers.begin(), supportedLayers.end());
+
+        XR::StringList instanceExtensions = GetInstanceExtensionNames();
+        XR::RawStringList supportedExtensions = FilterList(optionalExtensions, instanceExtensions);
+        m_requiredExtensions.insert(m_requiredExtensions.end(), supportedExtensions.begin(), supportedExtensions.end());
+
+        if (validationMode == AZ::RHI::ValidationMode::Enabled)
+        {
+            AZ_Printf("OpenXrVk", "Available Extensions: (%i)\n", instanceExtensions.size());
+            for (const AZStd::string& extension : instanceExtensions)
+            {
+                AZ_Printf("OpenXrVk", "Name=%s\n", extension.c_str());
+            }
+
+            AZ_Printf("OpenXrVk", "Available Layers: (%i)\n", instanceLayerNames.size());
+            for (const AZStd::string& layerName : instanceLayerNames)
+            {
+                AZ_Printf("OpenXrVk", "Name=%s \n", layerName.c_str());
+            }
+        }
+
+        AZ_Assert(m_xrInstance == XR_NULL_HANDLE, "XR Instance is already initialized");
+        XrInstanceCreateInfo createInfo{ XR_TYPE_INSTANCE_CREATE_INFO };
+        createInfo.next = nullptr;
+        createInfo.enabledExtensionCount = aznumeric_cast<AZ::u32>(supportedExtensions.size());
+        createInfo.enabledExtensionNames = supportedExtensions.data();
+        createInfo.enabledApiLayerCount = aznumeric_cast<AZ::u32>(supportedLayers.size());
+        createInfo.enabledApiLayerNames = supportedLayers.data();
+
+        azstrncpy(createInfo.applicationInfo.applicationName, XR_MAX_APPLICATION_NAME_SIZE, "O3deApp", 4);
+        createInfo.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
+
+        //Create XR instance
+        XrResult result = xrCreateInstance(&createInfo, &m_xrInstance);
+        if(IsError(result))
+        {
+            AZ_Warning("OpenXrVk", false, "Failed to create XR instance");
+            return AZ::RHI::ResultCode::Fail;
+        }
+
+        if (validationMode == AZ::RHI::ValidationMode::Enabled)
+        {
+            XrInstanceProperties instanceProperties{ XR_TYPE_INSTANCE_PROPERTIES };
+            result = xrGetInstanceProperties(m_xrInstance, &instanceProperties);
+            if (IsSuccess(result))
+            {
+                AZStd::string verStr = AZStd::string::format("%d.%d.%d",
+                XR_VERSION_MAJOR(instanceProperties.runtimeVersion),
+                XR_VERSION_MINOR(instanceProperties.runtimeVersion),
+                XR_VERSION_PATCH(instanceProperties.runtimeVersion));
+                AZ_Printf("OpenXrVk", "Instance RuntimeName=%s RuntimeVersion=%s\n", instanceProperties.runtimeName, verStr.c_str());
+            }
+        }
+
+        AZ_Assert(m_xrInstance != XR_NULL_HANDLE, "XR Isntance is Null");
+        AZ_Assert(m_xrSystemId == XR_NULL_SYSTEM_ID, "XR System id already initialized");
+
+        //TODO::Add support for handheld display
+        m_formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
+
+        //TODO::Add support for other view configuration types
+        m_viewConfigType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
+
+        //TODO::Add support for other environment blend types
+        m_environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
+
+        XrSystemGetInfo systemInfo{ XR_TYPE_SYSTEM_GET_INFO };
+        systemInfo.formFactor = m_formFactor;
+        result = xrGetSystem(m_xrInstance, &systemInfo, &m_xrSystemId);
+        if (IsError(result))
+        {
+            AZ_Warning("OpenXrVk", false, "Failed to get XR System id");
+            return AZ::RHI::ResultCode::Fail;
+        }
+
+        // Query the runtime Vulkan API version requirements
+        XrGraphicsRequirementsVulkan2KHR graphicsRequirements{ XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR };
+        PFN_xrGetVulkanGraphicsRequirementsKHR pfnGetVulkanGraphicsRequirementsKHR = nullptr;
+        result = xrGetInstanceProcAddr(m_xrInstance, "xrGetVulkanGraphicsRequirementsKHR",
+                                       reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetVulkanGraphicsRequirementsKHR));
+        WARN_IF_UNSUCCESSFUL(result);
+
+        XrGraphicsRequirementsVulkanKHR legacyRequirements{ XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR };
+        result = pfnGetVulkanGraphicsRequirementsKHR(m_xrInstance, m_xrSystemId, &legacyRequirements);
+        WARN_IF_UNSUCCESSFUL(result);
+        graphicsRequirements.maxApiVersionSupported = legacyRequirements.maxApiVersionSupported;
+        graphicsRequirements.minApiVersionSupported = legacyRequirements.minApiVersionSupported;
+
+        if (validationMode == AZ::RHI::ValidationMode::Enabled)
+        {
+            AZ_Printf("OpenXrVk", "graphicsRequirements.maxApiVersionSupported %d.%d.%d\n",
+            XR_VERSION_MAJOR(graphicsRequirements.maxApiVersionSupported),
+            XR_VERSION_MINOR(graphicsRequirements.maxApiVersionSupported),
+            XR_VERSION_PATCH(graphicsRequirements.maxApiVersionSupported));
+
+            AZ_Printf("OpenXrVk", "graphicsRequirements.minApiVersionSupported %d.%d.%d\n",
+            XR_VERSION_MAJOR(graphicsRequirements.minApiVersionSupported),
+            XR_VERSION_MINOR(graphicsRequirements.minApiVersionSupported),
+            XR_VERSION_PATCH(graphicsRequirements.minApiVersionSupported));
+
+            AZ_Printf("OpenXrVk", "Using system %d for form factor %s\n", m_xrSystemId, to_string(m_formFactor));
+            LogViewConfigurations();
+        }
+		
+        return AZ::RHI::ResultCode::Success;
+    }
+
+    void Instance::LogViewConfigurations()
+    {
+        AZ::u32 viewConfigTypeCount = 0;
+        XrResult result = xrEnumerateViewConfigurations(m_xrInstance, m_xrSystemId, 0, &viewConfigTypeCount, nullptr);
+        RETURN_IF_UNSUCCESSFUL(result);
+
+        AZStd::vector<XrViewConfigurationType> viewConfigTypes(viewConfigTypeCount);
+        result = xrEnumerateViewConfigurations(m_xrInstance, m_xrSystemId, viewConfigTypeCount, &viewConfigTypeCount, viewConfigTypes.data());
+        RETURN_IF_UNSUCCESSFUL(result);
+
+        AZ_Warning("OpenXrVk", aznumeric_cast<AZ::u32>(viewConfigTypes.size()) == viewConfigTypeCount, "Size Mismatch");
+
+        AZ_Printf("OpenXrVk", "Available View Configuration Types: (%d)\n", viewConfigTypeCount);
+        for (XrViewConfigurationType viewConfigType : viewConfigTypes)
+        {
+            AZ_Printf("OpenXrVk", "View Configuration Type: %s %s\n", to_string(viewConfigType),
+            viewConfigType == m_viewConfigType ? "(Selected)" : "");
+
+            XrViewConfigurationProperties viewConfigProperties{ XR_TYPE_VIEW_CONFIGURATION_PROPERTIES };
+            result = xrGetViewConfigurationProperties(m_xrInstance, m_xrSystemId, viewConfigType, &viewConfigProperties);
+            RETURN_IF_UNSUCCESSFUL(result);
+
+            AZ_Printf("OpenXrVk", "View configuration FovMutable=%s\n", viewConfigProperties.fovMutable == XR_TRUE ? "True" : "False");
+
+            AZ::u32 viewCount = 0;
+            result = xrEnumerateViewConfigurationViews(m_xrInstance, m_xrSystemId, viewConfigType, 0, &viewCount, nullptr);
+            RETURN_IF_UNSUCCESSFUL(result);
+            if (viewCount > 0)
+            {
+                AZStd::vector<XrViewConfigurationView> views(viewCount);
+                for (XrViewConfigurationView& view : views)
+                {
+                    view.type = XR_TYPE_VIEW_CONFIGURATION_VIEW;
+                }
+                result = xrEnumerateViewConfigurationViews(m_xrInstance, m_xrSystemId, viewConfigType, viewCount, &viewCount, views.data());
+                RETURN_IF_UNSUCCESSFUL(result);
+
+                for (uint32_t i = 0; i < views.size(); i++)
+                {
+                    const XrViewConfigurationView& view = views[i];
+                    AZ_Printf(
+                        "OpenXrVk", "View [%d]: Recommended Width=%d Height=%d SampleCount=%d\n", i, view.recommendedImageRectWidth,
+                        view.recommendedImageRectHeight, view.recommendedSwapchainSampleCount);
+                    AZ_Printf(
+                        "OpenXrVk", "View [%d]:     Maximum Width=%d Height=%d SampleCount=%d\n", i, view.maxImageRectWidth,
+                        view.maxImageRectHeight, view.maxSwapchainSampleCount);
+                }
+            }
+            else
+            {
+                AZ_Printf("OpenXrVk", "Empty view configuration type\n");
+            }
+
+            LogEnvironmentBlendMode(viewConfigType);
+        }
+    }
+
+    void Instance::LogEnvironmentBlendMode(XrViewConfigurationType type)
+    {
+        AZ::u32 count = 0;
+        XrResult result = xrEnumerateEnvironmentBlendModes(m_xrInstance, m_xrSystemId, type, 0, &count, nullptr);
+        AZ_Warning("OpenXrVk", count > 0, "BlendModes not supported");
+        RETURN_IF_UNSUCCESSFUL(result);
+
+        AZ_Printf("OpenXrVk", "Available Environment Blend Mode count : (%d)\n", count);
+
+        AZStd::vector<XrEnvironmentBlendMode> blendModes(count);
+        result = xrEnumerateEnvironmentBlendModes(m_xrInstance, m_xrSystemId, type, count, &count, blendModes.data());
+        RETURN_IF_UNSUCCESSFUL(result);
+
+        bool blendModeFound = false;
+        for (XrEnvironmentBlendMode mode : blendModes)
+        {
+            const bool blendModeMatch = (mode == m_environmentBlendMode);
+            AZ_Printf("OpenXrVk", "Environment Blend Mode (%s) : %s\n", to_string(mode), blendModeMatch ? "(Selected)" : "");
+            blendModeFound |= blendModeMatch;
+        }
+    }
+
+    AZ::RHI::ResultCode Instance::InitNativeInstance(AZ::RHI::XRInstanceDescriptor* instanceDescriptor)
+    {
+        m_functionLoader = FunctionLoader::Create();
+        if (!m_functionLoader->Init())
+        {
+            AZ_Warning("Vulkan", false, "OpenXRVk Could not initialized function loader.");
+            return AZ::RHI::ResultCode::Fail;
+        }
+
+        AZ::Vulkan::XRInstanceDescriptor* xrInstanceDescriptor = static_cast<AZ::Vulkan::XRInstanceDescriptor*>(instanceDescriptor);
+        XrVulkanInstanceCreateInfoKHR createInfo{ XR_TYPE_VULKAN_INSTANCE_CREATE_INFO_KHR };
+        createInfo.systemId = m_xrSystemId;
+        createInfo.pfnGetInstanceProcAddr = vkGetInstanceProcAddr;
+        createInfo.vulkanCreateInfo = xrInstanceDescriptor->m_inputData.m_createInfo;
+        createInfo.vulkanAllocator = nullptr;
+
+        PFN_xrGetVulkanInstanceExtensionsKHR pfnGetVulkanInstanceExtensionsKHR = nullptr;
+        XrResult result = xrGetInstanceProcAddr(m_xrInstance, "xrGetVulkanInstanceExtensionsKHR", reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetVulkanInstanceExtensionsKHR));
+        ASSERT_IF_UNSUCCESSFUL(result);
+
+        AZ::u32 extensionNamesSize = 0;
+        result = pfnGetVulkanInstanceExtensionsKHR(m_xrInstance, m_xrSystemId, 0, &extensionNamesSize, nullptr);
+        ASSERT_IF_UNSUCCESSFUL(result);
+
+        AZStd::vector<char> extensionNames(extensionNamesSize);
+        result = pfnGetVulkanInstanceExtensionsKHR(m_xrInstance, m_xrSystemId, extensionNamesSize, &extensionNamesSize, &extensionNames[0]);
+        ASSERT_IF_UNSUCCESSFUL(result);
+		
+        AZStd::vector<const char*> extensions = ParseExtensionString(&extensionNames[0]);
+        for (uint32_t i = 0; i < createInfo.vulkanCreateInfo->enabledExtensionCount; ++i)
+        {
+            extensions.push_back(createInfo.vulkanCreateInfo->ppEnabledExtensionNames[i]);
+        }
+
+        VkInstanceCreateInfo instInfo{ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
+        memcpy(&instInfo, createInfo.vulkanCreateInfo, sizeof(instInfo));
+        instInfo.enabledExtensionCount = aznumeric_cast<AZ::u32>(extensions.size());
+        instInfo.ppEnabledExtensionNames = extensions.empty() ? nullptr : extensions.data();
+
+        auto pfnCreateInstance = (PFN_vkCreateInstance)createInfo.pfnGetInstanceProcAddr(nullptr, "vkCreateInstance");
+        VkResult vkResult = pfnCreateInstance(&instInfo, nullptr, &m_xrVkInstance);
+        if (vkResult != VK_SUCCESS)
+        {
+            return AZ::RHI::ResultCode::Fail;
+        }
+		
+        //Populate the instance descriptor with the correct VkInstance
+        xrInstanceDescriptor->m_outputData.m_xrVkInstance = m_xrVkInstance;
+
+        //Get the list of Physical devices
+        m_supportedXRDevices = PhysicalDevice::EnumerateDeviceList(m_xrSystemId, m_xrInstance, m_xrVkInstance);
+        if (m_supportedXRDevices.size() > 1)
+        {
+            //Just use the first device at the moment.
+            m_physicalDeviceActiveIndex = 0;
+        }
+        return AZ::RHI::ResultCode::Success;
+    }
+
+    void Instance::ShutdownInternal()
+    {
+        if (m_xrVkInstance != VK_NULL_HANDLE)
+        {
+            m_supportedXRDevices.clear();
+
+            vkDestroyInstance(m_xrVkInstance, nullptr);
+            m_functionLoader = VK_NULL_HANDLE;
+        }
+
+        if (m_functionLoader)
+        {
+            m_functionLoader->Shutdown();
+        }
+        m_functionLoader = nullptr;
+    }
+
+    AZ::RHI::ResultCode Instance::GetXRPhysicalDevice(AZ::RHI::XRPhysicalDeviceDescriptor* physicalDeviceDescriptor, int32_t index)
+    {
+        AZ::Vulkan::XRPhysicalDeviceDescriptor* xrPhysicalDeviceDescriptor = static_cast<AZ::Vulkan::XRPhysicalDeviceDescriptor*>(physicalDeviceDescriptor);
+        if (xrPhysicalDeviceDescriptor && (index < m_supportedXRDevices.size()))
+        {
+            xrPhysicalDeviceDescriptor->m_outputData.m_xrVkPhysicalDevice = m_supportedXRDevices[index];
+            return AZ::RHI::ResultCode::Success;
+        }
+        return AZ::RHI::ResultCode::Fail;
+    }
+
+    AZ::u32 Instance::GetNumPhysicalDevices() const
+    {
+        return aznumeric_cast<AZ::u32>(m_supportedXRDevices.size());
+    }
+
+    VkPhysicalDevice Instance::GetActivePhysicalDevice() const
+    {
+        AZ_Assert(m_physicalDeviceActiveIndex < m_supportedXRDevices.size(), "Index out of range");
+        return m_supportedXRDevices[m_physicalDeviceActiveIndex];
+    }
+
+    XrInstance Instance::GetXRInstance() const
+    {
+        return m_xrInstance;
+    }
+
+    XrSystemId Instance::GetXRSystemId() const
+    {
+        return m_xrSystemId;
+    }
+
+    VkInstance Instance::GetNativeInstance() const
+    {
+        return m_xrVkInstance;
+    }
+
+    XrEnvironmentBlendMode Instance::GetEnvironmentBlendMode() const
+    {
+        return m_environmentBlendMode;
+    }
+
+    XrViewConfigurationType Instance::GetViewConfigType() const
+    {
+        return m_viewConfigType;
+    }
+}

+ 45 - 0
Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp

@@ -0,0 +1,45 @@
+/*
+ * 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 <AzCore/Memory/SystemAllocator.h>
+#include <AzCore/Module/Module.h>
+#include <OpenXrVk/OpenXrVkSystemComponent.h>
+
+namespace OpenXRVk
+{   
+    //! This module is in charge of loading system components related to Openxrvk. 
+    class Module
+        : public AZ::Module
+    {
+    public:
+        AZ_RTTI(Module, "{C34AA64E-0983-4D30-A33C-0D7C7676A20E}", AZ::Module);
+        AZ_CLASS_ALLOCATOR(Module, AZ::SystemAllocator, 0);
+
+        Module()
+            : AZ::Module()
+        {
+            m_descriptors.insert(m_descriptors.end(), {
+                    SystemComponent::CreateDescriptor(),
+            });
+        }
+
+        AZ::ComponentTypeList GetRequiredSystemComponents() const override
+        {
+            return
+            {
+                azrtti_typeid<OpenXRVk::SystemComponent>()
+            };
+        }
+    };
+}
+
+
+// DO NOT MODIFY THIS LINE UNLESS YOU RENAME THE GEM
+// The first parameter should be GemName_GemIdLower
+// The second should be the fully qualified name of the class above
+AZ_DECLARE_MODULE_CLASS(Gem_OpenXRVk, OpenXRVk::Module)

+ 34 - 0
Gems/OpenXRVk/Code/Source/OpenXRVkPhysicalDevice.cpp

@@ -0,0 +1,34 @@
+/*
+ * 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/OpenXRVkPhysicalDevice.h>
+#include <OpenXRVk/OpenXRVkUtils.h>
+
+namespace OpenXRVk
+{
+    AZStd::vector<VkPhysicalDevice> PhysicalDevice::EnumerateDeviceList(XrSystemId xrSystemId, XrInstance xrInstance, VkInstance vkInstance)
+    {
+        AZStd::vector<VkPhysicalDevice> physicalDevices;
+		
+        XrVulkanGraphicsDeviceGetInfoKHR deviceGetInfo{ XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR };
+        deviceGetInfo.systemId = xrSystemId;
+        deviceGetInfo.vulkanInstance = vkInstance;
+        VkPhysicalDevice vkPhysicalDevice = VK_NULL_HANDLE;
+
+        PFN_xrGetVulkanGraphicsDeviceKHR pfnGetVulkanGraphicsDeviceKHR = nullptr;
+        XrResult result = xrGetInstanceProcAddr(
+	        xrInstance, "xrGetVulkanGraphicsDeviceKHR", reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetVulkanGraphicsDeviceKHR));
+        ASSERT_IF_UNSUCCESSFUL(result);
+
+        //TODO::Look into api that can retreive multiple physicall devices if connected
+        result = pfnGetVulkanGraphicsDeviceKHR(xrInstance, xrSystemId, vkInstance, &vkPhysicalDevice);
+        ASSERT_IF_UNSUCCESSFUL(result);
+        physicalDevices.push_back(vkPhysicalDevice);
+        return physicalDevices;
+    }
+}

+ 330 - 0
Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp

@@ -0,0 +1,330 @@
+/*
+ * 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/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
+{
+    XR::Ptr<Session> Session::Create()
+    {
+        return aznew Session;
+    }
+
+    AZ::RHI::ResultCode Session::InitInternal(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());
+
+        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()
+    {
+        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::LogActionSourceName(XrAction action, const AZStd::string_view actionName) const
+    {
+        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"));
+    }
+
+    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 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);
+        }
+    }
+}

+ 155 - 0
Gems/OpenXRVk/Code/Source/OpenXRVkSpace.cpp

@@ -0,0 +1,155 @@
+/*
+ * 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/OpenXRVkSpace.h>
+#include <OpenXRVk/OpenXRVkUtils.h>
+#include <AzCore/Debug/Trace.h>
+
+namespace OpenXRVk
+{
+    XR::Ptr<Space> Space::Create()
+    {
+        return aznew Space;
+    }
+
+    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();
+    }
+}

+ 276 - 0
Gems/OpenXRVk/Code/Source/OpenXRVkSwapChain.cpp

@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <Atom/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
+{
+
+    XR::Ptr<SwapChain> SwapChain::Create()
+    {
+        return aznew SwapChain;
+    }
+
+    XR::Ptr<SwapChain::Image> SwapChain::Image::Create()
+    {
+        return aznew SwapChain::Image;
+    }
+
+    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()
+    {
+        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)
+        {
+            // 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();
+        }
+    }
+}

+ 92 - 0
Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp

@@ -0,0 +1,92 @@
+/*
+ * 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 <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
+{
+    void SystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
+    {
+        provided.push_back(XR::Factory::GetPlatformService());
+    }
+
+    void SystemComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<SystemComponent, AZ::Component>()
+                ->Version(1);
+        }
+    }
+
+    SystemComponent::SystemComponent()
+    {
+        // Only have Vulkan back-end implementation for XR at the moment so register it. 
+        XR::Factory::Register(this);
+    }
+
+    SystemComponent::~SystemComponent()
+    {
+        XR::Factory::Unregister(this);
+    }
+
+    XR::Ptr<XR::Instance> SystemComponent::CreateInstance()
+    {
+        return Instance::Create();
+    }
+
+    XR::Ptr<XR::Device> SystemComponent::CreateDevice()
+    {
+        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()
+    {
+    }
+
+    void SystemComponent::Deactivate()
+    {
+    }
+}

+ 77 - 0
Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp

@@ -0,0 +1,77 @@
+/*
+ * 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/OpenXRVkUtils.h>
+#include <AzCore/Debug/Trace.h>
+
+namespace OpenXRVk
+{
+    AZ::RHI::ResultCode ConvertResult(XrResult xrResult)
+    {
+        switch (xrResult)
+        {
+        case XR_SUCCESS:
+            return AZ::RHI::ResultCode::Success;
+        case XR_ERROR_OUT_OF_MEMORY:
+            return AZ::RHI::ResultCode::OutOfMemory;
+        default:
+            return AZ::RHI::ResultCode::Fail;
+        }
+    }
+
+    bool IsSuccess(XrResult result)
+    {
+        if (result != XR_SUCCESS)
+        {
+            AZ_Error("XR", false, "ERROR: XR API method failed: %s", GetResultString(result));
+            return false;
+        }
+        return true;
+    }
+
+    bool IsError(XrResult result)
+    {
+        return IsSuccess(result) == false;
+    }
+
+    const char* GetResultString(const XrResult result)
+    {
+        return to_string(result);
+    }
+
+    XR::RawStringList FilterList(const XR::RawStringList& source, const XR::StringList& filter)
+    {
+        XR::RawStringList filteredList;
+        for (auto& item : source)
+        {
+            if (AZStd::find(filter.begin(), filter.end(), item) != filter.end())
+            {
+                filteredList.push_back(item);
+            }
+        }
+        return filteredList;
+    }
+
+    AZStd::vector<const char*> ParseExtensionString(char* names)
+    {
+        AZStd::vector<const char*> list;
+        while (*names != 0)
+        {
+            list.push_back(names);
+            while (*(++names) != 0)
+            {
+                if (*names == ' ')
+                {
+                    *names++ = '\0';
+                    break;
+                }
+            }
+        }
+        return list;
+    }
+}

+ 11 - 0
Gems/OpenXRVk/Code/Source/Platform/Android/OpenXRVk_Traits_Android.h

@@ -0,0 +1,11 @@
+/*
+ * 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
+
+#define VULKAN_DLL "vulkan.dll"
+#define VULKAN_1_DLL "vulkan-1.dll"

+ 10 - 0
Gems/OpenXRVk/Code/Source/Platform/Android/OpenXRVk_Traits_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * 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 <OpenXRVk_Traits_Android.h>

+ 9 - 0
Gems/OpenXRVk/Code/Source/Platform/Android/PAL_android.cmake

@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_OPENXRVK_SUPPORTED TRUE)

+ 12 - 0
Gems/OpenXRVk/Code/Source/Platform/Android/platform_private_android_files.cmake

@@ -0,0 +1,12 @@
+#
+# 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
+#
+#
+
+set(FILES
+    OpenXRVk_Traits_Android.h
+    OpenXRVk_Traits_Platform.h
+)

+ 8 - 0
Gems/OpenXRVk/Code/Source/Platform/Android/platform_private_static_android.cmake

@@ -0,0 +1,8 @@
+#
+# 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
+#
+#
+

+ 9 - 0
Gems/OpenXRVk/Code/Source/Platform/Common/Unimplemented/Empty_Unimplemented.cpp

@@ -0,0 +1,9 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+// This is an intentionally empty file used to compile on platforms that cannot support artifacts without at least one source file

+ 31 - 0
Gems/OpenXRVk/Code/Source/Platform/Common/Unimplemented/ModuleStub_Unimplemented.cpp

@@ -0,0 +1,31 @@
+/*
+ * 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 <AzCore/Module/Module.h>
+
+namespace AZ
+{
+    namespace OpenXRVk
+    {
+        class PlatformModule
+            : public AZ::Module
+        {
+        public:
+            AZ_RTTI(PlatformModule, "{958CB096-796C-42C7-9B29-17C6FE792C30}", Module);
+
+            PlatformModule() = default;
+            ~PlatformModule() override = default;
+
+            AZ::ComponentTypeList GetRequiredSystemComponents() const override
+            {
+                return AZ::ComponentTypeList();
+            }
+        };
+    }
+}
+
+AZ_DECLARE_MODULE_CLASS(Gem_OpenXRVk_Private, AZ::OpenXRVk::PlatformModule)

+ 11 - 0
Gems/OpenXRVk/Code/Source/Platform/Linux/OpenXRVk_Traits_Linux.h

@@ -0,0 +1,11 @@
+/*
+ * 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
+
+#define VULKAN_DLL "vulkan.dll"
+#define VULKAN_1_DLL "vulkan-1.dll"

+ 10 - 0
Gems/OpenXRVk/Code/Source/Platform/Linux/OpenXRVk_Traits_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * 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 <OpenXRVk_Traits_Linux.h>

+ 9 - 0
Gems/OpenXRVk/Code/Source/Platform/Linux/PAL_linux.cmake

@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_OPENXRVK_SUPPORTED TRUE)

+ 12 - 0
Gems/OpenXRVk/Code/Source/Platform/Linux/platform_private_linux_files.cmake

@@ -0,0 +1,12 @@
+#
+# 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
+#
+#
+
+set(FILES
+    OpenXRVk_Traits_Linux.h
+    OpenXRVk_Traits_Platform.h
+)

+ 8 - 0
Gems/OpenXRVk/Code/Source/Platform/Linux/platform_private_static_linux.cmake

@@ -0,0 +1,8 @@
+#
+# 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
+#
+#
+

+ 9 - 0
Gems/OpenXRVk/Code/Source/Platform/Mac/PAL_mac.cmake

@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_OPENXRVK_SUPPORTED FALSE)

+ 10 - 0
Gems/OpenXRVk/Code/Source/Platform/Windows/OpenXRVk_Traits_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * 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 <OpenXRVk_Traits_Windows.h>

+ 11 - 0
Gems/OpenXRVk/Code/Source/Platform/Windows/OpenXRVk_Traits_Windows.h

@@ -0,0 +1,11 @@
+/*
+ * 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
+
+#define VULKAN_DLL "vulkan.dll"
+#define VULKAN_1_DLL "vulkan-1.dll"

+ 10 - 0
Gems/OpenXRVk/Code/Source/Platform/Windows/PAL_windows.cmake

@@ -0,0 +1,10 @@
+#
+# 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
+#
+#
+
+set(PAL_TRAIT_OPENXRVK_SUPPORTED TRUE)
+

+ 8 - 0
Gems/OpenXRVk/Code/Source/Platform/Windows/platform_private_static_windows.cmake

@@ -0,0 +1,8 @@
+#
+# 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
+#
+#
+

+ 12 - 0
Gems/OpenXRVk/Code/Source/Platform/Windows/platform_private_windows_files.cmake

@@ -0,0 +1,12 @@
+#
+# 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
+#
+#
+
+set(FILES
+    OpenXRVk_Traits_Windows.h
+    OpenXRVk_Traits_Platform.h
+)

+ 9 - 0
Gems/OpenXRVk/Code/Source/Platform/iOS/PAL_ios.cmake

@@ -0,0 +1,9 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+
+set(PAL_TRAIT_OPENXRVK_SUPPORTED FALSE)

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

@@ -0,0 +1,39 @@
+#
+# 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
+#
+#
+
+set(FILES
+    Include/OpenXRVk/OpenXRVkDevice.h
+    Include/OpenXRVk/OpenXRVkInput.h
+    Include/OpenXRVk/OpenXRVkInstance.h
+    Include/OpenXRVk/OpenXRVkPhysicalDevice.h
+    Include/OpenXRVk/OpenXRVkSession.h
+    Include/OpenXRVk/OpenXRVkSpace.h
+    Include/OpenXRVk/OpenXRVkSwapChain.h
+    Include/OpenXRVk/OpenXRVkSystemComponent.h
+    Include/OpenXRVk/OpenXRVkUtils.h
+    Include/OpenXRVk/OpenXRVkFunctionLoader.h
+    Include/OpenXRVk/OpenXRVkGladFunctionLoader.h
+    Source/OpenXRVkDevice.cpp
+    Source/OpenXRVkInput.cpp
+    Source/OpenXRVkInstance.cpp
+    Source/OpenXRVkPhysicalDevice.cpp
+    Source/OpenXRVkSession.cpp
+    Source/OpenXRVkSpace.cpp
+    Source/OpenXRVkSwapChain.cpp
+    Source/OpenXRVkSystemComponent.cpp
+    Source/OpenXRVkUtils.cpp
+    Source/OpenXRVkFunctionLoader.cpp
+    Source/OpenXRVkGladFunctionLoader.cpp
+)
+
+set(SKIP_UNITY_BUILD_INCLUSION_FILES
+    # The following file defines GLAD_VULKAN_IMPLEMENTATION before including vulkan.h changing
+    # the behavior inside vulkan.h. Other files also includes vulkan.h so this file cannot
+    # be added to unity, other files could end up including vulkan.h and making this one fail.
+    Source/OpenXRVkGladFunctionLoader.cpp
+)

+ 11 - 0
Gems/OpenXRVk/Code/openxrvk_private_common_shared_files.cmake

@@ -0,0 +1,11 @@
+#
+# 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
+#
+#
+
+set(FILES
+    Source/OpenXRVkModule.cpp
+)

+ 11 - 0
Gems/OpenXRVk/Code/openxrvk_stub_module.cmake

@@ -0,0 +1,11 @@
+#
+# 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
+#
+#
+
+set(FILES
+    Source/Platform/Common/Unimplemented/ModuleStub_Unimplemented.cpp
+)

+ 19 - 0
Gems/OpenXRVk/gem.json

@@ -0,0 +1,19 @@
+{
+    "gem_name": "OpenXRVk",
+    "display_name": "OpenXR Vulkan",
+    "license": "Apache-2.0 Or MIT",
+    "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt",
+    "origin": "Open 3D Engine - o3de.org",
+    "origin_url": "https://github.com/o3de/o3de",
+    "type": "Code",
+    "summary": "OpenXR Vulcan for Atom",
+    "canonical_tags": [
+        "Gem"
+    ],
+    "user_tags": [],
+    "requirements": "",
+    "documentation_url": "",
+    "dependencies": [
+        "Atom_RPI"
+    ]
+}

BIN
Gems/OpenXRVk/preview.png