Skip to content

Commit

Permalink
Add Oculus Quest 2 support at RHI level (#14)
Browse files Browse the repository at this point in the history
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  o3de/o3de#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: o3de/o3de#11254
  • Loading branch information
moraaar authored Aug 17, 2022
1 parent eee482a commit a3d8e88
Show file tree
Hide file tree
Showing 13 changed files with 156 additions and 15 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 42 additions & 0 deletions Gems/OpenXRVk/3rdParty/Platform/Android/FindOpenXROculus.cmake
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -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)
2 changes: 1 addition & 1 deletion Gems/OpenXRVk/Code/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 4 additions & 7 deletions Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSwapChain.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
27 changes: 27 additions & 0 deletions Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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 };

Expand Down
43 changes: 38 additions & 5 deletions Gems/OpenXRVk/Code/Source/OpenXRVkSwapChain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -223,20 +250,21 @@ 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),
AZStd::end(SupportedColorSwapchainFormats));
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
Expand Down Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions Gems/OpenXRVk/External/OculusOpenXRMobileSDK/README.md
Original file line number Diff line number Diff line change
@@ -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/
5 changes: 4 additions & 1 deletion Gems/XR/Code/Include/XR/XRSwapChain.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions Gems/XR/Code/Include/XR/XRSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
///////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////
Expand Down
15 changes: 15 additions & 0 deletions Gems/XR/Code/Source/XRSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit a3d8e88

Please sign in to comment.