Procházet zdrojové kódy

Merge pull request #3 from o3de/main

merge main back into dev because xr was commited to main directly
AMZN-byrcolin před 3 roky
rodič
revize
f0f4120862
100 změnil soubory, kde provedl 4400 přidání a 0 odebrání
  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. 3 0
      Gems/OpenXRVk/preview.png
  70. 11 0
      Gems/README.md
  71. 11 0
      Gems/XR/3rdParty/Platform/Android/BuiltInPackages_android.cmake
  72. 11 0
      Gems/XR/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake
  73. 10 0
      Gems/XR/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake
  74. 10 0
      Gems/XR/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake
  75. 10 0
      Gems/XR/3rdParty/Platform/iOS/BuiltInPackages_ios.cmake
  76. 17 0
      Gems/XR/CMakeLists.txt
  77. 77 0
      Gems/XR/Code/CMakeLists.txt
  78. 13 0
      Gems/XR/Code/Include/XR/Platform/Android/XR_Android.h
  79. 9 0
      Gems/XR/Code/Include/XR/Platform/Android/XR_Platform.h
  80. 12 0
      Gems/XR/Code/Include/XR/Platform/Android/platform_private_android_files.cmake
  81. 14 0
      Gems/XR/Code/Include/XR/Platform/Linux/XR_Linux.h
  82. 10 0
      Gems/XR/Code/Include/XR/Platform/Linux/XR_Platform.h
  83. 11 0
      Gems/XR/Code/Include/XR/Platform/Linux/platform_private_linux_files.cmake
  84. 13 0
      Gems/XR/Code/Include/XR/Platform/Mac/XR_Mac.h
  85. 9 0
      Gems/XR/Code/Include/XR/Platform/Mac/XR_Platform.h
  86. 10 0
      Gems/XR/Code/Include/XR/Platform/Mac/platform_private_mac_files.cmake
  87. 9 0
      Gems/XR/Code/Include/XR/Platform/Windows/XR_Platform.h
  88. 12 0
      Gems/XR/Code/Include/XR/Platform/Windows/XR_Windows.h
  89. 12 0
      Gems/XR/Code/Include/XR/Platform/Windows/platform_private_windows_files.cmake
  90. 10 0
      Gems/XR/Code/Include/XR/Platform/iOS/XR_Platform.h
  91. 10 0
      Gems/XR/Code/Include/XR/Platform/iOS/platform_private_ios_files.cmake
  92. 38 0
      Gems/XR/Code/Include/XR/XRBase.h
  93. 100 0
      Gems/XR/Code/Include/XR/XRDevice.h
  94. 77 0
      Gems/XR/Code/Include/XR/XRFactory.h
  95. 55 0
      Gems/XR/Code/Include/XR/XRInput.h
  96. 59 0
      Gems/XR/Code/Include/XR/XRInstance.h
  97. 77 0
      Gems/XR/Code/Include/XR/XRObject.h
  98. 98 0
      Gems/XR/Code/Include/XR/XRSession.h
  99. 46 0
      Gems/XR/Code/Include/XR/XRSpace.h
  100. 129 0
      Gems/XR/Code/Include/XR/XRSwapChain.h

+ 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"
+    ]
+}

+ 3 - 0
Gems/OpenXRVk/preview.png

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

+ 11 - 0
Gems/README.md

@@ -0,0 +1,11 @@
+# O3DE-Extras-Gems
+
+This folder contains extra Gems for O3DE. See the readme at the root of this repo for instructions.
+
+## Contribute
+
+For information about contributing to Open 3D Engine, visit [https://o3de.org/docs/contributing/](https://o3de.org/docs/contributing/).
+
+## License
+
+For terms please see the LICENSE*.TXT files at the root of this distribution.

+ 11 - 0
Gems/XR/3rdParty/Platform/Android/BuiltInPackages_android.cmake

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

+ 11 - 0
Gems/XR/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake

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

+ 10 - 0
Gems/XR/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.
+#

+ 10 - 0
Gems/XR/3rdParty/Platform/Windows/BuiltInPackages_windows.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.
+#

+ 10 - 0
Gems/XR/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/XR/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)

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

@@ -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
+#
+#
+
+o3de_pal_dir(pal_include_dir ${CMAKE_CURRENT_LIST_DIR}/Include/XR/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_XR_TARGETS_ALREADY_DEFINED)
+    return() # XR targets already defined in PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake
+endif()
+
+if(NOT PAL_TRAIT_XR_SUPPORTED)
+
+    # Create stub modules. Once we support gem loading configuration, we can remove this stubbed targets
+    ly_add_target(
+        NAME XR ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
+        NAMESPACE Gem
+        FILES_CMAKE
+            xr_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 XR.Static STATIC
+    NAMESPACE Gem
+    FILES_CMAKE
+        xr_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
+            AZ::AtomCore
+            Gem::Atom_RPI.Public
+)
+
+ly_add_target(
+    NAME XR ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
+    NAMESPACE Gem
+    FILES_CMAKE
+        xr_private_common_shared_files.cmake
+    INCLUDE_DIRECTORIES
+        PRIVATE
+            Source
+            ${pal_source_dir}
+        PUBLIC
+            Include
+    BUILD_DEPENDENCIES
+        PRIVATE
+            Gem::XR.Static
+)
+

+ 13 - 0
Gems/XR/Code/Include/XR/Platform/Android/XR_Android.h

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

+ 9 - 0
Gems/XR/Code/Include/XR/Platform/Android/XR_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 <XR_Android.h>

+ 12 - 0
Gems/XR/Code/Include/XR/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
+    XR_Platform.h
+    XR_Android.h
+)

+ 14 - 0
Gems/XR/Code/Include/XR/Platform/Linux/XR_Linux.h

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

+ 10 - 0
Gems/XR/Code/Include/XR/Platform/Linux/XR_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 <XR_Linux.h>

+ 11 - 0
Gems/XR/Code/Include/XR/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
+    XR_Platform.h
+)

+ 13 - 0
Gems/XR/Code/Include/XR/Platform/Mac/XR_Mac.h

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

+ 9 - 0
Gems/XR/Code/Include/XR/Platform/Mac/XR_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
+

+ 10 - 0
Gems/XR/Code/Include/XR/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/XR/Code/Include/XR/Platform/Windows/XR_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 <XR_Windows.h>

+ 12 - 0
Gems/XR/Code/Include/XR/Platform/Windows/XR_Windows.h

@@ -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
+ *
+ */
+#pragma once
+
+#include <AzCore/base.h>
+#include <AzCore/PlatformIncl.h>
+#include <AzCore/std/algorithm.h>

+ 12 - 0
Gems/XR/Code/Include/XR/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
+    XR_Platform.h
+    XR_Windows.h
+)

+ 10 - 0
Gems/XR/Code/Include/XR/Platform/iOS/XR_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
+
+

+ 10 - 0
Gems/XR/Code/Include/XR/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
+)

+ 38 - 0
Gems/XR/Code/Include/XR/XRBase.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/std/smart_ptr/intrusive_ptr.h>
+#include <AzCore/std/string/string.h>
+#include <AzCore/std/containers/vector.h>
+
+namespace XR
+{
+    template <typename T>
+    using Ptr = AZStd::intrusive_ptr<T>;
+
+    template<typename T>
+    using ConstPtr = AZStd::intrusive_ptr<const T>;
+
+    using StringList = AZStd::vector<AZStd::string>;
+    using RawStringList = AZStd::vector<const char*>;
+    
+    enum class  Side : uint32_t
+    {
+        Left = 0,
+        Right,
+        Count
+    }; 
+
+#define RETURN_RESULTCODE_IF_UNSUCCESSFUL(result) \
+    if (result != AZ::RHI::ResultCode::Success) {\
+        return result;\
+    }
+}
+

+ 100 - 0
Gems/XR/Code/Include/XR/XRDevice.h

@@ -0,0 +1,100 @@
+/*
+ * 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 <Atom/RHI/XRRenderingInterface.h>
+#include <Atom/RPI.Public/XR/XRRenderingInterface.h>
+#include <XR/XRBase.h>
+#include <XR/XRInstance.h>
+#include <XR/XRSwapChain.h>
+#include <XR/XRObject.h>
+
+namespace XR
+{
+    //! Base XR device class which will provide access to the back-end concrete object
+    class Session;
+    class Device
+        : public XR::Object
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(Device, AZ::SystemAllocator, 0);
+        AZ_RTTI(Device, "{A31B0DC2-BD54-443E-9350-EB1B10670FF9}");
+
+        Device() = default;
+        virtual ~Device() = default;
+
+        struct Descriptor
+        {
+            AZ::RHI::ValidationMode m_validationMode = AZ::RHI::ValidationMode::Disabled;
+            Ptr<Instance> m_instance;
+        };
+
+        //! Create the xr specific native device object and populate the XRDeviceDescriptor with it.
+        virtual AZ::RHI::ResultCode InitDeviceInternal(AZ::RHI::XRDeviceDescriptor* instanceDescriptor) = 0;
+        
+        //! Returns true if rendering data is valid for the current frame.
+        virtual bool ShouldRender() const = 0;
+        
+        //! Returns fov data for a give view index.
+        virtual AZ::RPI::FovData GetViewFov(AZ::u32 viewIndex) const = 0;
+
+        //! Returns pose data for a give view index.
+        virtual AZ::RPI::PoseData GetViewPose(AZ::u32 viewIndex) const = 0;
+
+        //! Init the XR device.
+        AZ::RHI::ResultCode Init(Descriptor descriptor);
+        
+        //! Signal Begin frame to the underlying back end.
+        bool BeginFrame();
+
+        //! Signal End frame to the underlying back end.
+        void EndFrame(Ptr<SwapChain>);
+
+        //! Signal the back-end to acquire swapchain images.
+        bool AcquireSwapChainImage(AZ::u32 viewIndex, SwapChain* swapChain);
+
+        //! Register XR session with the device.
+        void RegisterSession(Ptr<Session> session);
+    
+        //! UnRegister XR session with the device.
+        void UnRegisterSession();
+
+        //! Get the descriptor.
+        const Descriptor& GetDescriptor() const;
+
+        //! Get the xr session registered with the device
+        Ptr<Session> GetSession() const;
+
+    protected:
+    
+        //! Called when the device is being shutdown.
+        virtual void ShutdownInternal() = 0;
+
+        //! Called when the device is beginning a frame for processing.
+        virtual bool BeginFrameInternal() = 0;
+
+        //! Called when the device is ending a frame for processing. 
+        //! Pass in the active swapchain in order to allow the back end to release the swap chain images
+        virtual void EndFrameInternal(XR::Ptr<XR::SwapChain>) = 0;
+
+        //! Called when the device is beginning a frame for processing.
+        virtual bool AcquireSwapChainImageInternal(AZ::u32 viewIndex, XR::SwapChain* baseSwapChain) = 0;
+
+    private:
+
+        ///////////////////////////////////////////////////////////////////
+        // XR::Object
+        void Shutdown() override;
+        ///////////////////////////////////////////////////////////////////
+
+        Ptr<Session> m_session;
+        Descriptor m_descriptor;
+    };
+}

+ 77 - 0
Gems/XR/Code/Include/XR/XRFactory.h

@@ -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
+ *
+ */
+
+#pragma once
+
+#include <AzCore/Memory/SystemAllocator.h>
+#include <AzCore/std/containers/vector.h>
+
+#include <XR/XRBase.h>
+#include <XR/XRInstance.h>
+#include <XR/XRDevice.h>
+#include <XR/XRSession.h>
+#include <XR/XRInput.h>
+#include <XR/XRSpace.h>
+#include <XR/XRSwapChain.h>
+
+namespace XR
+{
+    //! Interface responsible for creating all the XR objects which are
+    //! internally backed by concrete objects
+    class Factory
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(Factory, AZ::SystemAllocator, 0);
+        AZ_RTTI(Factory, "{A3D7271A-64FD-442C-9116-DBC32224222F}");
+
+        Factory() = default;
+        virtual ~Factory() = default;
+
+        AZ_DISABLE_COPY_MOVE(Factory);
+
+        //! Returns the component service name CRC used by the platform RHI system component.
+        static AZ::u32 GetPlatformService();
+
+        //! Registers the global factory instance.
+        static void Register(XR::Factory* instance);
+
+        //! Unregisters the global factory instance.
+        static void Unregister(XR::Factory* instance);
+
+        //! Returns if the factory ready.
+        static bool IsReady();
+
+        //! Access the global factory instance.
+        static XR::Factory& Get();
+
+        //! Create XR::Instance object.
+        virtual Ptr<XR::Instance> CreateInstance() = 0;
+
+        //! Create XR::Device object.
+        virtual Ptr<XR::Device> CreateDevice() = 0;
+
+        //! Create XR::Session object.
+        virtual Ptr<XR::Session> CreateSession() = 0;
+
+        //! Create XR::Input object.
+        virtual Ptr<XR::Input> CreateInput() = 0;
+
+        //! Create XR::Space object.
+        virtual Ptr<XR::Space> CreateSpace() = 0;
+
+        //! Create XR::Swapchain object.
+        virtual Ptr<XR::SwapChain> CreateSwapChain() = 0;
+
+        //! Create XR::Swapchain::View object.
+        virtual Ptr<XR::SwapChain::View> CreateSwapChainView() = 0;
+
+        //! Create XR::Swapchain::Image object.
+        virtual Ptr<XR::SwapChain::Image> CreateSwapChainImage() = 0;
+    };
+} // namespace XR
+

+ 55 - 0
Gems/XR/Code/Include/XR/XRInput.h

@@ -0,0 +1,55 @@
+/*
+ * 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 <XR/XRBase.h>
+#include <XR/XRObject.h>
+
+namespace XR
+{ 
+    class Session;
+    class Instance;
+    // This class will be responsible for creating XR::Input
+    // which manage event queue or poll actions
+    class Input
+        : public XR::Object
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(Input, AZ::SystemAllocator, 0);
+        AZ_RTTI(Input, "{DCDFC6A7-B457-414B-BC24-0831C2AC628B}");
+
+        Input() = default;
+        virtual ~Input() = default;
+        
+        struct Descriptor
+        {
+            Ptr<Instance> m_instance;
+            Ptr<Device> m_device;
+            Ptr<Session> m_session;
+        };
+        
+        AZ::RHI::ResultCode Init(Descriptor descriptor);
+        const Descriptor& GetDescriptor() const;
+
+        virtual void PollActions() = 0;
+       
+    private:
+        ///////////////////////////////////////////////////////////////////
+        // XR::Object
+        void Shutdown() override;
+        ///////////////////////////////////////////////////////////////////
+
+        //! Called when the XR instance is being shutdown.
+        virtual void ShutdownInternal() = 0;
+        virtual AZ::RHI::ResultCode InitInternal() = 0;
+
+        Descriptor m_descriptor;
+    };
+} // namespace XR

+ 59 - 0
Gems/XR/Code/Include/XR/XRInstance.h

@@ -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
+ *
+ */
+
+#pragma once
+
+#include <AzCore/Memory/SystemAllocator.h>
+#include <Atom/RHI/ValidationLayer.h>
+#include <Atom/RHI/XRRenderingInterface.h>
+#include <XR/XRBase.h>
+#include <XR/XRObject.h>
+
+namespace XR
+{
+    class Instance
+        : public XR::Object
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(Instance, AZ::SystemAllocator, 0);
+        AZ_RTTI(Instance, "{1C457924-56A4-444F-BC72-4D31A097BA70}");
+
+        Instance() = default;
+        virtual ~Instance() = default;
+
+        //! Init the back-end instance. It is responsible for figuring out supported layers and extensions 
+        //! and based on that a xr instance is created. It also has logging support based on validation mode.  
+        AZ::RHI::ResultCode Init(AZ::RHI::ValidationMode validationMode);
+
+        //! API to init the native instance object and populate the XRInstanceDecriptor with it.
+        virtual AZ::RHI::ResultCode InitNativeInstance(AZ::RHI::XRInstanceDescriptor* instanceDescriptor) = 0;
+
+        //! Get number of physical devices for XR.
+        virtual AZ::u32 GetNumPhysicalDevices() const = 0;
+
+        //! API to retrieve the native physical device for a specific index.
+        virtual AZ::RHI::ResultCode GetXRPhysicalDevice(AZ::RHI::XRPhysicalDeviceDescriptor* physicalDeviceDescriptor, int32_t index) = 0;
+
+    private:
+
+        ///////////////////////////////////////////////////////////////////
+        // XR::Object
+        void Shutdown() override;
+        ///////////////////////////////////////////////////////////////////
+
+        //! Called when the XR instance is being shutdown.
+        virtual void ShutdownInternal() = 0;
+
+        //! API to allow backend object to initialize native xr instance. 
+        virtual AZ::RHI::ResultCode InitInstanceInternal(AZ::RHI::ValidationMode m_validationMode) = 0;
+       
+        //Cache validation mode in case the backend object needs to use it.
+        AZ::RHI::ValidationMode m_validationMode = AZ::RHI::ValidationMode::Disabled;
+    };
+
+} // namespace XR

+ 77 - 0
Gems/XR/Code/Include/XR/XRObject.h

@@ -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
+ *
+ */
+#pragma once
+
+#include <AzCore/std/smart_ptr/intrusive_base.h>
+#include <AzCore/Name/Name.h>
+
+namespace XR
+{
+    //! All objects have an explicit Init / Shutdown path in addition to
+    //! creation / deletion. The user is expected to Init the derived variant
+    //! in order to use it. This extension allows the user to forgo shutdown
+    //! explicitly and depend on the Ptr ref-counting if necessary.
+    //! This requires that Shutdown properly account for being called multiple times.
+    struct ObjectDeleter
+        : public AZStd::intrusive_default_delete
+    {
+        template <typename U>
+        void operator () (U* p) const;
+    };
+
+    //! Base class for any persistent resource. Provides a name, reference
+    //! counting, and a common RTTI base class for all objects .
+    class Object
+        : public AZStd::intrusive_refcount<AZStd::atomic_uint, ObjectDeleter>
+    {
+    public:
+        AZ_RTTI(Object, "{74FCA8BF-CBDA-43EB-A378-89F752ED2FCA}");
+        virtual ~Object() = default;
+
+        //! Sets the name of the object.
+        void SetName(const AZ::Name& name)
+        {
+            m_name = name;
+            SetNameInternal(m_name.GetStringView());
+        }
+
+        //! Returns the name set on the object by SetName
+        const AZ::Name& GetName() const { return m_name; }
+
+    protected:
+        Object() = default;
+
+    private:
+        // Friended to allow private call to Shutdown.
+        friend struct ObjectDeleter;
+
+        //! Shuts down the object. Derived classes can make this public if it fits
+        //! with their lifecycle model (i.e. if they use an explicit Init / Shutdown).
+        //! By default, it is private in order to maintain consistency with a simpler RAII lifecycle.
+        virtual void Shutdown() {};
+
+        virtual void SetNameInternal([[maybe_unused]] const AZStd::string_view& name) {}
+
+        //! Object name. Very helpful for debugging
+        AZ::Name m_name;
+    };
+
+    template <typename U>
+    void ObjectDeleter::operator () (U* p) const
+    {
+        //! Recover the mutable parent object pointer from the refcount base class.
+        Object* object = const_cast<Object*>(static_cast<const Object*>(p));
+
+        //! Shuts down the object before deletion.
+        object->Shutdown();
+
+        //! Then invoke the base delete policy.
+        AZStd::intrusive_default_delete::operator () (object);
+    }
+}
+

+ 98 - 0
Gems/XR/Code/Include/XR/XRSession.h

@@ -0,0 +1,98 @@
+/*
+ * 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 <XR/XRBase.h>
+#include <XR/XRInput.h>
+#include <XR/XRObject.h>
+#include <XR/XRSpace.h>
+
+namespace XR
+{
+    class Device;
+
+    // This class will be responsible for creating XR::Session and
+    // all the code around managing the session state
+    class Session
+        : public XR::Object
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(Session, AZ::SystemAllocator, 0);
+        AZ_RTTI(Session, "{E7276FE1-94B8-423A-9C1D-1BCF1A0066BC}");
+
+        struct Descriptor
+        {
+            AZ::RHI::ValidationMode m_validationMode = AZ::RHI::ValidationMode::Disabled;
+            Ptr<Device> m_device;
+            Ptr<Instance> m_instance;
+        };
+
+        Session() = default;
+        virtual ~Session() = default;
+        
+        //! Initialize the XrSession object which is responsible for creating
+        //! XrInput and XrSpace
+        AZ::RHI::ResultCode Init(const Descriptor& descriptor);
+
+        //! Get the descriptor for the class
+        const Descriptor& GetDescriptor() const;
+
+        //! Get the Xr Input object
+        Input* GetInput() const;
+
+        //! Get the Xr Space object
+        Space* GetSpace() const;
+        
+        //! Return true if session is running
+        virtual bool IsSessionRunning() const = 0;
+
+        //! Return true if session is focused
+        virtual bool IsSessionFocused() const = 0;
+
+        //! Return true if a restart is requested
+        virtual bool IsRestartRequested() const = 0;
+    
+        //! Return true if render loop skip is requested
+        virtual bool IsExitRenderLoopRequested() const = 0;
+
+        //! Poll events and process the pending messages accordingly
+        virtual void PollEvents() = 0;
+
+        //! All the back end object to initialize properly
+        virtual AZ::RHI::ResultCode InitInternal(AZ::RHI::XRSessionDescriptor* descriptor) = 0;
+
+        //! Allow the back-end to cache the controller space data
+        virtual void LocateControllerSpace(AZ::u32 handIndex) = 0;
+
+        //! Api to retrieve the controller space data
+        virtual AZ::RPI::PoseData GetControllerPose(AZ::u32 handIndex) const = 0;
+
+        //! Api to retrieve the controller scale data
+        virtual float GetControllerScale(AZ::u32 handIndex) const = 0;
+
+        //! Api to retrieve the front view space data
+        virtual AZ::RPI::PoseData GetViewFrontPose() const = 0;
+
+    private:
+
+        ///////////////////////////////////////////////////////////////////
+        // XR::Object
+        void Shutdown() override;
+        ///////////////////////////////////////////////////////////////////
+
+        //! Called when the XR instance is being shutdown.
+        virtual void ShutdownInternal() = 0;
+
+        Ptr<Input> m_input;
+        Ptr<Space> m_space;
+        Descriptor m_descriptor;
+    };
+} // namespace XR

+ 46 - 0
Gems/XR/Code/Include/XR/XRSpace.h

@@ -0,0 +1,46 @@
+/*
+ * 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/intrusive_base.h>
+#include <XR/XRObject.h>
+
+namespace XR
+{
+    //! Base XR Space class which will provide access to the back-end concrete object
+    class Space
+        : public ::XR::Object
+    {
+	    
+    public:
+        AZ_CLASS_ALLOCATOR(Space, AZ::SystemAllocator, 0);
+        AZ_RTTI(Space, "{A78A37F1-8861-4EB4-8FC6-0E9C11394EF1}");
+
+        struct Descriptor
+        {
+            AZ::RHI::ValidationMode m_validationMode = AZ::RHI::ValidationMode::Disabled;
+        };
+
+        AZ::RHI::ResultCode Init(Descriptor descriptor);
+        const Space::Descriptor& GetDescriptor() const;
+    private:
+
+        ///////////////////////////////////////////////////////////////////
+        // XR::Object
+        void Shutdown() override;
+        ///////////////////////////////////////////////////////////////////
+
+        //! Called when the XR instance is being shutdown.
+        virtual void ShutdownInternal() = 0;
+        virtual AZ::RHI::ResultCode InitInternal() = 0;
+       
+        Descriptor m_descriptor;
+    };
+} // namespace XR

+ 129 - 0
Gems/XR/Code/Include/XR/XRSwapChain.h

@@ -0,0 +1,129 @@
+/*
+ * 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/containers/vector.h>
+#include <AzCore/std/smart_ptr/intrusive_ptr.h>
+#include <Atom/RHI/XRRenderingInterface.h>
+#include <XR/XRObject.h>
+
+namespace XR
+{
+    class Session;
+    class Device;
+
+    //! This class will be responsible for creating multiple XR::SwapChain::ViewSwapchains
+    //! (one per view). Each XR::SwapChain::ViewSwapchain will then be responsible
+    //! for manging and synchronizing multiple swap chain images
+    class SwapChain 
+        : public XR::Object
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(SwapChain, AZ::SystemAllocator, 0);
+        AZ_RTTI(SwapChain, "{0C666E76-E4B7-4097-8D14-713DC2C446EF}");
+
+        class Image 
+            : public AZStd::intrusive_base
+        {
+        public:
+            AZ_CLASS_ALLOCATOR(Image, AZ::SystemAllocator, 0);
+            AZ_RTTI(Image, "{4037835D-F1BB-4407-BC98-2299CC7BE0A3}");
+
+            Image() = default;
+            virtual ~Image() = default;
+        };
+
+        class View 
+            : public AZStd::intrusive_base
+        {
+        public:
+            AZ_CLASS_ALLOCATOR(View, AZ::SystemAllocator, 0);
+            AZ_RTTI(View, "{774EB724-8261-4684-AA78-EDF6BBECD48A}");
+
+            View() = default;
+            virtual ~View() = default;
+
+            virtual void Shutdown() = 0;
+            virtual AZ::u32 GetCurrentImageIndex() const = 0;
+            //! All the images associated with this View
+            AZStd::vector<Ptr<SwapChain::Image>> m_images;
+
+            //! The active image index
+            AZ::u32 m_activeImageIndex = 0;
+
+            //! Number of swapchain images associated with this view.
+            AZ::u32 m_numImages = 0;
+
+            //! Returns if an image acquired for this view.
+            bool m_isImageAcquired = false;
+
+            //! Width of the swap chain view.
+            AZ::u32 m_width = 0;
+    
+            //! Height of the swap chain view.
+            AZ::u32 m_height = 0;
+        };
+
+        struct Descriptor
+        {
+            AZ::RHI::ValidationMode m_validationMode = AZ::RHI::ValidationMode::Disabled;
+            Ptr<Instance> m_instance;
+            Ptr<Session> m_session;
+            Ptr<Device> m_device;
+        };
+
+        //! Returns the view swap chain related to the index.
+        SwapChain::View* GetView(const AZ::u32 swapChainIndex) const;
+
+        //! Returns the image associated with the provided image
+        //! index and view swap chain index.
+        SwapChain::Image* GetImage(AZ::u32 imageIndex, AZ::u32 swapChainIndex) const;
+
+        //! Initialize the XR swapchain.
+        AZ::RHI::ResultCode Init(const Descriptor& descriptor);
+
+        //! Get the descriptor. 
+        const Descriptor& GetDescriptor() const;
+
+        //! Get the number of Xr views
+        AZ::u32 GetNumViews() const;
+
+        //! Api to allow the back end object to return the requested native swapchain image
+        virtual AZ::RHI::ResultCode GetSwapChainImage(AZ::RHI::XRSwapChainDescriptor* swapchainDescriptor) const = 0;
+       
+        //! Api to allow the back end to report the recommended swapchain width
+        virtual AZ::u32 GetSwapChainWidth(AZ::u32 viewIndex) const = 0;
+        
+        //! Api to allow the back end to report the recommended swapchain height
+        virtual AZ::u32 GetSwapChainHeight(AZ::u32 viewIndex) const = 0;
+
+    protected:
+        
+        //! Number of Xr views
+        AZ::u32 m_numViews = 0;
+
+        //! Vector to hold all the SwapChain View objects
+        AZStd::vector<Ptr<SwapChain::View>> m_viewSwapchains;
+   
+    private:
+        ///////////////////////////////////////////////////////////////////
+        // XR::Object override
+        void Shutdown() override;
+        ///////////////////////////////////////////////////////////////////
+
+        //! Called when the swapchain is being shutdown.
+        virtual void ShutdownInternal() = 0;
+
+        //! Api to allow the back end object to initialize  
+        virtual AZ::RHI::ResultCode InitInternal() = 0;
+
+        Descriptor m_descriptor;
+    };
+} // namespace XR

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů