فهرست منبع

Add Oculus Quest 2 support at RHI level (#14)

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

- OpenXRVk uses Oculus's version of OpenXR library when built with `--oculus-project` option. If not present it will error whiule building with instruction of how to obtain Oculus OpenXR Mobile SDK.
- Also added instructions under `OpenXRVk/External/OculusOpenXRMobileSDK/` folder with how to download Oculus OpenXR Mobile SDK.
- Added `OpenXRVk/3rdParty/Platform/Android/FindOpenXROculus.cmake` which adds the 3rdparty target and checks if the Oculus OpenXR Mobile SDK has been downloaded to the external folder.
- On Android platform it needs to initialize the loader before calling XR functions, otherwise it crashes.
- Added functions to obtain xr swap chain format, necessary when creating the swapchains from Atom Vulkan side.

These changes go together with these changes  https://github.com/o3de/o3de/pull/11310

Tests done:
Built ASV project on PC and Android.
Run `RHI/OpenXr` sample on ASV on PC and Android. Available here: https://github.com/aws-lumberyard-dev/o3de-atom-sampleviewer/tree/openxr. Run on Android passing the options `-openxr=enable -sample=RHI/OpenXr`.
Built android with and without `--oculus-project` option and checked the manifest and OpenXR 3rdparty used are correct in each case.

NOTE: At the moment there is this bug when running `RHI/OpenXr` sample on Quest 2 natively: https://github.com/o3de/o3de/issues/11254
moraaar 2 سال پیش
والد
کامیت
a3d8e88161

+ 3 - 0
.gitignore

@@ -21,3 +21,6 @@ _savebackup/
 *.swatches
 /imgui.ini
 
+# Ignore Oculus OpenXR Mobile SDK files except the README.md file
+Gems/OpenXRVk/External/OculusOpenXRMobileSDK/**
+!Gems/OpenXRVk/External/OculusOpenXRMobileSDK/README.md

+ 9 - 1
Gems/OpenXRVk/3rdParty/Platform/Android/BuiltInPackages_android.cmake

@@ -6,4 +6,12 @@
 #
 #
 
-ly_associate_package(PACKAGE_NAME OpenXR-1.0.22-rev1-android    TARGETS OpenXR  PACKAGE_HASH 1227204583ce224c7e3843e82bb36deb576df6b458eecce46740cb8941902f21)
+set(ANDROID_USE_OCULUS_OPENXR OFF CACHE BOOL "When ON it uses OpenXR library from Oculus SDK.")
+
+if(ANDROID_USE_OCULUS_OPENXR)
+    include(${CMAKE_CURRENT_LIST_DIR}/FindOpenXROculus.cmake)
+    set(openxr_dependency 3rdParty::OpenXROculus)
+else()
+    ly_associate_package(PACKAGE_NAME OpenXR-1.0.22-rev1-android    TARGETS OpenXR  PACKAGE_HASH 1227204583ce224c7e3843e82bb36deb576df6b458eecce46740cb8941902f21)
+    set(openxr_dependency 3rdParty::OpenXR)
+endif()

+ 42 - 0
Gems/OpenXRVk/3rdParty/Platform/Android/FindOpenXROculus.cmake

@@ -0,0 +1,42 @@
+#
+# 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 file actually ingests the library and defines targets.
+set(TARGET_WITH_NAMESPACE "3rdParty::OpenXROculus")
+if (TARGET ${TARGET_WITH_NAMESPACE})
+    return()
+endif()
+
+set(MY_NAME "OpenXROculus")
+
+get_property(openxrvk_gem_root GLOBAL PROPERTY "@GEMROOT:OpenXRVk@")
+
+set(OculusOpenXRSDKPath ${openxrvk_gem_root}/External/OculusOpenXRMobileSDK)
+
+set(${MY_NAME}_INCLUDE_DIR 
+    ${OculusOpenXRSDKPath}/3rdParty/khronos/openxr/OpenXR-SDK/include
+    ${OculusOpenXRSDKPath}/OpenXR/Include)
+
+set(PATH_TO_SHARED_LIBS ${OculusOpenXRSDKPath}/OpenXR/Libs/Android/arm64-v8a)
+
+if(NOT EXISTS ${PATH_TO_SHARED_LIBS}/Release/libopenxr_loader.so)
+    message(FATAL_ERROR
+        "Oculus OpenXR loader library not found at ${PATH_TO_SHARED_LIBS}/Release. "
+        "Oculus OpenXR Mobile SDK needs to be downloaded via https://developer.oculus.com/downloads/native-android/ "
+        "and uncompressed into OpenXRVk/External/OculusOpenXRMobileSDK folder.")
+    return()
+endif()
+
+add_library(${TARGET_WITH_NAMESPACE} SHARED IMPORTED GLOBAL)
+ly_target_include_system_directories(TARGET ${TARGET_WITH_NAMESPACE} INTERFACE ${${MY_NAME}_INCLUDE_DIR})
+set_target_properties(${TARGET_WITH_NAMESPACE}
+    PROPERTIES
+        IMPORTED_LOCATION ${PATH_TO_SHARED_LIBS}/Release/libopenxr_loader.so
+        IMPORTED_LOCATION_DEBUG ${PATH_TO_SHARED_LIBS}/Debug/libopenxr_loader.so)
+
+set(${MY_NAME}_FOUND True)

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

@@ -7,3 +7,5 @@
 #
 
 ly_associate_package(PACKAGE_NAME OpenXR-1.0.22-rev2-linux  TARGETS OpenXR  PACKAGE_HASH 7d9045de0078a3f4a88bea2e3167e2c159acc8c62ac40ae15f8a31902b8d1f08)
+
+set(openxr_dependency 3rdParty::OpenXR)

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

@@ -7,3 +7,5 @@
 #
 
 ly_associate_package(PACKAGE_NAME OpenXR-1.0.22-rev1-windows    TARGETS OpenXR  PACKAGE_HASH 55235d77253efe1af046a4a3e7dd7a8e5f6768401326d5e077c827cce323cd11)
+
+set(openxr_dependency 3rdParty::OpenXR)

+ 1 - 1
Gems/OpenXRVk/Code/CMakeLists.txt

@@ -55,7 +55,7 @@ ly_add_target(
         PUBLIC
             AZ::AzCore
             AZ::AzFramework
-            3rdParty::OpenXR
+            ${openxr_dependency}
             AZ::AtomCore
             Gem::Atom_RHI_Vulkan.Reflect
             Gem::Atom_RHI_Vulkan.Glad.Static

+ 4 - 7
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSwapChain.h

@@ -66,14 +66,11 @@ namespace OpenXRVk
             XrSwapchain m_handle = XR_NULL_HANDLE;
         };
 
-        //! Assign the correct native Swapchain image based on the swapchain index and swapchain image index
+        // XR::SwapChain overrides...
         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;
+        AZ::RHI::Format GetSwapChainFormat(AZ::u32 viewIndex) const override;
 
         //! Get the view configurations supported by the drivers
         AZStd::vector<XrViewConfigurationView> GetViewConfigs() const;
@@ -87,10 +84,10 @@ namespace OpenXRVk
         void ShutdownInternal() override;
 
         //! Return supported swapchain image format
-        AZ::s64 SelectColorSwapChainFormat(const AZStd::vector<int64_t>& runtimeFormats) const;
+        VkFormat SelectColorSwapChainFormat(const AZStd::vector<int64_t>& runtimeFormats) const;
         
         AZStd::vector<XrViewConfigurationView> m_configViews;
-        AZ::s64 m_colorSwapChainFormat{ -1 };
+        VkFormat m_colorSwapChainFormat{ VK_FORMAT_UNDEFINED };
         AZ::u32 m_mipCount = 1;
         AZ::u32 m_faceCount = 1;
         AZ::u32 m_arraySize = 1;

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

@@ -11,6 +11,10 @@
 #include <Atom/RHI.Reflect/Vulkan/XRVkDescriptors.h>
 #include <AzCore/Casting/numeric_cast.h>
 
+#ifdef AZ_PLATFORM_ANDROID
+#include <AzCore/Android/AndroidEnv.h>
+#endif
+
 namespace OpenXRVk
 {
     XR::Ptr<Instance> Instance::Create()
@@ -84,6 +88,29 @@ namespace OpenXRVk
 
     AZ::RHI::ResultCode Instance::InitInstanceInternal(AZ::RHI::ValidationMode validationMode)
     {
+#ifdef AZ_PLATFORM_ANDROID
+        // Initialize the loader for Android platform
+        {
+            AZ::Android::AndroidEnv* androidEnv = AZ::Android::AndroidEnv::Get();
+            AZ_Assert(androidEnv != nullptr, "Invalid android enviroment");
+            
+            PFN_xrInitializeLoaderKHR initializeLoader = nullptr;
+            XrResult resultLoader = xrGetInstanceProcAddr(
+                XR_NULL_HANDLE, "xrInitializeLoaderKHR",
+                reinterpret_cast<PFN_xrVoidFunction*>(&initializeLoader));
+            ASSERT_IF_UNSUCCESSFUL(resultLoader);
+
+            XrLoaderInitInfoAndroidKHR loaderInitInfoAndroid;
+            memset(&loaderInitInfoAndroid, 0, sizeof(loaderInitInfoAndroid));
+            loaderInitInfoAndroid.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR;
+            loaderInitInfoAndroid.next = nullptr;
+            loaderInitInfoAndroid.applicationVM = androidEnv->GetActivityJavaVM();
+            loaderInitInfoAndroid.applicationContext = androidEnv->GetActivityRef();
+            
+            initializeLoader(reinterpret_cast<const XrLoaderInitInfoBaseHeaderKHR*>(&loaderInitInfoAndroid));
+        }
+#endif
+
         XR::RawStringList optionalLayers;
         XR::RawStringList optionalExtensions = { XR_KHR_VULKAN_ENABLE_EXTENSION_NAME };
 

+ 38 - 5
Gems/OpenXRVk/Code/Source/OpenXRVkSwapChain.cpp

@@ -16,6 +16,33 @@
 #include <OpenXRVk/OpenXRVkUtils.h>
 #include <XR/XRFactory.h>
 
+// TODO: Expose ConvertFormat in Vulkan RHI::Reflect
+#include <../Source/RHI/Formats.inl>
+
+namespace AZ::Vulkan
+{
+    RHI::Format ConvertFormat(VkFormat format)
+    {
+#define RHIVK_VK_TO_RHI(_FormatID, _VKFormat, _Color, _Depth, _Stencil) \
+case _VKFormat: \
+    return RHI::Format::_FormatID;
+
+        switch (format)
+        {
+        case VK_FORMAT_UNDEFINED:
+            return RHI::Format::Unknown;
+
+        RHIVK_EXPAND_FOR_FORMATS(RHIVK_VK_TO_RHI)
+
+        default:
+            AZ_Assert(false, "unhandled conversion in ConvertFormat");
+            return RHI::Format::Unknown;
+        }
+
+#undef RHIVK_VK_TO_RHI
+    }
+}
+
 namespace OpenXRVk
 {
 
@@ -141,7 +168,7 @@ namespace OpenXRVk
                 AZStd::string swapchainFormatsString;
                 for (int64_t format : swapChainFormats)
                 {
-                    const bool selected = format == m_colorSwapChainFormat;
+                    const bool selected = format == static_cast<int64_t>(m_colorSwapChainFormat);
                     swapchainFormatsString += " ";
                     if (selected)
                     {
@@ -175,7 +202,7 @@ namespace OpenXRVk
                     // Create the xr swapchain.
                     XrSwapchainCreateInfo swapchainCreateInfo{ XR_TYPE_SWAPCHAIN_CREATE_INFO };
                     swapchainCreateInfo.arraySize = m_arraySize;
-                    swapchainCreateInfo.format = m_colorSwapChainFormat;
+                    swapchainCreateInfo.format = static_cast<int64_t>(m_colorSwapChainFormat);
                     swapchainCreateInfo.width = configView.recommendedImageRectWidth;
                     swapchainCreateInfo.height = configView.recommendedImageRectHeight;
                     swapchainCreateInfo.mipCount = m_mipCount;
@@ -223,10 +250,10 @@ namespace OpenXRVk
         return AZ::RHI::ResultCode::Success;
     }
 
-    AZ::s64 SwapChain::SelectColorSwapChainFormat(const AZStd::vector<int64_t>& runtimeFormats) const
+    VkFormat SwapChain::SelectColorSwapChainFormat(const AZStd::vector<int64_t>& runtimeFormats) const
     {
         // List of supported color swapchain formats.
-        constexpr AZ::s64 SupportedColorSwapchainFormats[] = { VK_FORMAT_B8G8R8A8_UNORM };
+        constexpr int64_t SupportedColorSwapchainFormats[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM };
 
         auto swapchainFormatIt =
             AZStd::find_first_of(runtimeFormats.begin(), runtimeFormats.end(), AZStd::begin(SupportedColorSwapchainFormats),
@@ -234,9 +261,10 @@ namespace OpenXRVk
         if (swapchainFormatIt == runtimeFormats.end()) 
         {
             AZ_Error("OpenXRVk", false, "No runtime swapchain format supported for color swapchain");
+            return VK_FORMAT_UNDEFINED;
         }
 
-        return *swapchainFormatIt;
+        return static_cast<VkFormat>(*swapchainFormatIt);
     }
 
     AZStd::vector<XrViewConfigurationView> SwapChain::GetViewConfigs() const
@@ -266,6 +294,11 @@ namespace OpenXRVk
         return m_configViews[viewIndex].recommendedImageRectHeight;
     }
 
+    AZ::RHI::Format SwapChain::GetSwapChainFormat([[maybe_unused]] AZ::u32 viewIndex) const
+    {
+        return AZ::Vulkan::ConvertFormat(m_colorSwapChainFormat);
+    }
+
     void SwapChain::ShutdownInternal()
     {
         for(XR::Ptr<XR::SwapChain::View> viewSwapChain : m_viewSwapchains)

+ 7 - 0
Gems/OpenXRVk/External/OculusOpenXRMobileSDK/README.md

@@ -0,0 +1,7 @@
+# Oculus OpenXR Mobile SDK
+
+The Oculus OpenXR Mobile SDK is not included as part of O3DE.
+
+When enabling OpenXRVk Gem, download the SDK and uncompress it in the following folder within the gem: `OpenXRVk\External\OculusOpenXRMobileSDK`
+
+The Oculus OpenXR Mobile SDK can be found in the following link: https://developer.oculus.com/downloads/native-android/

+ 4 - 1
Gems/XR/Code/Include/XR/XRSwapChain.h

@@ -11,7 +11,7 @@
 #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 <Atom/RHI.Reflect/Format.h>
 #include <XR/XRObject.h>
 
 namespace XR
@@ -104,6 +104,9 @@ namespace XR
         //! Api to allow the back end to report the recommended swapchain height
         virtual AZ::u32 GetSwapChainHeight(AZ::u32 viewIndex) const = 0;
 
+        //! Api to allow the back end to report the swapchain format.
+        virtual AZ::RHI::Format GetSwapChainFormat(AZ::u32 viewIndex) const = 0;
+
     protected:
         
         //! Number of Xr views

+ 2 - 0
Gems/XR/Code/Include/XR/XRSystem.h

@@ -61,6 +61,7 @@ namespace XR
         AZ::RHI::ResultCode GetSwapChainImage(AZ::RHI::XRSwapChainDescriptor* swapchainDescriptor) const override;
         AZ::u32 GetSwapChainWidth(AZ::u32 viewIndex) const override;
         AZ::u32 GetSwapChainHeight(AZ::u32 viewIndex) const override;
+        AZ::RHI::Format GetSwapChainFormat(AZ::u32 viewIndex) const override;
         AZ::RPI::FovData GetViewFov(AZ::u32 viewIndex) const override;
         AZ::RPI::PoseData GetViewPose(AZ::u32 viewIndex) const override;
         AZ::RPI::PoseData GetViewFrontPose() const override;
@@ -70,6 +71,7 @@ namespace XR
         AZ::Matrix4x4 CreateProjectionOffset(float angleLeft, float angleRight, 
                                              float angleBottom, float angleTop, 
                                              float nearDist, float farDist) override;
+        AZ::RHI::XRRenderingInterface* GetRHIXRRenderingInterface() override;
         ///////////////////////////////////////////////////////////////////
 
         ///////////////////////////////////////////////////////////////////

+ 15 - 0
Gems/XR/Code/Source/XRSystem.cpp

@@ -122,6 +122,16 @@ namespace XR
         return 0;
     }
 
+    AZ::RHI::Format System::GetSwapChainFormat(AZ::u32 viewIndex) const
+    {
+        AZ_Assert(m_swapChain, "SwapChain is null");
+        if (m_swapChain)
+        {
+            return m_swapChain->GetSwapChainFormat(viewIndex);
+        }
+        return AZ::RHI::Format::Unknown;
+    }
+
     void System::OnSystemTick()
     {
         m_session->PollEvents();
@@ -220,6 +230,11 @@ namespace XR
         return XR::CreateProjectionOffset(angleLeft, angleRight, angleBottom, angleTop, nearDist, farDist);
     }
 
+    AZ::RHI::XRRenderingInterface* System::GetRHIXRRenderingInterface()
+    {
+        return this;
+    }
+
     void System::Shutdown()
     {
         AZ::SystemTickBus::Handler::BusDisconnect();