Android build settings + metaxr

This commit is contained in:
2025-05-14 14:00:02 +03:00
parent 6a2bb7475e
commit d5aa21f55c
594 changed files with 200530 additions and 2 deletions

View File

@@ -0,0 +1,90 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRCoreExtensionPlugin.h"
#include "DefaultSpectatorScreenController.h"
#include "OculusXRFunctionLibrary.h"
#include "OculusXRHMDRuntimeSettings.h"
#include "OculusXRXRFunctions.h"
#include "OculusXROpenXRUtilities.h"
#include "OpenXRCore.h"
#include "OpenXRHMDSettings.h"
#if PLATFORM_ANDROID
// #include <openxr_oculus.h>
#include <dlfcn.h>
#endif // PLATFORM_ANDROID
DEFINE_LOG_CATEGORY(LogOculusOpenXRPlugin);
namespace OculusXR
{
bool FCoreExtensionPlugin::IsStandaloneStereoOnlyDevice()
{
#if PLATFORM_ANDROID
const bool bIsStandaloneStereoDevice = FAndroidMisc::GetDeviceMake() == FString("Oculus");
#else
const bool bIsStandaloneStereoDevice = false;
#endif
return bIsStandaloneStereoDevice;
}
bool FCoreExtensionPlugin::GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions)
{
return true;
}
bool FCoreExtensionPlugin::GetSpectatorScreenController(FHeadMountedDisplayBase* InHMDBase, TUniquePtr<FDefaultSpectatorScreenController>& OutSpectatorScreenController)
{
#if PLATFORM_ANDROID
OutSpectatorScreenController = nullptr;
return true;
#else // PLATFORM_ANDROID
OutSpectatorScreenController = MakeUnique<FDefaultSpectatorScreenController>(InHMDBase);
return false;
#endif // PLATFORM_ANDROID
}
const void* FCoreExtensionPlugin::OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext)
{
check(IsInGameThread());
InitOpenXRFunctions(InInstance);
if (UOpenXRHMDSettings* OpenXRHMDSettings = GetMutableDefault<UOpenXRHMDSettings>())
{
OpenXRHMDSettings->bIsFBFoveationEnabled = true;
}
if (IConsoleVariable* VariableRateShadingCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.VRS.Enable")))
{
VariableRateShadingCVar->Set(1);
}
if (const UOculusXRHMDRuntimeSettings* OculusXRHMDSettings = GetDefault<UOculusXRHMDRuntimeSettings>())
{
if (IConsoleVariable* FoveationLevelCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("xr.OpenXRFBFoveationLevel")))
{
FoveationLevelCVar->Set(static_cast<int>(OculusXRHMDSettings->FoveatedRenderingLevel));
}
if (IConsoleVariable* FoveationDynamicCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("xr.OpenXRFBFoveationDynamic")))
{
FoveationDynamicCVar->Set(OculusXRHMDSettings->bDynamicFoveatedRendering);
}
}
#if PLATFORM_ANDROID
#if UE_VERSION_OLDER_THAN(5, 5, 0)
if (GRHISupportsRHIThread && GIsThreadedRendering && GUseRHIThread_InternalUseOnly)
{
SetRHIThreadEnabled(false, false);
}
#else
GPendingRHIThreadMode = ERHIThreadMode::None;
#endif // UE_VERSION_OLDER_THAN
#endif // PLATFORM_ANDROID
return InNext;
}
} // namespace OculusXR

View File

@@ -0,0 +1,23 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "Misc/EngineVersionComparison.h"
#include "OpenXR/IOculusXRExtensionPlugin.h"
DECLARE_LOG_CATEGORY_EXTERN(LogOculusOpenXRPlugin, Log, All);
namespace OculusXR
{
class FCoreExtensionPlugin : public IOculusXRExtensionPlugin
{
public:
// IOpenXRExtensionPlugin
virtual bool IsStandaloneStereoOnlyDevice() override;
virtual bool GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
virtual bool GetSpectatorScreenController(FHeadMountedDisplayBase* InHMDBase, TUniquePtr<FDefaultSpectatorScreenController>& OutSpectatorScreenController) override;
virtual const void* OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext) override;
};
} // namespace OculusXR

View File

@@ -0,0 +1,136 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include <functional>
#include <mutex>
#include "khronos/openxr/openxr.h"
#include "IOpenXRExtensionPlugin.h"
#include "OculusXRHMDTypes.h"
#include "Shader.h"
#include "Misc/EngineVersionComparison.h"
namespace OculusXR
{
/// Texture handle which can be cast to GLuint, VkImage, ID3D11Texture2D*, or ID3D12Resource*
using TextureHandle = unsigned long long;
constexpr uint32 EYE_COUNT = 2;
void RenderHardOcclusions_RenderThread(IRendererModule* RendererModule, const FVector2f& DepthFactors,
const FMatrix44f ScreenToDepthMatrices[EYE_COUNT], FRHITexture* DepthTexture, FRHICommandList& RHICmdList, const FSceneView& InView);
void RenderEnvironmentDepthMinMaxTexture_RenderThread(IRendererModule* RendererModule, FTextureRHIRef EnvironmentDepthMinMaxTexture,
FTextureRHIRef EnvironmentDepthSwapchain, FRHICommandListImmediate& RHICmdList);
#ifdef WITH_OCULUS_BRANCH
extern PFN_xrCreateEnvironmentDepthProviderMETA xrCreateEnvironmentDepthProviderMETA;
extern PFN_xrDestroyEnvironmentDepthProviderMETA xrDestroyEnvironmentDepthProviderMETA;
extern PFN_xrStartEnvironmentDepthProviderMETA xrStartEnvironmentDepthProviderMETA;
extern PFN_xrStopEnvironmentDepthProviderMETA xrStopEnvironmentDepthProviderMETA;
extern PFN_xrCreateEnvironmentDepthSwapchainMETA xrCreateEnvironmentDepthSwapchainMETA;
extern PFN_xrDestroyEnvironmentDepthSwapchainMETA xrDestroyEnvironmentDepthSwapchainMETA;
extern PFN_xrEnumerateEnvironmentDepthSwapchainImagesMETA xrEnumerateEnvironmentDepthSwapchainImagesMETA;
extern PFN_xrGetEnvironmentDepthSwapchainStateMETA xrGetEnvironmentDepthSwapchainStateMETA;
extern PFN_xrAcquireEnvironmentDepthImageMETA xrAcquireEnvironmentDepthImageMETA;
extern PFN_xrSetEnvironmentDepthHandRemovalMETA xrSetEnvironmentDepthHandRemovalMETA;
class FEnvironmentDepthExtensionPlugin : public IOpenXRExtensionPlugin
{
public:
void RegisterOpenXRExtensionPlugin()
{
#if defined(WITH_OCULUS_BRANCH)
RegisterOpenXRExtensionModularFeature();
#endif
}
virtual void PostCreateSession(XrSession InSession) override;
virtual void BindExtensionPluginDelegates(class IOpenXRExtensionPluginDelegates& OpenXRHMD) override;
virtual bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
virtual const void* OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext) override;
virtual const void* OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext) override;
virtual void OnBeginRendering_GameThread(XrSession InSession) override;
virtual void OnBeginRenderingLate_RenderThread(XrSession InSession, FRHICommandListImmediate& RHICmdList) override;
virtual void PostBeginFrame_RHIThread(XrTime PredictedDisplayTime) override;
virtual void PostRenderBasePassMobile_RenderThread(FRHICommandList& RHICmdList, FSceneView& InView) override;
virtual void PostRenderBasePassDeferred_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView,
const FRenderTargetBindingSlots& RenderTargets, TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTextures) override;
virtual bool OnStartGameFrame(FWorldContext& WorldContext) override;
virtual bool OnEndGameFrame(FWorldContext& WorldContext) override;
virtual void OnDestroySession(XrSession InSession) override;
virtual const void* OnLocateViews(XrSession InSession, XrTime InDisplayTime, XrViewConfigurationType ViewConfigurationType, const void* InNext) override;
#ifdef WITH_OCULUS_BRANCH
virtual bool FindEnvironmentDepthTexture_RenderThread(FTextureRHIRef& OutTexture, FTextureRHIRef& OutMinMaxTexture,
FVector2f& OutDepthFactors, FMatrix44f OutScreenToDepthMatrices[2], FMatrix44f OutDepthViewProjMatrices[2]) override;
#endif // WITH_OCULUS_BRANCH
bool StartEnvironmentDepth();
bool StopEnvironmentDepth();
void SetXROcclusionsMode(UObject* WorldContextObject, EOculusXROcclusionsMode Mode);
bool IsEnvironmentDepthStarted() const;
bool InitializeEnvironmentDepth_RenderThread();
bool DestroyEnvironmentDepth_RenderThread();
bool GetEnvironmentDepthTextureStageCount_RenderThread(int& OutStageCount);
bool GetEnvironmentDepthTexture_RenderThread(int Stage, TextureHandle& OutHandle);
bool SetEnvironmentDepthHandRemoval_RenderThread(bool Enabled);
bool StartEnvironmentDepth_RenderThread();
bool StopEnvironmentDepth_RenderThread();
bool AcquireEnvironmentDepthTexture_RHIThread(XrTime predictedDisplayTime);
bool GetEnvironmentDepthFrameDesc_RenderThread(TOptional<XrEnvironmentDepthImageMETA>& OutEnvironmentDepthFrameDesc);
private:
XrSession Session = XR_NULL_HANDLE;
XrSpace StageSpace = XR_NULL_HANDLE;
XrEnvironmentDepthProviderMETA EnvironmentDepthProviderMeta = XR_NULL_HANDLE;
XrEnvironmentDepthSwapchainMETA EnvironmentDepthSwapchainMeta = XR_NULL_HANDLE;
XrEnvironmentDepthSwapchainStateMETA EnvironmentDepthSwapchainStateMeta{ XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_STATE_META };
std::mutex EnvironmentDepthTextureMutex;
bool bExtEnvironmentDepthAvailable = false;
bool bHandsRemovalSupported = false;
bool bEnvironmentDepthRunning = false;
std::function<FTextureRHIRef(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, const FClearValueBinding& InBinding, uint32 InNumMips,
uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, TextureHandle InTexture,
ETextureCreateFlags InTexCreateFlags)>
CreateTexture_RenderThread_Fn;
TArray<FTextureRHIRef> EnvironmentDepthSwapchain;
FTextureRHIRef EnvironmentDepthMinMaxTexture;
int PrevEnvironmentDepthMinMaxSwapchainIndex = -1;
TArray<TextureHandle> EnvironmentDepthTextures;
TOptional<XrEnvironmentDepthImageMETA> EnvironmentDepthFrameDesc;
std::atomic<bool> bHardOcclusionsEnabled = false;
std::atomic<bool> bSoftOcclusionsEnabled = false;
std::atomic<bool> bEnvironmentDepthHandRemovalEnabled = false;
FString RHIString;
IRendererModule* RendererModule = nullptr;
float WorldToMeters;
float WorldToMeters_RenderThread;
FTransform TrackingToWorld;
FTransform TrackingToWorld_RenderThread;
FQuat HeadOrientation;
FQuat HeadOrientation_RenderThread;
FQuat BaseOrientation;
FQuat BaseOrientation_RenderThread;
TArray<XrView> EyeViews;
TArray<XrView> EyeViews_RenderThread;
XrStructureType GetEnvironmentDepthSwapchainImageType() const;
bool ComputeEnvironmentDepthParameters_RenderThread(FVector2f& DepthFactors, FMatrix44f ScreenToDepth[EYE_COUNT],
FMatrix44f DepthViewProj[EYE_COUNT], int& SwapchainIndex);
TArray<FTextureRHIRef> CreateSwapChainTextures_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat,
const FClearValueBinding& InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType,
const TArray<TextureHandle>& InTextures, ETextureCreateFlags InTexCreateFlags, const TCHAR* DebugName) const;
void PrepareAndRenderHardOcclusions_RenderThread(FRHICommandList& RHICmdList, FSceneView& InView);
};
#endif
} // namespace OculusXR

View File

@@ -0,0 +1,76 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRExtensionPluginManager.h"
namespace OculusXR
{
FExtensionPluginManager::FExtensionPluginManager()
: CoreExtensionPlugin()
, PerformanceExtensionPlugin()
, XRSimulatorExtensionPlugin()
, GuardianExtensionPlugin()
, LayerExtensionPlugin()
#ifdef WITH_OCULUS_BRANCH
, EnvironmentDepthExtensionPlugin()
#endif
#if defined(WITH_OCULUS_BRANCH)
, SpaceWarpExtensionPlugin()
#endif // defined(WITH_OCULUS_BRANCH)
, SystemInfoExtensionPlugin()
{
}
FExtensionPluginManager::~FExtensionPluginManager()
{
}
void FExtensionPluginManager::StartupOpenXRPlugins()
{
CoreExtensionPlugin.RegisterOpenXRExtensionPlugin();
PerformanceExtensionPlugin.RegisterOpenXRExtensionPlugin();
XRSimulatorExtensionPlugin.RegisterOpenXRExtensionPlugin();
SystemInfoExtensionPlugin.RegisterOpenXRExtensionPlugin();
GuardianExtensionPlugin.RegisterOpenXRExtensionPlugin();
LayerExtensionPlugin.RegisterOpenXRExtensionPlugin();
#ifdef WITH_OCULUS_BRANCH
EnvironmentDepthExtensionPlugin.RegisterOpenXRExtensionPlugin();
#endif
#if defined(WITH_OCULUS_BRANCH)
SpaceWarpExtensionPlugin.RegisterOpenXRExtensionPlugin();
#endif // defined(WITH_OCULUS_BRANCH)
MultiPlayerStateExtensionPlugin.RegisterOpenXRExtensionPlugin();
}
FPerformanceExtensionPlugin& FExtensionPluginManager::GetPerformanceExtensionPlugin()
{
return PerformanceExtensionPlugin;
}
FSystemInfoExtensionPlugin& FExtensionPluginManager::GetSystemInfoExtensionPlugin()
{
return SystemInfoExtensionPlugin;
}
FGuardianExtensionPlugin& FExtensionPluginManager::GetGuardianExtensionPlugin()
{
return GuardianExtensionPlugin;
}
FLayerExtensionPlugin& FExtensionPluginManager::GetLayerExtensionPlugin()
{
return LayerExtensionPlugin;
}
#ifdef WITH_OCULUS_BRANCH
FEnvironmentDepthExtensionPlugin& FExtensionPluginManager::GetEnvironmentDepthExtensionPlugin()
{
return EnvironmentDepthExtensionPlugin;
}
#endif // WITH_OCULUS_BRANCH
FMultiPlayerStateExtensionPlugin& FExtensionPluginManager::GetMultiPlayerStateExtensionPlugin()
{
return MultiPlayerStateExtensionPlugin;
}
} // namespace OculusXR

View File

@@ -0,0 +1,52 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "OculusXREnvironmentDepthExtensionPlugin.h"
#include "OculusXRCoreExtensionPlugin.h"
#include "OculusXRGuardianExtensionPlugin.h"
#include "OculusXRLayerExtensionPlugin.h"
#include "OculusXRPerformanceExtensionPlugin.h"
#include "OculusXRSimulatorExtensionPlugin.h"
#include "OculusXRSpaceWarp.h"
#include "OculusXRSystemInfoExtensionPlugin.h"
#include "OculusXRMultiPlayerStateExtensionPlugin.h"
namespace OculusXR
{
class FExtensionPluginManager
{
public:
FExtensionPluginManager();
virtual ~FExtensionPluginManager();
void StartupOpenXRPlugins();
FPerformanceExtensionPlugin& GetPerformanceExtensionPlugin();
FSystemInfoExtensionPlugin& GetSystemInfoExtensionPlugin();
FGuardianExtensionPlugin& GetGuardianExtensionPlugin();
FLayerExtensionPlugin& GetLayerExtensionPlugin();
#ifdef WITH_OCULUS_BRANCH
FEnvironmentDepthExtensionPlugin& GetEnvironmentDepthExtensionPlugin();
#endif
FMultiPlayerStateExtensionPlugin& GetMultiPlayerStateExtensionPlugin();
private:
FCoreExtensionPlugin CoreExtensionPlugin;
FPerformanceExtensionPlugin PerformanceExtensionPlugin;
FXRSimulatorExtensionPlugin XRSimulatorExtensionPlugin;
FGuardianExtensionPlugin GuardianExtensionPlugin;
FLayerExtensionPlugin LayerExtensionPlugin;
#ifdef WITH_OCULUS_BRANCH
FEnvironmentDepthExtensionPlugin EnvironmentDepthExtensionPlugin;
#endif
#if defined(WITH_OCULUS_BRANCH)
FSpaceWarpExtensionPlugin SpaceWarpExtensionPlugin;
#endif // defined(WITH_OCULUS_BRANCH)
FMultiPlayerStateExtensionPlugin MultiPlayerStateExtensionPlugin;
FSystemInfoExtensionPlugin SystemInfoExtensionPlugin;
};
} // namespace OculusXR

View File

@@ -0,0 +1,78 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRGuardianExtensionPlugin.h"
#include "Engine/GameEngine.h"
#include "IXRTrackingSystem.h"
#include "OculusXROpenXRUtilities.h"
#include "OpenXRCore.h"
DEFINE_LOG_CATEGORY(LogOculusXRGuardian);
namespace OculusXR
{
void FGuardianExtensionPlugin::PostCreateSession(XrSession InSession)
{
Session = InSession;
}
bool FGuardianExtensionPlugin::IsGuardianConfigured()
{
XrExtent2Df Extent;
const XrResult Result = xrGetReferenceSpaceBoundsRect(Session, XR_REFERENCE_SPACE_TYPE_STAGE, &Extent);
return (XR_SUCCEEDED(Result) && (Result != XR_SPACE_BOUNDS_UNAVAILABLE));
}
bool FGuardianExtensionPlugin::GetGuardianPoints(TArray<FVector>& BoundaryPoints)
{
XrExtent2Df Extent;
XrResult Result = xrGetReferenceSpaceBoundsRect(Session, XR_REFERENCE_SPACE_TYPE_STAGE, &Extent);
if (!XR_SUCCEEDED(Result) || Result == XR_SPACE_BOUNDS_UNAVAILABLE)
{
return false;
}
IXRTrackingSystem* TrackingSystem = GetOpenXRTrackingSystem();
if (TrackingSystem == nullptr)
{
return false;
}
FTransform OriginTransform;
if (!TrackingSystem->GetTrackingOriginTransform(EHMDTrackingOrigin::Stage, OriginTransform))
{
return false;
}
const float WorldToMetersScale = TrackingSystem->GetWorldToMetersScale();
const XrVector3f XrPoints[] = {
{ -Extent.width * 0.5f, 0.0f, -Extent.height * 0.5 },
{ -Extent.width * 0.5f, 0.0f, Extent.height * 0.5f },
{ Extent.width * 0.5f, 0.0f, Extent.height * 0.5f },
{ Extent.width * 0.5f, 0.0f, -Extent.height * 0.5f }
};
BoundaryPoints.Reset(UE_ARRAY_COUNT(XrPoints));
for (int Index = 0; Index < UE_ARRAY_COUNT(XrPoints); ++Index)
{
BoundaryPoints.Emplace(OriginTransform.TransformPosition(ToFVector(XrPoints[Index], WorldToMetersScale)));
}
return true;
}
FVector FGuardianExtensionPlugin::GetGuardianDimensions()
{
XrExtent2Df Extent;
FVector Dimensions;
XrResult Result = xrGetReferenceSpaceBoundsRect(Session, XR_REFERENCE_SPACE_TYPE_STAGE, &Extent);
if (XR_SUCCEEDED(Result) && Result == XR_SPACE_BOUNDS_UNAVAILABLE)
{
// Set height to 10.0f which matches expected OVRPlugin Version
const float BoundsHeight = 10.0f;
Dimensions = FVector(Extent.width, BoundsHeight, Extent.height);
}
return Dimensions;
}
} // namespace OculusXR

View File

@@ -0,0 +1,24 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "OculusXRHMDTypes.h"
#include "OpenXR/IOculusXRExtensionPlugin.h"
DECLARE_LOG_CATEGORY_EXTERN(LogOculusXRGuardian, Log, All);
namespace OculusXR
{
class FGuardianExtensionPlugin : public IOculusXRExtensionPlugin
{
public:
// IOpenXRExtensionPlugin
virtual void PostCreateSession(XrSession InSession) override;
bool IsGuardianConfigured();
bool GetGuardianPoints(TArray<FVector>& BoundaryPoints);
FVector GetGuardianDimensions();
private:
XrSession Session;
};
} // namespace OculusXR

View File

@@ -0,0 +1,344 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRLayerExtensionPlugin.h"
#include "Async/Async.h"
#include "DynamicResolutionState.h"
#include "IHeadMountedDisplay.h"
#include "IOpenXRHMD.h"
#include "IOpenXRHMDModule.h"
#include "OculusXRHMD_DynamicResolutionState.h"
#include "OculusXRHMDRuntimeSettings.h"
#include "OculusXROpenXRUtilities.h"
#include "OculusXRXRFunctions.h"
#include "OpenXRCore.h"
#include "XRThreadUtils.h"
namespace
{
XrCompositionLayerSettingsFlagsFB ToSharpenLayerFlag(EOculusXREyeBufferSharpenType EyeBufferSharpenType)
{
XrCompositionLayerSettingsFlagsFB Flag = 0;
switch (EyeBufferSharpenType)
{
case EOculusXREyeBufferSharpenType::SLST_None:
Flag = 0;
break;
case EOculusXREyeBufferSharpenType::SLST_Normal:
Flag = XR_COMPOSITION_LAYER_SETTINGS_NORMAL_SHARPENING_BIT_FB;
break;
case EOculusXREyeBufferSharpenType::SLST_Quality:
Flag = XR_COMPOSITION_LAYER_SETTINGS_QUALITY_SHARPENING_BIT_FB;
break;
case EOculusXREyeBufferSharpenType::SLST_Auto:
Flag = XR_COMPOSITION_LAYER_SETTINGS_AUTO_LAYER_FILTER_BIT_META;
break;
default:
break;
}
return Flag;
}
XrColor4f ToXrColor4f(FLinearColor Color)
{
return XrColor4f{ Color.R, Color.G, Color.B, Color.A };
}
} // namespace
namespace OculusXR
{
FLayerExtensionPlugin::FLayerExtensionPlugin()
: Session(XR_NULL_HANDLE)
, bExtLocalDimmingAvailable(false)
, bExtCompositionLayerSettingsAvailable(false)
, bRecommendedResolutionExtensionAvailable(false)
, LocalDimmingMode_RHIThread(XR_LOCAL_DIMMING_MODE_ON_META)
, LocalDimmingExt_RHIThread{}
, EyeSharpenLayerFlags_RHIThread(0)
, ColorScaleInfo_RHIThread{}
, _HeadersStorage{}
, bPixelDensityAdaptive(false)
, RecommendedImageHeight_GameThread(0)
, Settings_GameThread{}
, MaxPixelDensity_RenderThread(0)
{
}
bool FLayerExtensionPlugin::GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions)
{
OutExtensions.Add(XR_META_LOCAL_DIMMING_EXTENSION_NAME);
OutExtensions.Add(XR_FB_COMPOSITION_LAYER_SETTINGS_EXTENSION_NAME);
OutExtensions.Add(XR_META_RECOMMENDED_LAYER_RESOLUTION_EXTENSION_NAME);
return true;
}
const void* FLayerExtensionPlugin::OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext)
{
if (InModule != nullptr)
{
bExtLocalDimmingAvailable = InModule->IsExtensionEnabled(XR_META_LOCAL_DIMMING_EXTENSION_NAME);
bExtCompositionLayerSettingsAvailable = InModule->IsExtensionEnabled(XR_FB_COMPOSITION_LAYER_SETTINGS_EXTENSION_NAME);
bRecommendedResolutionExtensionAvailable = InModule->IsExtensionEnabled(XR_META_RECOMMENDED_LAYER_RESOLUTION_EXTENSION_NAME);
}
return IOculusXRExtensionPlugin::OnCreateInstance(InModule, InNext);
}
void FLayerExtensionPlugin::PostCreateSession(XrSession InSession)
{
Session = InSession;
const UOculusXRHMDRuntimeSettings* HMDSettings = GetDefault<UOculusXRHMDRuntimeSettings>();
if (HMDSettings != nullptr)
{
#ifdef WITH_OCULUS_BRANCH
// currently only enabled in fork
bPixelDensityAdaptive = HMDSettings->bDynamicResolution && bRecommendedResolutionExtensionAvailable;
#endif
if (IConsoleVariable* MobileDynamicResCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("xr.MobileLDRDynamicResolution")))
{
MobileDynamicResCVar->Set(bPixelDensityAdaptive);
}
if (bPixelDensityAdaptive)
{
Settings_GameThread = MakeShareable(new OculusXRHMD::FSettings());
Settings_GameThread->Flags.bPixelDensityAdaptive = bPixelDensityAdaptive;
if (IConsoleVariable* DynamicResOperationCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.DynamicRes.OperationMode")))
{
// Operation mode for dynamic resolution
// Enable regardless of the game user settings
DynamicResOperationCVar->Set(2);
}
GEngine->ChangeDynamicResolutionStateAtNextFrame(MakeShareable(new OculusXRHMD::FDynamicResolutionState(Settings_GameThread)));
const float MaxPixelDensity = Settings_GameThread->GetPixelDensityMax();
ENQUEUE_RENDER_COMMAND(OculusXR_SetEnableLocalDimming)
([this, MaxPixelDensity](FRHICommandListImmediate& RHICmdList) {
MaxPixelDensity_RenderThread = MaxPixelDensity;
});
}
}
}
void FLayerExtensionPlugin::OnBeginRendering_GameThread(XrSession InSession)
{
check(IsInGameThread());
if (bPixelDensityAdaptive)
{
IXRTrackingSystem* TrackingSystem = OculusXR::GetOpenXRTrackingSystem();
if (TrackingSystem != nullptr)
{
IOpenXRHMD* OpenXRHMD = TrackingSystem->GetIOpenXRHMD();
check(OpenXRHMD != nullptr);
const XrTime PredictedDisplayTime = OpenXRHMD->GetDisplayTime();
ENQUEUE_RENDER_COMMAND(OculusXR_UpdatePredictedTime)
([this, PredictedDisplayTime](FRHICommandListImmediate& RHICmdList) {
RHICmdList.EnqueueLambda([this, PredictedDisplayTime](FRHICommandListImmediate& RHICmdList) {
PredictedDisplayTime_RHIThread = PredictedDisplayTime;
});
});
IHeadMountedDisplay* Hmd = TrackingSystem->GetHMDDevice();
IHeadMountedDisplay::MonitorInfo MonitorInfo = {};
check(Hmd != nullptr);
if (Hmd->GetHMDMonitorInfo(MonitorInfo))
{
float PixelDensity = RecommendedImageHeight_GameThread == 0 ? Hmd->GetPixelDenity() : static_cast<float>(RecommendedImageHeight_GameThread) / MonitorInfo.ResolutionY;
static const auto CVarOculusDynamicPixelDensity = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.Oculus.DynamicResolution.PixelDensity"));
const float PixelDensityCVarOverride = CVarOculusDynamicPixelDensity != nullptr ? CVarOculusDynamicPixelDensity->GetValueOnAnyThread() : 0.0f;
if (PixelDensityCVarOverride > 0.0f)
{
PixelDensity = PixelDensityCVarOverride;
}
check(Settings_GameThread != nullptr)
Settings_GameThread->SetPixelDensitySmooth(PixelDensity);
}
}
}
else
{
if (Settings_GameThread != nullptr)
{
#if !UE_VERSION_OLDER_THAN(5, 5, 0)
static const auto PixelDensityCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("xr.SecondaryScreenPercentage.HMDRenderTarget"));
#else
static const auto PixelDensityCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("vr.PixelDensity"));
#endif
Settings_GameThread->SetPixelDensity(PixelDensityCVar ? PixelDensityCVar->GetFloat() : 1.0f);
}
}
}
#ifdef WITH_OCULUS_BRANCH
float FLayerExtensionPlugin::GetMaxPixelDensity()
{
check(IsInGameThread() || IsInRenderingThread());
float PixelDensity = 0.0f;
if (bPixelDensityAdaptive)
{
// Engine allows this call to happen on game or rendering thread.
PixelDensity = IsInRenderingThread() ? MaxPixelDensity_RenderThread : Settings_GameThread->GetPixelDensityMax();
}
return PixelDensity;
}
#endif
const void* FLayerExtensionPlugin::OnEndFrame(XrSession InSession, XrTime DisplayTime, const void* InNext)
{
check(IsInRenderingThread() || IsInRHIThread());
const void* Next = InNext;
if (bExtLocalDimmingAvailable)
{
LocalDimmingExt_RHIThread.type = XR_TYPE_LOCAL_DIMMING_FRAME_END_INFO_META;
LocalDimmingExt_RHIThread.localDimmingMode = LocalDimmingMode_RHIThread;
LocalDimmingExt_RHIThread.next = Next;
Next = &LocalDimmingExt_RHIThread;
}
return Next;
}
const void* FLayerExtensionPlugin::OnEndProjectionLayer(XrSession InSession, int32 InLayerIndex, const void* InNext, XrCompositionLayerFlags& OutFlags)
{
check(IsInRenderingThread() || IsInRHIThread());
const void* Next = InNext;
if (bExtCompositionLayerSettingsAvailable)
{
XrCompositionLayerSettingsExt.type = XR_TYPE_COMPOSITION_LAYER_SETTINGS_FB;
XrCompositionLayerSettingsExt.next = Next;
XrCompositionLayerSettingsExt.layerFlags = EyeSharpenLayerFlags_RHIThread;
Next = &XrCompositionLayerSettingsExt;
}
return Next;
}
void FLayerExtensionPlugin::SetEnableLocalDimming(bool Enable)
{
ENQUEUE_RENDER_COMMAND(OculusXR_SetEnableLocalDimming)
([this, Enable](FRHICommandListImmediate& RHICmdList) {
RHICmdList.EnqueueLambda([this, Enable](FRHICommandListImmediate& RHICmdList) {
LocalDimmingMode_RHIThread = Enable ? XR_LOCAL_DIMMING_MODE_ON_META : XR_LOCAL_DIMMING_MODE_OFF_META;
});
});
}
void FLayerExtensionPlugin::SetEyeBufferSharpenType(EOculusXREyeBufferSharpenType EyeBufferSharpenType)
{
ENQUEUE_RENDER_COMMAND(OculusXR_SetEyeBufferSharpenType)
([this, EyeBufferSharpenType](FRHICommandListImmediate& RHICmdList) {
RHICmdList.EnqueueLambda([this, EyeBufferSharpenType](FRHICommandListImmediate& RHICmdList) {
EyeSharpenLayerFlags_RHIThread = ToSharpenLayerFlag(EyeBufferSharpenType);
});
});
}
void FLayerExtensionPlugin::SetColorScaleAndOffset(FLinearColor ColorScale, FLinearColor ColorOffset, bool bApplyToAllLayers)
{
IXRTrackingSystem* TrackingSystem = OculusXR::GetOpenXRTrackingSystem();
if (TrackingSystem != nullptr)
{
IHeadMountedDisplay* Hmd = TrackingSystem->GetHMDDevice();
Hmd->SetColorScaleAndBias(ColorScale, ColorOffset);
}
#ifdef WITH_OCULUS_BRANCH
ENQUEUE_RENDER_COMMAND(OculusXR_SetColorScaleAndOffset)
([this, ColorScale, ColorOffset, bApplyToAllLayers](FRHICommandListImmediate& RHICmdList) {
RHICmdList.EnqueueLambda([this, ColorScale, ColorOffset, bApplyToAllLayers](FRHICommandListImmediate& RHICmdList) {
ColorScaleInfo_RHIThread.ColorScale = ColorScale;
ColorScaleInfo_RHIThread.ColorOffset = ColorOffset;
ColorScaleInfo_RHIThread.bApplyColorScaleAndOffsetToAllLayers = bApplyToAllLayers;
});
});
#endif
}
#ifdef WITH_OCULUS_BRANCH
static bool ShouldApplyColorScale(const XrCompositionLayerBaseHeader* Header)
{
switch (Header->type)
{
case XR_TYPE_COMPOSITION_LAYER_QUAD:
case XR_TYPE_COMPOSITION_LAYER_EQUIRECT_KHR:
case XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR:
case XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR:
return true;
break;
default:
break;
}
return false;
}
void FLayerExtensionPlugin::UpdatePixelDensity(const XrCompositionLayerBaseHeader* LayerHeader)
{
check(LayerHeader != nullptr);
if (LayerHeader->type == XR_TYPE_COMPOSITION_LAYER_PROJECTION && bPixelDensityAdaptive && bRecommendedResolutionExtensionAvailable)
{
IXRTrackingSystem* TrackingSystem = OculusXR::GetOpenXRTrackingSystem();
if (TrackingSystem != nullptr)
{
XrRecommendedLayerResolutionMETA ResolutionRecommendation = {};
ResolutionRecommendation.type = XR_TYPE_RECOMMENDED_LAYER_RESOLUTION_META;
ResolutionRecommendation.next = nullptr;
ResolutionRecommendation.isValid = false;
XrRecommendedLayerResolutionGetInfoMETA ResolutionRecommendationGetInfo = {};
ResolutionRecommendationGetInfo.type = XR_TYPE_RECOMMENDED_LAYER_RESOLUTION_GET_INFO_META;
ResolutionRecommendationGetInfo.next = nullptr;
ResolutionRecommendationGetInfo.layer = LayerHeader;
ResolutionRecommendationGetInfo.predictedDisplayTime = PredictedDisplayTime_RHIThread;
ENSURE_XRCMD(xrGetRecommendedLayerResolutionMETA.GetValue()(Session, &ResolutionRecommendationGetInfo, &ResolutionRecommendation));
if (ResolutionRecommendation.isValid == XR_TRUE)
{
AsyncTask(ENamedThreads::GameThread, [this, ResolutionRecommendation] {
RecommendedImageHeight_GameThread = ResolutionRecommendation.recommendedImageDimensions.height;
});
}
}
}
}
void FLayerExtensionPlugin::UpdateCompositionLayers(XrSession InSession, TArray<XrCompositionLayerBaseHeader*>& Headers)
{
check(IsInRenderingThread() || IsInRHIThread());
if (ColorScaleInfo_RHIThread.bApplyColorScaleAndOffsetToAllLayers)
{
ColorScale_RHIThread.Reset(Headers.Num());
}
for (const XrCompositionLayerBaseHeader* Header : Headers)
{
if (Header->type == XR_TYPE_COMPOSITION_LAYER_PROJECTION)
{
UpdatePixelDensity(Header);
}
if (ColorScaleInfo_RHIThread.bApplyColorScaleAndOffsetToAllLayers && ShouldApplyColorScale(Header))
{
ColorScale_RHIThread.AddUninitialized();
XrCompositionLayerColorScaleBiasKHR& ColorScaleBias = ColorScale_RHIThread.Last();
ColorScaleBias = { XR_TYPE_COMPOSITION_LAYER_COLOR_SCALE_BIAS_KHR };
ColorScaleBias.next = const_cast<void*>(Header->next);
ColorScaleBias.colorScale = ToXrColor4f(ColorScaleInfo_RHIThread.ColorScale);
ColorScaleBias.colorBias = ToXrColor4f(ColorScaleInfo_RHIThread.ColorOffset);
const_cast<XrCompositionLayerBaseHeader*>(Header)->next = &ColorScaleBias;
}
}
}
#endif
} // namespace OculusXR

View File

@@ -0,0 +1,71 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "khronos/openxr/openxr.h"
#include "CoreMinimal.h"
#include "OculusXRHMDTypes.h"
#include "OculusXRHMD_Settings.h"
#include "OpenXR/IOculusXRExtensionPlugin.h"
namespace OculusXR
{
class FLayerExtensionPlugin : public IOculusXRExtensionPlugin
{
public:
FLayerExtensionPlugin();
// IOpenXRExtensionPlugin
virtual bool GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
const void* OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext);
virtual const void* OnEndFrame(XrSession InSession, XrTime DisplayTime, const void* InNext) override;
virtual const void* OnEndProjectionLayer(XrSession InSession, int32 InLayerIndex, const void* InNext, XrCompositionLayerFlags& OutFlags);
virtual void PostCreateSession(XrSession InSession) override;
virtual void OnBeginRendering_GameThread(XrSession InSession) override;
#ifdef WITH_OCULUS_BRANCH
virtual float GetMaxPixelDensity() override;
#endif
#ifdef WITH_OCULUS_BRANCH
// epic branch has member as const, not usable in this case
virtual void UpdateCompositionLayers(XrSession InSession, TArray<XrCompositionLayerBaseHeader*>& Headers) override;
#endif
void SetEnableLocalDimming(bool Enable);
void SetEyeBufferSharpenType(EOculusXREyeBufferSharpenType EyeBufferSharpenType);
void SetColorScaleAndOffset(FLinearColor ColorScale, FLinearColor ColorOffset, bool bApplyToAllLayers);
private:
void UpdatePixelDensity(const XrCompositionLayerBaseHeader* LayerHeader);
XrSession Session;
bool bExtLocalDimmingAvailable;
bool bExtCompositionLayerSettingsAvailable;
bool bRecommendedResolutionExtensionAvailable;
XrLocalDimmingModeMETA LocalDimmingMode_RHIThread;
XrLocalDimmingFrameEndInfoMETA LocalDimmingExt_RHIThread;
XrCompositionLayerSettingsFlagsFB EyeSharpenLayerFlags_RHIThread;
XrCompositionLayerSettingsFB XrCompositionLayerSettingsExt;
struct FColorScaleInfo
{
FColorScaleInfo()
: ColorScale{ 1.0f, 1.0f, 1.0f, 1.0f }
, ColorOffset{ 0.0f, 0.0f, 0.0f, 0.0f }
, bApplyColorScaleAndOffsetToAllLayers(false){};
FLinearColor ColorScale;
FLinearColor ColorOffset;
bool bApplyColorScaleAndOffsetToAllLayers;
};
FColorScaleInfo ColorScaleInfo_RHIThread;
TArray<XrCompositionLayerColorScaleBiasKHR> ColorScale_RHIThread;
TArray<XrCompositionLayerBaseHeader> _HeadersStorage;
bool bPixelDensityAdaptive;
uint32_t RecommendedImageHeight_GameThread;
OculusXRHMD::FSettingsPtr Settings_GameThread;
XrTime PredictedDisplayTime_RHIThread;
float MaxPixelDensity_RenderThread;
};
} // namespace OculusXR

View File

@@ -0,0 +1,120 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRMultiPlayerStateExtensionPlugin.h"
#include "OculusXRHMDRuntimeSettings.h"
DEFINE_LOG_CATEGORY(LogOculusMultiPlayerStateExtensionPlugin);
namespace OculusXR
{
FMultiPlayerStateExtensionPlugin::FMultiPlayerStateExtensionPlugin()
{
#ifdef WITH_OCULUS_BRANCH
ResetPose();
#endif // WITH_OCULUS_BRANCH
}
FMultiPlayerStateExtensionPlugin::~FMultiPlayerStateExtensionPlugin()
{
}
void FMultiPlayerStateExtensionPlugin::SwitchPrimaryPIE(int PrimaryPIEIndex)
{
CurPlayerIndex = PrimaryPIEIndex;
}
void FMultiPlayerStateExtensionPlugin::InitMultiPlayerPoses(const FTransform& CurPose)
{
#if WITH_EDITOR && PLATFORM_WINDOWS
if (!GIsEditor || MultiPlayerPoses.Num())
{
return;
}
if (!FApp::HasVRFocus())
{
return;
}
ULevelEditorPlaySettings* PlayInSettings = GetMutableDefault<ULevelEditorPlaySettings>();
check(PlayInSettings);
int PlayNumberOfClients;
PlayInSettings->GetPlayNumberOfClients(PlayNumberOfClients);
if (PlayNumberOfClients <= 1)
{
return;
}
EPlayNetMode OutPlayNetMode;
PlayInSettings->GetPlayNetMode(OutPlayNetMode);
if (OutPlayNetMode != EPlayNetMode::PIE_Standalone)
{
// In case of non-standalone mode, server is the first player, client idx should start from 1
PlayNumberOfClients++;
}
LastFrameHMDHeadPose = CurPose;
MultiPlayerPoses.Empty();
MultiPlayerPoses.InsertDefaulted(0, PlayNumberOfClients);
for (auto& PlayerPose : MultiPlayerPoses)
{
PlayerPose = CurPose;
}
UE_LOG(LogHMD, Log, TEXT("MultiPlayer poses are initialized."));
#endif
}
#ifdef WITH_OCULUS_BRANCH
void FMultiPlayerStateExtensionPlugin::ResetPose()
{
#if WITH_EDITOR && PLATFORM_WINDOWS
CurPlayerIndex = 0;
LastFrameHMDHeadPose = FTransform::Identity;
MultiPlayerPoses.Empty();
#endif
}
void FMultiPlayerStateExtensionPlugin::ReCalcPose(FTransform& CurHMDHeadPose)
{
#if WITH_EDITOR && PLATFORM_WINDOWS
if (!GIsEditor || GetMutableDefault<UOculusXRHMDRuntimeSettings>()->MPPoseRestoreType == EOculusXRMPPoseRestoreType::Disabled)
{
return;
}
if (!MultiPlayerPoses.Num())
{
InitMultiPlayerPoses(CurHMDHeadPose);
}
if (MultiPlayerPoses.Num() <= 1)
{
return;
}
if (CurPlayerIndex >= MultiPlayerPoses.Num())
{
UE_LOG(LogHMD, Error, TEXT("CurPlayerIndex %i is larger than MultiPlayerPoses.Num() !"), CurPlayerIndex, MultiPlayerPoses.Num());
}
FTransform& PlayerPose = MultiPlayerPoses[CurPlayerIndex];
if (GetMutableDefault<UOculusXRHMDRuntimeSettings>()->MPPoseRestoreType == EOculusXRMPPoseRestoreType::PositionOnly)
{
FVector DeltaPosition = CurHMDHeadPose.GetTranslation() - LastFrameHMDHeadPose.GetTranslation();
PlayerPose.SetTranslation(PlayerPose.GetTranslation() + DeltaPosition);
LastFrameHMDHeadPose.SetTranslation(CurHMDHeadPose.GetTranslation());
CurHMDHeadPose.SetTranslation(PlayerPose.GetTranslation());
}
else
{
FTransform DeltaPose = LastFrameHMDHeadPose.Inverse() * CurHMDHeadPose;
PlayerPose = PlayerPose * DeltaPose;
LastFrameHMDHeadPose = CurHMDHeadPose;
CurHMDHeadPose = PlayerPose;
}
#endif
}
#endif // WITH_OCULUS_BRANCH
} // namespace OculusXR

View File

@@ -0,0 +1,31 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "OpenXR/IOculusXRExtensionPlugin.h"
DECLARE_LOG_CATEGORY_EXTERN(LogOculusMultiPlayerStateExtensionPlugin, Log, All);
namespace OculusXR
{
class FMultiPlayerStateExtensionPlugin : public IOculusXRExtensionPlugin
{
public:
FMultiPlayerStateExtensionPlugin();
~FMultiPlayerStateExtensionPlugin();
void SwitchPrimaryPIE(int PrimaryPIEIndex);
#ifdef WITH_OCULUS_BRANCH
virtual void ResetPose() override;
virtual void ReCalcPose(FTransform& CurHMDHeadPose) override;
#endif // WITH_OCULUS_BRANCH
private:
void InitMultiPlayerPoses(const FTransform& CurPose);
int CurPlayerIndex;
FTransform LastFrameHMDHeadPose;
TArray<FTransform> MultiPlayerPoses;
};
} // namespace OculusXR

View File

@@ -0,0 +1,28 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXROpenXRUtilities.h"
#include "RHICommandList.h"
#include "RenderingThread.h"
namespace OculusXR
{
XrResult CheckXrResult(XrResult Result, const char* Cmd)
{
if (!XR_SUCCEEDED(Result))
{
UE_LOG(LogHMD, Error, TEXT("%hs failed (%d)"), Cmd, Result);
}
return Result;
}
IXRTrackingSystem* GetOpenXRTrackingSystem()
{
static FName SystemName(TEXT("OpenXR"));
if (GEngine && GEngine->XRSystem.IsValid() && (GEngine->XRSystem->GetSystemName() == SystemName))
{
return GEngine->XRSystem.Get();
}
return nullptr;
}
} // namespace OculusXR

View File

@@ -0,0 +1,109 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "khronos/openxr/openxr.h"
#include "RHI.h"
#include "HeadMountedDisplayTypes.h"
#include "OpenXRCore.h"
#include "IXRTrackingSystem.h"
#include "Engine/Engine.h"
#define ENSURE_XRCMD(cmd) \
XR_ENSURE(cmd)
namespace OculusXR
{
#if defined(WITH_OCULUS_BRANCH)
template <typename T>
static void XRGetInstanceProcAddr(XrInstance InInstance, const char* Name, T* Function)
{
if (XR_FAILED(OpenXRDynamicAPI::xrGetInstanceProcAddr(InInstance, Name, reinterpret_cast<PFN_xrVoidFunction*>(Function))))
{
UE_LOG(LogHMD, Fatal, TEXT("Failed to bind OpenXR entry %s."), ANSI_TO_TCHAR(Name));
}
}
template <typename T>
static void XRGetInstanceProcAddr(XrInstance InInstance, const char* Name, TOptional<T>* Function)
{
if (XR_FAILED(OpenXRDynamicAPI::xrGetInstanceProcAddr(InInstance, Name, reinterpret_cast<PFN_xrVoidFunction*>(Function))))
{
UE_LOG(LogHMD, Warning, TEXT("Unable to bind optional OpenXR entry %s."), ANSI_TO_TCHAR(Name));
}
}
static void XRAppendToChain(XrBaseOutStructure* ToAppend, XrBaseOutStructure* Chain)
{
while (Chain->next != XR_NULL_HANDLE)
{
if (Chain->next == ToAppend)
{
return;
}
Chain = Chain->next;
}
Chain->next = ToAppend;
}
#else
// Don't call xrGetInstanceProcAddr on marketplace build. This prevents the linker
// from adding a dependency on OpenXr. On the marketplace build OpenXR loading is
// currently disabled
template <typename T>
static void XRGetInstanceProcAddr(XrInstance InInstance, const char* Name, T* Function)
{
(void)InInstance;
(void)Name;
(void)Function;
UE_LOG(LogHMD, Warning, TEXT("Failed to bind OpenXR entry %s."), ANSI_TO_TCHAR(Name));
}
template <typename T>
static void XRGetInstanceProcAddr(XrInstance InInstance, const char* Name, TOptional<T>* Function)
{
(void)InInstance;
(void)Name;
(void)Function;
UE_LOG(LogHMD, Warning, TEXT("Unable to bind optional OpenXR entry %s."), ANSI_TO_TCHAR(Name));
}
static void XRAppendToChain(XrBaseOutStructure* ToAppend, XrBaseOutStructure* Chain)
{
(void)ToAppend;
(void)Chain;
UE_LOG(LogHMD, Warning, TEXT("Unable to append structure to structure chain. Appending Type: %d -- Base chain Type: %d"), ToAppend->type, Chain->type);
}
#endif
XrResult CheckXrResult(XrResult res, const char* cmd);
static inline double FromXrDuration(const XrDuration duration)
{
return (duration * 1e-9);
}
static inline XrDuration ToXrDuration(const double duration)
{
return (duration * 1e9);
}
static inline double FromXrTime(const XrTime time)
{
return (time * 1e-9);
}
static inline XrTime ToXrTime(const double time)
{
return (time * 1e9);
}
static bool IsOpenXRSystem()
{
const FName SystemName(TEXT("OpenXR"));
return GEngine->XRSystem.IsValid() && (GEngine->XRSystem->GetSystemName() == SystemName);
}
IXRTrackingSystem* GetOpenXRTrackingSystem();
} // namespace OculusXR

View File

@@ -0,0 +1,244 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRPerformanceExtensionPlugin.h"
#include "OculusXRXRFunctions.h"
#include "OculusXROpenXRUtilities.h"
#include "OculusXRHMDRuntimeSettings.h"
#include "OpenXRCore.h"
DEFINE_LOG_CATEGORY(LogOculusPerformanceExtensionPlugin);
namespace OculusXR
{
namespace // anonymous
{
XrPerfSettingsLevelEXT ToXrPerfSettingsLevel(EOculusXRProcessorPerformanceLevel PerformanceLevel)
{
switch (PerformanceLevel)
{
case EOculusXRProcessorPerformanceLevel::PowerSavings:
return XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT;
case EOculusXRProcessorPerformanceLevel::SustainedLow:
return XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT;
case EOculusXRProcessorPerformanceLevel::SustainedHigh:
return XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT;
case EOculusXRProcessorPerformanceLevel::Boost:
return XR_PERF_SETTINGS_LEVEL_BOOST_EXT;
default:
check(false);
}
return XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT;
}
static constexpr int MaxCPUCores = CPUCoreLast - CPUCoreStart;
} // namespace
FPerformanceExtensionPlugin::FPerformanceExtensionPlugin()
: Instance(XR_NULL_HANDLE)
, bPerfSettingsInitialized(false)
, bPerfLevelsChanged(false)
, CpuPerfLevel(EOculusXRProcessorPerformanceLevel::PowerSavings)
, GpuPerfLevel(EOculusXRProcessorPerformanceLevel::PowerSavings)
, MetricsUpdateMask(0)
, PathPerformanceMetricsMap{}
, PerformanceMetrics{}
, PerformanceMetricsMask(0)
{
PerformanceMetrics.CpuCoreUtil.Init(0, MaxCPUCores);
}
bool FPerformanceExtensionPlugin::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
{
return true;
}
bool FPerformanceExtensionPlugin::GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions)
{
OutExtensions.Add(XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME);
OutExtensions.Add(XR_META_PERFORMANCE_METRICS_EXTENSION_NAME);
return true;
}
void FPerformanceExtensionPlugin::PostCreateSession(XrSession InSession)
{
const UOculusXRHMDRuntimeSettings* HMDSettings = GetDefault<UOculusXRHMDRuntimeSettings>();
LoadFromSettings();
InitializePerformanceMetrics(InSession);
}
void* FPerformanceExtensionPlugin::OnWaitFrame(XrSession InSession, void* InNext)
{
UpdatePerformanceLevels(InSession);
UpdatePerformanceMetrics(InSession);
return InNext;
}
void FPerformanceExtensionPlugin::PostCreateInstance(XrInstance InInstance)
{
Instance = InInstance;
}
void FPerformanceExtensionPlugin::UpdatePerformanceLevels(XrSession InSession)
{
if (bPerfLevelsChanged && xrPerfSettingsSetPerformanceLevelEXT.IsSet() && xrPerfSettingsSetPerformanceLevelEXT.GetValue() != nullptr)
{
UE_LOG(LogOculusPerformanceExtensionPlugin, Log, TEXT("Oculus OpenXR SetPerformanceLevel CPU=%d, GPU=%d"), CpuPerfLevel, GpuPerfLevel);
ENSURE_XRCMD(xrPerfSettingsSetPerformanceLevelEXT.GetValue()(InSession, XR_PERF_SETTINGS_DOMAIN_CPU_EXT, ToXrPerfSettingsLevel(CpuPerfLevel)));
ENSURE_XRCMD(xrPerfSettingsSetPerformanceLevelEXT.GetValue()(InSession, XR_PERF_SETTINGS_DOMAIN_GPU_EXT, ToXrPerfSettingsLevel(GpuPerfLevel)));
bPerfLevelsChanged = false;
}
}
void FPerformanceExtensionPlugin::GetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel& CpuLevel, EOculusXRProcessorPerformanceLevel& GpuLevel)
{
if (!bPerfSettingsInitialized)
{
LoadFromSettings();
}
CpuLevel = CpuPerfLevel;
GpuLevel = GpuPerfLevel;
}
void FPerformanceExtensionPlugin::SetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel CpuLevel, EOculusXRProcessorPerformanceLevel GpuLevel)
{
if (CpuPerfLevel != CpuLevel || GpuPerfLevel != GpuLevel)
{
CpuPerfLevel = CpuLevel;
GpuPerfLevel = GpuLevel;
bPerfLevelsChanged = true;
}
bPerfSettingsInitialized = true;
}
void FPerformanceExtensionPlugin::LoadFromSettings()
{
const UOculusXRHMDRuntimeSettings* HMDSettings = GetDefault<UOculusXRHMDRuntimeSettings>();
SetSuggestedCpuAndGpuPerformanceLevels(HMDSettings->SuggestedCpuPerfLevel, HMDSettings->SuggestedGpuPerfLevel);
}
void FPerformanceExtensionPlugin::InitializePerformanceMetrics(XrSession InSession)
{
if (xrEnumeratePerformanceMetricsCounterPathsMETA.GetValue() == 0 || xrSetPerformanceMetricsStateMETA.GetValue() == 0 || xrQueryPerformanceMetricsCounterMETA.GetValue() == 0)
{
return;
}
TArray<XrPath> PerfMetricsCounterPaths;
uint32 NumPerfMetricsCounterPaths = 0;
XrPerformanceMetricsStateMETA MetricsState = { XR_TYPE_PERFORMANCE_METRICS_STATE_META };
MetricsState.enabled = XR_TRUE;
ENSURE_XRCMD(xrSetPerformanceMetricsStateMETA.GetValue()(InSession, &MetricsState));
ENSURE_XRCMD(xrEnumeratePerformanceMetricsCounterPathsMETA.GetValue()(Instance, 0, &NumPerfMetricsCounterPaths, nullptr));
PerfMetricsCounterPaths.SetNum(NumPerfMetricsCounterPaths);
ENSURE_XRCMD(xrEnumeratePerformanceMetricsCounterPathsMETA.GetValue()(Instance, NumPerfMetricsCounterPaths, &NumPerfMetricsCounterPaths, PerfMetricsCounterPaths.GetData()));
TSet<XrPath> PerfMetricsCounterPathHash;
for (auto& Path : PerfMetricsCounterPaths)
{
PerfMetricsCounterPathHash.Emplace(Path);
}
auto AddMetrics = [&](FString Name, EPerformanceMetricsType Metric) {
const FOpenXRPath MetricsPath(Name);
if (PerfMetricsCounterPathHash.Contains(MetricsPath))
{
PathPerformanceMetricsMap.Add(MetricsPath, Metric);
PerformanceMetricsMask |= static_cast<uint64>(1) << Metric;
UE_LOG(LogOculusPerformanceExtensionPlugin, Log, TEXT("Supported Performance Metrics: %s"), *Name);
}
};
AddMetrics(TEXT("/perfmetrics_meta/app/cpu_frametime"), AppCPUTimeFloat);
AddMetrics(TEXT("/perfmetrics_meta/app/gpu_frametime"), AppGPUTimeFloat);
AddMetrics(TEXT("/perfmetrics_meta/compositor/cpu_frametime"), CompositorCPUTimeFloat);
AddMetrics(TEXT("/perfmetrics_meta/compositor/gpu_frametime"), CompositorGPUTimeFloat);
AddMetrics(TEXT("/perfmetrics_meta/compositor/dropped_frame_count"), CompositorDroppedFramesInt);
AddMetrics(TEXT("/perfmetrics_meta/compositor/spacewarp_mode"), CompositorSpaceWarpModeInt);
AddMetrics(TEXT("/perfmetrics_meta/device/gpu_utilization"), GPUUtilizationFloat);
AddMetrics(TEXT("/perfmetrics_meta/device/cpu_utilization_average"), GPUUtilizationAverageFloat);
AddMetrics(TEXT("/perfmetrics_meta/device/cpu_utilization_worst"), GPUUtilizationWorstFloat);
for (uint32 CPUId = 0; CPUId < MaxCPUCores; ++CPUId)
{
const FString PerCpuCounterPathString = FString::Printf(TEXT("/perfmetrics_meta/device/cpu%d_utilization"), CPUId);
AddMetrics(PerCpuCounterPathString,
static_cast<EPerformanceMetricsType>(CPUCore0UtilizationFloat + CPUId));
}
}
bool FPerformanceExtensionPlugin::IsPerformanceMetricsSupported(EPerformanceMetricsType Metric) const
{
const uint64_t Val = static_cast<uint64>(1) << Metric;
return ((PerformanceMetricsMask & Val) != 0);
}
void FPerformanceExtensionPlugin::UpdatePerformanceMetrics(XrSession InSession)
{
if ((xrQueryPerformanceMetricsCounterMETA.GetValue() == 0) || (PerformanceMetricsMask == 0))
{
return;
}
for (auto& Pair : PathPerformanceMetricsMap)
{
const XrPath Path = Pair.Key;
const EPerformanceMetricsType Metric = Pair.Value;
XrPerformanceMetricsCounterMETA Counter = { XR_TYPE_PERFORMANCE_METRICS_COUNTER_META };
ENSURE_XRCMD(xrQueryPerformanceMetricsCounterMETA.GetValue()(InSession, Path, &Counter));
switch (Metric)
{
case AppCPUTimeFloat:
PerformanceMetrics.AppCpuTime = Counter.floatValue;
break;
case AppGPUTimeFloat:
PerformanceMetrics.AppGpuTime = Counter.floatValue;
break;
case CompositorCPUTimeFloat:
PerformanceMetrics.ComCpuTime = Counter.floatValue;
break;
case CompositorGPUTimeFloat:
PerformanceMetrics.ComGpuTime = Counter.floatValue;
break;
case CompositorDroppedFramesInt:
PerformanceMetrics.DroppedFrames = Counter.uintValue;
break;
case CompositorSpaceWarpModeInt:
PerformanceMetrics.ComSpaceWarpMode = Counter.uintValue;
break;
case GPUUtilizationFloat:
PerformanceMetrics.GpuUtil = Counter.floatValue;
break;
case GPUUtilizationAverageFloat:
PerformanceMetrics.CpuUtilAvg = Counter.floatValue;
break;
case GPUUtilizationWorstFloat:
PerformanceMetrics.CpuUtilWorst = Counter.floatValue;
break;
default:
{
if (Metric >= CPUCore0UtilizationFloat && Metric < CPUCore0UtilizationFloat + MaxCPUCores)
{
const int32 CpuCoreIdx = Metric - CPUCore0UtilizationFloat;
check(CpuCoreIdx < PerformanceMetrics.CpuCoreUtil.Num());
PerformanceMetrics.CpuCoreUtil[CpuCoreIdx] = Counter.floatValue / 100.0f;
}
else
{
UE_LOG(LogOculusPerformanceExtensionPlugin, Log, TEXT("Unsupported Metric: %d"), static_cast<uint32>(Metric));
}
}
break;
}
}
}
const FOculusXRPerformanceMetrics& FPerformanceExtensionPlugin::GetPerformanceMetrics() const
{
return PerformanceMetrics;
}
} // namespace OculusXR

View File

@@ -0,0 +1,70 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "OculusXRHMDTypes.h"
#include "OpenXR/IOculusXRExtensionPlugin.h"
DECLARE_LOG_CATEGORY_EXTERN(LogOculusPerformanceExtensionPlugin, Log, All);
namespace OculusXR
{
enum EPerformanceMetricsType
{
AppCPUTimeFloat,
AppGPUTimeFloat,
CompositorCPUTimeFloat,
CompositorGPUTimeFloat,
CompositorDroppedFramesInt,
CompositorSpaceWarpModeInt,
GPUUtilizationFloat,
GPUUtilizationAverageFloat,
GPUUtilizationWorstFloat,
CPUCoreStart,
CPUCore0UtilizationFloat,
CPUCore1UtilizationFloat,
CPUCore2UtilizationFloat,
CPUCore3UtilizationFloat,
CPUCore4UtilizationFloat,
CPUCore5UtilizationFloat,
CPUCore6UtilizationFloat,
CPUCore7UtilizationFloat,
CPUCoreLast = CPUCore7UtilizationFloat,
};
class FPerformanceExtensionPlugin : public IOculusXRExtensionPlugin
{
private:
XrInstance Instance;
bool bPerfSettingsInitialized;
bool bPerfLevelsChanged;
EOculusXRProcessorPerformanceLevel CpuPerfLevel;
EOculusXRProcessorPerformanceLevel GpuPerfLevel;
uint64_t MetricsUpdateMask;
TMap<XrPath, EPerformanceMetricsType> PathPerformanceMetricsMap;
FOculusXRPerformanceMetrics PerformanceMetrics;
uint64 PerformanceMetricsMask;
public:
FPerformanceExtensionPlugin();
// IOpenXRExtensionPlugin
virtual bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
virtual bool GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
virtual void PostCreateSession(XrSession InSession) override;
virtual void PostCreateInstance(XrInstance InInstance) override;
virtual void* OnWaitFrame(XrSession InSession, void* InNext) override;
void UpdatePerformanceLevels(XrSession InSession);
void GetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel& CpuPerfLevel, EOculusXRProcessorPerformanceLevel& GpuPerfLevel);
void SetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel CpuPerfLevel, EOculusXRProcessorPerformanceLevel GpuPerfLevel);
void LoadFromSettings();
void InitializePerformanceMetrics(XrSession InSession);
bool IsPerformanceMetricsSupported(EPerformanceMetricsType Metric) const;
void UpdatePerformanceMetrics(XrSession InSession);
const FOculusXRPerformanceMetrics& GetPerformanceMetrics() const;
};
} // namespace OculusXR

View File

@@ -0,0 +1,19 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRSimulatorExtensionPlugin.h"
#include "OculusXRSimulator.h"
DEFINE_LOG_CATEGORY(LogOculusXRSimulatorPlugin);
namespace OculusXR
{
bool FXRSimulatorExtensionPlugin::GetCustomLoader(PFN_xrGetInstanceProcAddr* OutGetProcAddr)
{
#if PLATFORM_WINDOWS
FMetaXRSimulator::TryActivateOnStartup();
#endif // PLATFORM_WINDOWS
return false;
}
} // namespace OculusXR

View File

@@ -0,0 +1,18 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "OpenXR/IOculusXRExtensionPlugin.h"
DECLARE_LOG_CATEGORY_EXTERN(LogOculusXRSimulatorPlugin, Log, All);
namespace OculusXR
{
class FXRSimulatorExtensionPlugin : public IOculusXRExtensionPlugin
{
public:
// IOpenXRExtensionPlugin
virtual bool GetCustomLoader(PFN_xrGetInstanceProcAddr* OutGetProcAddr) override;
};
} // namespace OculusXR

View File

@@ -0,0 +1,386 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRSpaceWarp.h"
#include "IOpenXRHMD.h"
#include "IOpenXRHMDModule.h"
#include "IXRTrackingSystem.h"
#include "OculusXROpenXRUtilities.h"
#include "OpenXRCore.h"
#include "OpenXRHMD_Swapchain.h"
#include "StereoRenderUtils.h"
#if defined(WITH_OCULUS_BRANCH)
DEFINE_LOG_CATEGORY(LogOculusSpaceWarpExtensionPlugin);
static const int64 VELOCITY_SWAPCHAIN_WAIT_TIMEOUT = 100000000ll; // 100ms in nanoseconds.
namespace OculusXR
{
FSpaceWarpExtensionPlugin::FSpaceWarpExtensionPlugin()
{
}
bool FSpaceWarpExtensionPlugin::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
{
UE_LOG(LogOculusSpaceWarpExtensionPlugin, Warning, TEXT("FSpaceWarpExtensionPlugin::GetRequiredExtensions"));
static const auto CVarSupportMobileSpaceWarp = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("vr.SupportMobileSpaceWarp"));
if (CVarSupportMobileSpaceWarp && (CVarSupportMobileSpaceWarp->GetValueOnAnyThread() != 0))
{
OutExtensions.Add(XR_FB_SPACE_WARP_EXTENSION_NAME);
}
return true;
}
void* FSpaceWarpExtensionPlugin::OnEnumerateViewConfigurationViews(XrInstance InInstance, XrSystemId InSystem, XrViewConfigurationType InViewConfigurationType, uint32_t InViewIndex, void* InNext)
{
SelectedViewConfigurationType = InViewConfigurationType;
return InNext;
}
const void* FSpaceWarpExtensionPlugin::OnCreateInstance(IOpenXRHMDModule* InModule, const void* InNext)
{
static const auto CVarSupportMobileSpaceWarp = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("vr.SupportMobileSpaceWarp"));
if (InModule && CVarSupportMobileSpaceWarp && (CVarSupportMobileSpaceWarp->GetValueOnAnyThread() != 0))
{
bSpaceWarpExtensionEnabled = InModule->IsExtensionEnabled(XR_FB_SPACE_WARP_EXTENSION_NAME);
}
return InNext;
}
void FSpaceWarpExtensionPlugin::PostCreateInstance(XrInstance InInstance)
{
if (!bSpaceWarpExtensionEnabled)
{
return;
}
XrInstanceProperties InstanceProperties = { XR_TYPE_INSTANCE_PROPERTIES, nullptr };
XR_ENSURE(xrGetInstanceProperties(InInstance, &InstanceProperties));
InstanceProperties.runtimeName[XR_MAX_RUNTIME_NAME_SIZE - 1] = 0; // Ensure the name is null terminated.
}
void FSpaceWarpExtensionPlugin::PostCreateSession(XrSession InSession)
{
if (!bSpaceWarpExtensionEnabled)
{
return;
}
XrSystemProperties SystemProperties;
SystemProperties = XrSystemProperties{ XR_TYPE_SYSTEM_PROPERTIES, &SpaceWarpSystemProperties };
XR_ENSURE(xrGetSystemProperties(IOpenXRHMDModule::Get().GetInstance(), IOpenXRHMDModule::Get().GetSystemId(), &SystemProperties));
SpaceWarpViewExtension = FSceneViewExtensions::NewExtension<FSpaceWarpViewExtension>(this);
UE::StereoRenderUtils::FStereoShaderAspects Aspects(GMaxRHIShaderPlatform);
bIsMobileMultiViewEnabled = Aspects.IsMobileMultiViewEnabled();
}
void FSpaceWarpExtensionPlugin::OnBeginRendering_RenderThread(XrSession InSession)
{
check(IsInRenderingThread());
if (!PipelinedVelocityState_RenderThread.bEnabled)
{
return;
}
SCOPED_NAMED_EVENT(FSpaceWarpExtensionPlugin_OnBeginRendering_RenderThread, FColor::Red);
const FXRSwapChainPtr& VelocitySwapchain = PipelinedVelocityState_RenderThread.VelocitySwapchain;
const FXRSwapChainPtr& VelocityDepthSwapchain = PipelinedVelocityState_RenderThread.VelocityDepthSwapchain;
if (VelocitySwapchain)
{
VelocitySwapchain->IncrementSwapChainIndex_RHIThread();
if (VelocityDepthSwapchain)
{
VelocityDepthSwapchain->IncrementSwapChainIndex_RHIThread();
}
}
}
const void* FSpaceWarpExtensionPlugin::OnBeginProjectionView(XrSession InSession, int32 InLayerIndex, int32 InViewIndex, const void* InNext)
{
check(IsInRenderingThread());
IXRTrackingSystem* XRTrackingSystem = GetOpenXRTrackingSystem();
if (!XRTrackingSystem || !PipelinedVelocityState_RenderThread.bEnabled)
{
return InNext;
}
SCOPED_NAMED_EVENT(FSpaceWarpExtensionPlugin_OnBeginProjectionView, FColor::Red);
TSharedPtr<XrCompositionLayerSpaceWarpInfoFB> SpaceWarpLayerInfo = MakeShared<XrCompositionLayerSpaceWarpInfoFB>();
FTransform TrackingToWorld = XRTrackingSystem->GetTrackingToWorldTransform();
FTransform TrackingSpaceDeltaPose = TrackingToWorld * LastTrackingToWorld.Inverse();
LastTrackingToWorld = TrackingToWorld;
FTransform BaseTransform = FTransform(XRTrackingSystem->GetBaseOrientation(), XRTrackingSystem->GetBasePosition());
TrackingSpaceDeltaPose = BaseTransform.Inverse() * TrackingSpaceDeltaPose * BaseTransform;
SpaceWarpLayerInfo->type = XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB;
SpaceWarpLayerInfo->next = InNext;
SpaceWarpLayerInfo->layerFlags = 0;
SpaceWarpLayerInfo->appSpaceDeltaPose = ToXrPose(TrackingSpaceDeltaPose);
SpaceWarpLayerInfo->farZ = GNearClippingPlane / 100.f;
SpaceWarpLayerInfo->nearZ = INFINITY;
SpaceWarpLayerInfo->minDepth = 0.0f;
SpaceWarpLayerInfo->maxDepth = 1.0f;
FIntPoint TextureSize;
if (GetRecommendedVelocityTextureSize_RenderThread(TextureSize) && PipelinedVelocityState_RenderThread.VelocitySwapchain.IsValid())
{
FIntPoint TextureOffset(0, 0);
FIntRect ImageRect(TextureOffset, TextureOffset + TextureSize);
TSharedPtr<XrSwapchainSubImage> VelocityImage = MakeShared<XrSwapchainSubImage>();
VelocityImage->swapchain = static_cast<FOpenXRSwapchain*>(PipelinedVelocityState_RenderThread.VelocitySwapchain.Get())->GetHandle();
VelocityImage->imageArrayIndex = (bIsMobileMultiViewEnabled && InViewIndex < 2) ? InViewIndex : 0;
VelocityImage->imageRect = ToXrRect(ImageRect);
SpaceWarpLayerInfo->motionVectorSubImage = *VelocityImage;
TSharedPtr<XrSwapchainSubImage> VelocityDepthImage = nullptr;
if (PipelinedVelocityState_RenderThread.VelocityDepthSwapchain.IsValid())
{
VelocityDepthImage = MakeShared<XrSwapchainSubImage>();
VelocityDepthImage->swapchain = static_cast<FOpenXRSwapchain*>(PipelinedVelocityState_RenderThread.VelocityDepthSwapchain.Get())->GetHandle();
VelocityDepthImage->imageArrayIndex = (bIsMobileMultiViewEnabled && InViewIndex < 2) ? InViewIndex : 0;
VelocityDepthImage->imageRect = ToXrRect(ImageRect);
SpaceWarpLayerInfo->depthSubImage = *VelocityDepthImage;
}
// The SpaceWarpInfo that we return from here gets used when the layer is submitted on xrEndFrame on the RHI Thread,
// so we need to keep the XrSwapchainSubImage objects on the RHI Thread until PostEndFrame_RHIThread()
GetImmediateCommandList_ForRenderCommand().EnqueueLambda([this, SpaceWarpLayerInfo, VelocityImage, VelocityDepthImage, InViewIndex](FRHICommandList& RHICmdList) {
if (SpaceWarpLayerInfo_RHIThread.IsValidIndex(InViewIndex))
{
SpaceWarpLayerInfo_RHIThread[InViewIndex] = SpaceWarpLayerInfo;
}
if (VelocityImages_RHIThread.IsValidIndex(InViewIndex) && VelocityImage.IsValid())
{
VelocityImages_RHIThread[InViewIndex] = VelocityImage;
}
if (VelocityDepthImages_RHIThread.IsValidIndex(InViewIndex) && VelocityDepthImage.IsValid())
{
VelocityDepthImages_RHIThread[InViewIndex] = VelocityDepthImage;
}
});
return SpaceWarpLayerInfo.Get();
}
return InNext;
}
void FSpaceWarpExtensionPlugin::PostBeginFrame_RHIThread(XrTime PredictedDisplayTime)
{
check(IsRunningRHIInSeparateThread() ? IsInRHIThread() : IsInRenderingThread());
if (!PipelinedVelocityState_RHIThread.bEnabled)
{
return;
}
SCOPED_NAMED_EVENT(FSpaceWarpExtensionPlugin_PostBeginFrame_RHIThread, FColor::Red);
// We need a new swapchain image unless we've already acquired one for rendering
if (PipelinedVelocityState_RHIThread.VelocitySwapchain.IsValid())
{
PipelinedVelocityState_RHIThread.VelocitySwapchain->WaitCurrentImage_RHIThread(VELOCITY_SWAPCHAIN_WAIT_TIMEOUT);
if (PipelinedVelocityState_RHIThread.VelocityDepthSwapchain.IsValid())
{
PipelinedVelocityState_RHIThread.VelocityDepthSwapchain->WaitCurrentImage_RHIThread(VELOCITY_SWAPCHAIN_WAIT_TIMEOUT);
}
}
}
const void* FSpaceWarpExtensionPlugin::OnEndFrame(XrSession InSession, XrTime DisplayTime, const void* InNext)
{
check(IsRunningRHIInSeparateThread() ? IsInRHIThread() : IsInRenderingThread());
if (!PipelinedVelocityState_RHIThread.bEnabled)
{
return InNext;
}
SCOPED_NAMED_EVENT(FSpaceWarpExtensionPlugin_OnEndFrame, FColor::Red);
if (PipelinedVelocityState_RHIThread.VelocitySwapchain.IsValid())
{
PipelinedVelocityState_RHIThread.VelocitySwapchain->ReleaseCurrentImage_RHIThread(nullptr);
if (PipelinedVelocityState_RHIThread.VelocityDepthSwapchain.IsValid())
{
PipelinedVelocityState_RHIThread.VelocityDepthSwapchain->ReleaseCurrentImage_RHIThread(nullptr);
}
}
return InNext;
}
void FSpaceWarpExtensionPlugin::PostEndFrame_RHIThread()
{
SpaceWarpLayerInfo_RHIThread.Reset();
VelocityImages_RHIThread.Reset();
VelocityDepthImages_RHIThread.Reset();
}
void FSpaceWarpExtensionPlugin::AllocateRenderTargetTextures_RenderThread()
{
check(IsInRenderingThread());
if (!bSpaceWarpExtensionEnabled)
{
return;
}
SCOPED_NAMED_EVENT(FSpaceWarpExtensionPlugin_AllocateRenderTargetTextures_RenderThread, FColor::Red);
FIntPoint VelocitySize;
if (GetRecommendedVelocityTextureSize_RenderThread(VelocitySize))
{
IOpenXRHMD* OpenXRHMD = nullptr;
if (GEngine && GEngine->XRSystem.IsValid())
{
OpenXRHMD = GEngine->XRSystem.Get()->GetIOpenXRHMD();
if (OpenXRHMD)
{
uint8 UnusedActualFormat = 0;
const FOpenXRSwapchainProperties VelocitySwapchainProperties = {
TEXT("VelocitySwapchain"),
PF_FloatRGBA,
static_cast<uint32>(VelocitySize.X),
static_cast<uint32>(VelocitySize.Y),
static_cast<uint32>(bIsMobileMultiViewEnabled ? 2 : 1),
1,
1,
// BEGIN META SECTION - Add XR Cubemap Support
(bIsMobileMultiViewEnabled) ? ETextureDimension::Texture2DArray : ETextureDimension::Texture2D,
// END META SECTION - Add XR Cubemap Support
TexCreate_RenderTargetable | TexCreate_ResolveTargetable | TexCreate_ShaderResource | TexCreate_InputAttachmentRead | TexCreate_Dynamic,
FClearValueBinding::Transparent,
TexCreate_None
};
OpenXRHMD->AllocateSwapchainTextures_RenderThread(
VelocitySwapchainProperties,
PipelinedVelocityState_RenderThread.VelocitySwapchain,
UnusedActualFormat);
const FOpenXRSwapchainProperties VelocityDepthSwapchainProperties = {
TEXT("VelocityDepthSwapchain"),
PF_DepthStencil,
static_cast<uint32>(VelocitySize.X),
static_cast<uint32>(VelocitySize.Y),
static_cast<uint32>(bIsMobileMultiViewEnabled ? 2 : 1),
1,
1,
// BEGIN META SECTION - Add XR Cubemap Support
(bIsMobileMultiViewEnabled) ? ETextureDimension::Texture2DArray : ETextureDimension::Texture2D,
// END META SECTION - Add XR Cubemap Support
TexCreate_DepthStencilTargetable | TexCreate_ShaderResource | TexCreate_InputAttachmentRead | TexCreate_Dynamic,
FClearValueBinding::DepthZero,
TexCreate_None
};
OpenXRHMD->AllocateSwapchainTextures_RenderThread(
VelocityDepthSwapchainProperties,
PipelinedVelocityState_RenderThread.VelocityDepthSwapchain,
UnusedActualFormat);
}
}
}
}
bool FSpaceWarpExtensionPlugin::GetRecommendedVelocityTextureSize_RenderThread(FIntPoint& OutTextureSize)
{
check(IsInRenderingThread());
if (!bSpaceWarpExtensionEnabled)
{
return false;
}
OutTextureSize = FIntPoint(SpaceWarpSystemProperties.recommendedMotionVectorImageRectWidth, SpaceWarpSystemProperties.recommendedMotionVectorImageRectHeight);
return true;
}
FXRSwapChainPtr FSpaceWarpExtensionPlugin::GetVelocitySwapchain_RenderThread()
{
check(IsInRenderingThread());
if (!PipelinedVelocityState_RenderThread.bEnabled)
{
return nullptr;
}
return PipelinedVelocityState_RenderThread.VelocitySwapchain;
}
FXRSwapChainPtr FSpaceWarpExtensionPlugin::GetVelocityDepthSwapchain_RenderThread()
{
check(IsInRenderingThread());
if (!PipelinedVelocityState_RenderThread.bEnabled)
{
return nullptr;
}
return PipelinedVelocityState_RenderThread.VelocityDepthSwapchain;
}
bool FSpaceWarpExtensionPlugin::IsSpaceWarpEnabled() const
{
if (bSpaceWarpExtensionEnabled)
{
static const auto CVarOculusEnableSpaceWarp = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.Oculus.SpaceWarp.Enable"));
return CVarOculusEnableSpaceWarp && (CVarOculusEnableSpaceWarp->GetValueOnAnyThread() != 0);
}
return false;
}
FSpaceWarpExtensionPlugin::FSpaceWarpViewExtension::FSpaceWarpViewExtension(const FAutoRegister& AutoRegister, FSpaceWarpExtensionPlugin* InPlugin)
: FHMDSceneViewExtension(AutoRegister)
, SpaceWarpExtensionPlugin(InPlugin)
{
check(SpaceWarpExtensionPlugin);
}
void FSpaceWarpExtensionPlugin::FSpaceWarpViewExtension::SetupViewFamily(FSceneViewFamily& InViewFamily)
{
check(IsInGameThread())
InViewFamily.bRenderStereoVelocity = SpaceWarpExtensionPlugin->IsSpaceWarpEnabled();
}
void FSpaceWarpExtensionPlugin::FSpaceWarpViewExtension::SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView)
{
}
void FSpaceWarpExtensionPlugin::FSpaceWarpViewExtension::BeginRenderViewFamily(FSceneViewFamily& InViewFamily)
{
bool bEnabled = SpaceWarpExtensionPlugin->IsSpaceWarpEnabled();
ENQUEUE_RENDER_COMMAND(FSpaceWarpViewExtension_BeginRenderViewFamily)
([this, bEnabled](FRHICommandList& RHICmdList) {
SpaceWarpExtensionPlugin->PipelinedVelocityState_RenderThread.bEnabled = bEnabled;
FPipelinedVelocityState VelocityState = SpaceWarpExtensionPlugin->PipelinedVelocityState_RenderThread;
RHICmdList.EnqueueLambda([this, VelocityState, bEnabled](FRHICommandList& RHICmdList) {
SpaceWarpExtensionPlugin->PipelinedVelocityState_RHIThread = VelocityState;
uint32_t ViewConfigCount = 0;
XR_ENSURE(xrEnumerateViewConfigurationViews(IOpenXRHMDModule::Get().GetInstance(), IOpenXRHMDModule::Get().GetSystemId(), SpaceWarpExtensionPlugin->SelectedViewConfigurationType, 0, &ViewConfigCount, nullptr));
SpaceWarpExtensionPlugin->SpaceWarpLayerInfo_RHIThread.SetNum(ViewConfigCount);
SpaceWarpExtensionPlugin->VelocityImages_RHIThread.SetNum(ViewConfigCount);
SpaceWarpExtensionPlugin->VelocityDepthImages_RHIThread.SetNum(ViewConfigCount);
});
});
}
} // namespace OculusXR
#endif // defined(WITH_OCULUS_BRANCH)

View File

@@ -0,0 +1,84 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "SceneViewExtension.h"
#include "XRSwapChain.h"
#include "OpenXR/IOculusXRExtensionPlugin.h"
#if defined(WITH_OCULUS_BRANCH)
DECLARE_LOG_CATEGORY_EXTERN(LogOculusSpaceWarpExtensionPlugin, Log, All);
struct FOpenXRSwapchainProperties;
namespace OculusXR
{
class FSpaceWarpExtensionPlugin : public IOculusXRExtensionPlugin
{
public:
FSpaceWarpExtensionPlugin();
// IOpenXRExtensionPlugin
virtual bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
virtual void* OnEnumerateViewConfigurationViews(XrInstance InInstance, XrSystemId InSystem, XrViewConfigurationType InViewConfigurationType, uint32_t InViewIndex, void* InNext) override;
virtual const void* OnCreateInstance(IOpenXRHMDModule* InModule, const void* InNext) override;
virtual void PostCreateInstance(XrInstance InInstance) override;
virtual void PostCreateSession(XrSession InSession) override;
virtual void OnBeginRendering_RenderThread(XrSession InSession) override;
virtual const void* OnBeginProjectionView(XrSession InSession, int32 InLayerIndex, int32 InViewIndex, const void* InNext) override;
virtual void PostBeginFrame_RHIThread(XrTime PredictedDisplayTime) override;
virtual const void* OnEndFrame(XrSession InSession, XrTime DisplayTime, const void* InNext) override;
virtual void PostEndFrame_RHIThread() override;
virtual void AllocateRenderTargetTextures_RenderThread() override;
virtual bool GetRecommendedVelocityTextureSize_RenderThread(FIntPoint& OutTextureSize) override;
virtual FXRSwapChainPtr GetVelocitySwapchain_RenderThread() override;
virtual FXRSwapChainPtr GetVelocityDepthSwapchain_RenderThread() override;
bool IsSpaceWarpEnabled() const;
private:
class FSpaceWarpViewExtension : public FHMDSceneViewExtension
{
public:
FSpaceWarpViewExtension(const FAutoRegister& AutoRegister, FSpaceWarpExtensionPlugin* InPlugin);
// ISceneViewExtension
virtual void SetupViewFamily(FSceneViewFamily& InViewFamily) override;
virtual void SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) override;
virtual void BeginRenderViewFamily(FSceneViewFamily& InViewFamily) override;
private:
friend FSpaceWarpExtensionPlugin;
FSpaceWarpExtensionPlugin* SpaceWarpExtensionPlugin = nullptr;
};
struct FPipelinedVelocityState
{
FXRSwapChainPtr VelocitySwapchain;
FXRSwapChainPtr VelocityDepthSwapchain;
bool bEnabled = false;
};
TArray<TSharedPtr<XrSwapchainSubImage>> VelocityImages_RHIThread;
TArray<TSharedPtr<XrSwapchainSubImage>> VelocityDepthImages_RHIThread;
TArray<TSharedPtr<XrCompositionLayerSpaceWarpInfoFB>> SpaceWarpLayerInfo_RHIThread;
std::atomic<bool> bSpaceWarpExtensionEnabled = false;
bool bIsMobileMultiViewEnabled = false;
TSharedPtr<FSpaceWarpViewExtension> SpaceWarpViewExtension = nullptr;
FPipelinedVelocityState PipelinedVelocityState_RenderThread = {};
FPipelinedVelocityState PipelinedVelocityState_RHIThread = {};
XrViewConfigurationType SelectedViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
XrSystemSpaceWarpPropertiesFB SpaceWarpSystemProperties = { XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB };
FTransform LastTrackingToWorld = FTransform::Identity;
};
} // namespace OculusXR
#endif // defined(WITH_OCULUS_BRANCH)

View File

@@ -0,0 +1,619 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRSystemInfoExtensionPlugin.h"
#include "IOpenXRHMDModule.h"
#include "OculusXRAssetDirectory.h"
#include "OculusXRXRFunctions.h"
#include "OculusXROpenXRUtilities.h"
#include "OculusXRHMDRuntimeSettings.h"
DEFINE_LOG_CATEGORY(LogOculusSystemInfoExtensionPlugin);
namespace OculusXR
{
enum EOculusAsset
{
LeftTouchRiftS,
RightTouchRiftS,
LeftTouchQuest2,
RightTouchQuest2,
LeftTouchQuestPro,
RightTouchQuestPro,
LeftTouchQuest3,
RightTouchQuest3,
OculusAssetTotal
};
struct FRenderableDevice
{
FString Device;
EOculusXRDeviceType MinDeviceRange;
EOculusXRDeviceType MaxDeviceRange;
FSoftObjectPath MeshAssetRef;
};
static FRenderableDevice RenderableDevices[] = {
#if PLATFORM_ANDROID
// Quest 1 & 2
{ "/user/hand/left", EOculusXRDeviceType::OculusQuest_Deprecated, EOculusXRDeviceType::OculusQuest2, FOculusAssetDirectory::AssetListing[LeftTouchQuest2] },
{ "/user/hand/right", EOculusXRDeviceType::OculusQuest_Deprecated, EOculusXRDeviceType::OculusQuest2, FOculusAssetDirectory::AssetListing[RightTouchQuest2] },
// Quest Pro
{ "/user/hand/left", EOculusXRDeviceType::MetaQuestPro, EOculusXRDeviceType::MetaQuestPro, FOculusAssetDirectory::AssetListing[LeftTouchQuestPro] },
{ "/user/hand/right", EOculusXRDeviceType::MetaQuestPro, EOculusXRDeviceType::MetaQuestPro, FOculusAssetDirectory::AssetListing[RightTouchQuestPro] },
// Quest 3 & 3s
{ "/user/hand/left", EOculusXRDeviceType::MetaQuest3, EOculusXRDeviceType::MetaQuest3S, FOculusAssetDirectory::AssetListing[LeftTouchQuest3] },
{ "/user/hand/right", EOculusXRDeviceType::MetaQuest3, EOculusXRDeviceType::MetaQuest3S, FOculusAssetDirectory::AssetListing[RightTouchQuest3] },
#else
// PC - Rift S
{ "/user/hand/left", EOculusXRDeviceType::Rift_S, EOculusXRDeviceType::Rift_S, FOculusAssetDirectory::AssetListing[LeftTouchRiftS] },
{ "/user/hand/right", EOculusXRDeviceType::Rift_S, EOculusXRDeviceType::Rift_S, FOculusAssetDirectory::AssetListing[RightTouchRiftS] },
// PC - Quest 1 & 2
{ "/user/hand/left", EOculusXRDeviceType::Quest_Link_Deprecated, EOculusXRDeviceType::Quest2_Link, FOculusAssetDirectory::AssetListing[LeftTouchQuest2] },
{ "/user/hand/right", EOculusXRDeviceType::Quest_Link_Deprecated, EOculusXRDeviceType::Quest2_Link, FOculusAssetDirectory::AssetListing[RightTouchQuest2] },
// PC - Quest Pro
{ "/user/hand/left", EOculusXRDeviceType::MetaQuestProLink, EOculusXRDeviceType::MetaQuestProLink, FOculusAssetDirectory::AssetListing[LeftTouchQuestPro] },
{ "/user/hand/right", EOculusXRDeviceType::MetaQuestProLink, EOculusXRDeviceType::MetaQuestProLink, FOculusAssetDirectory::AssetListing[RightTouchQuestPro] },
// Quest 3 & 3s
{ "/user/hand/left", EOculusXRDeviceType::MetaQuest3Link, EOculusXRDeviceType::MetaQuest3SLink, FOculusAssetDirectory::AssetListing[LeftTouchQuest3] },
{ "/user/hand/right", EOculusXRDeviceType::MetaQuest3Link, EOculusXRDeviceType::MetaQuest3SLink, FOculusAssetDirectory::AssetListing[RightTouchQuest3] },
#endif
};
static uint32 RenderableDeviceCount = sizeof(RenderableDevices) / sizeof(RenderableDevices[0]);
FSoftObjectPath FindDeviceMesh(FString DevicePath, EOculusXRDeviceType SystemDeviceType)
{
if (DevicePath == "")
{
return NULL;
}
for (uint32 DeviceIndex = 0; DeviceIndex < RenderableDeviceCount; ++DeviceIndex)
{
const FRenderableDevice& RenderableDevice = RenderableDevices[DeviceIndex];
if (RenderableDevice.Device == DevicePath)
{
// If we have information about the current headset, load the model based of the headset information, otherwise load defaults.
if (SystemDeviceType != EOculusXRDeviceType::OculusUnknown)
{
if (SystemDeviceType >= RenderableDevice.MinDeviceRange && SystemDeviceType <= RenderableDevice.MaxDeviceRange)
{
return RenderableDevice.MeshAssetRef;
break;
}
}
else
{
return RenderableDevice.MeshAssetRef;
}
}
}
return NULL;
}
namespace // anonymous
{
void AppendToChain(XrBaseOutStructure* ToAppend, XrBaseOutStructure* Chain)
{
while (Chain->next != XR_NULL_HANDLE)
{
if (Chain->next == ToAppend)
{
return;
}
Chain = Chain->next;
}
Chain->next = ToAppend;
}
bool IsEqualUuid(const XrUuidEXT& a, const XrUuidEXT& b)
{
return FMemory::Memcmp(&a, &b, sizeof(XrUuidEXT)) == 0;
}
XrColorSpaceFB ToXrColorSpace(EOculusXRColorSpace ColorSpace)
{
XrColorSpaceFB XrColorSpace = XR_COLOR_SPACE_UNMANAGED_FB;
switch (ColorSpace)
{
case EOculusXRColorSpace::Unmanaged:
XrColorSpace = XR_COLOR_SPACE_UNMANAGED_FB;
break;
case EOculusXRColorSpace::Rec_2020:
XrColorSpace = XR_COLOR_SPACE_REC2020_FB;
break;
case EOculusXRColorSpace::Rec_709:
XrColorSpace = XR_COLOR_SPACE_REC709_FB;
break;
case EOculusXRColorSpace::Rift_CV1:
XrColorSpace = XR_COLOR_SPACE_RIFT_CV1_FB;
break;
case EOculusXRColorSpace::Rift_S:
XrColorSpace = XR_COLOR_SPACE_RIFT_S_FB;
break;
case EOculusXRColorSpace::Quest:
XrColorSpace = XR_COLOR_SPACE_QUEST_FB;
break;
case EOculusXRColorSpace::P3:
XrColorSpace = XR_COLOR_SPACE_P3_FB;
break;
case EOculusXRColorSpace::Adobe_RGB:
XrColorSpace = XR_COLOR_SPACE_ADOBE_RGB_FB;
break;
}
return XrColorSpace;
}
EOculusXRColorSpace ToOculusXRColorSpace(XrColorSpaceFB InXrColorSpace)
{
EOculusXRColorSpace ColorSpace = EOculusXRColorSpace::Unknown;
switch (InXrColorSpace)
{
case XR_COLOR_SPACE_UNMANAGED_FB:
ColorSpace = EOculusXRColorSpace::Unmanaged;
break;
case XR_COLOR_SPACE_REC2020_FB:
ColorSpace = EOculusXRColorSpace::Rec_2020;
break;
case XR_COLOR_SPACE_REC709_FB:
ColorSpace = EOculusXRColorSpace::Rec_709;
break;
case XR_COLOR_SPACE_RIFT_CV1_FB:
ColorSpace = EOculusXRColorSpace::Rift_CV1;
break;
case XR_COLOR_SPACE_RIFT_S_FB:
ColorSpace = EOculusXRColorSpace::Rift_S;
break;
case XR_COLOR_SPACE_QUEST_FB:
ColorSpace = EOculusXRColorSpace::Quest;
break;
case XR_COLOR_SPACE_P3_FB:
ColorSpace = EOculusXRColorSpace::P3;
break;
case XR_COLOR_SPACE_ADOBE_RGB_FB:
ColorSpace = EOculusXRColorSpace::Adobe_RGB;
break;
default:
ColorSpace = EOculusXRColorSpace::Unknown;
}
return ColorSpace;
}
} // namespace
FSystemInfoExtensionPlugin::FSystemInfoExtensionPlugin()
: Instance(XR_NULL_HANDLE)
, Session(XR_NULL_HANDLE)
, SystemId(XR_NULL_SYSTEM_ID)
, bExtHeadsetIdAvailable(false)
, SystemHeadsetId{}
, bSystemHeadsetIdValid(false)
, SystemProductName{}
, bExtDisplayRefreshAvailible(false)
, bExtColorspaceAvailable(false)
, bExtPassthroughAvailable(false)
, bExtPassthroughPreferencesAvailable(false)
{
}
bool FSystemInfoExtensionPlugin::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
{
return true;
}
bool FSystemInfoExtensionPlugin::GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions)
{
OutExtensions.Add(XR_META_HEADSET_ID_EXTENSION_NAME);
OutExtensions.Add(XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME);
OutExtensions.Add(XR_FB_COLOR_SPACE_EXTENSION_NAME);
OutExtensions.Add(XR_META_PASSTHROUGH_PREFERENCES_EXTENSION_NAME);
return true;
}
void FSystemInfoExtensionPlugin::PostCreateInstance(XrInstance InInstance)
{
Instance = InInstance;
}
const void* FSystemInfoExtensionPlugin::OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext)
{
if (InModule != nullptr)
{
bExtHeadsetIdAvailable = InModule->IsExtensionEnabled(XR_META_HEADSET_ID_EXTENSION_NAME);
bExtDisplayRefreshAvailible = InModule->IsExtensionEnabled(XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME);
bExtColorspaceAvailable = InModule->IsExtensionEnabled(XR_FB_COLOR_SPACE_EXTENSION_NAME);
bExtPassthroughAvailable = InModule->IsExtensionEnabled(XR_FB_PASSTHROUGH_EXTENSION_NAME);
bExtPassthroughPreferencesAvailable = InModule->IsExtensionEnabled(XR_META_PASSTHROUGH_PREFERENCES_EXTENSION_NAME);
}
return IOculusXRExtensionPlugin::OnCreateInstance(InModule, InNext);
}
void FSystemInfoExtensionPlugin::PostCreateSession(XrSession InSession)
{
Session = InSession;
XrSystemGetInfo SystemGetInfo = { XR_TYPE_SYSTEM_GET_INFO };
SystemGetInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
ENSURE_XRCMD(xrGetSystem(Instance, &SystemGetInfo, &SystemId));
XrSystemHeadsetIdPropertiesMETA SystemHeadsetIdProperties = { XR_TYPE_SYSTEM_HEADSET_ID_PROPERTIES_META };
XrSystemProperties SystemProperties = { XR_TYPE_SYSTEM_PROPERTIES };
if (bExtHeadsetIdAvailable)
{
AppendToChain(
reinterpret_cast<XrBaseOutStructure*>(&SystemHeadsetIdProperties),
reinterpret_cast<XrBaseOutStructure*>(&SystemProperties));
}
ENSURE_XRCMD(xrGetSystemProperties(Instance, SystemId, &SystemProperties));
if (bExtHeadsetIdAvailable)
{
SystemHeadsetId = SystemHeadsetIdProperties.id;
bSystemHeadsetIdValid = true;
}
SystemProductName = SystemProperties.systemName;
SystemDeviceType = GetSystemHeadsetType();
const UOculusXRHMDRuntimeSettings* HMDSettings = GetDefault<UOculusXRHMDRuntimeSettings>();
SetColorSpace(HMDSettings->ColorSpace);
ControllerPaths.TouchControllerPath = FOpenXRPath("/interaction_profiles/oculus/touch_controller");
ControllerPaths.TouchControllerProPath = FOpenXRPath("/interaction_profiles/facebook/touch_controller_pro");
ControllerPaths.TouchControllerPlusPath = FOpenXRPath("/interaction_profiles/meta/touch_controller_plus");
ControllerPaths.LeftHandPath = FOpenXRPath("/user/hand/left");
ControllerPaths.RightHandPath = FOpenXRPath("/user/hand/right");
}
bool FSystemInfoExtensionPlugin::GetControllerModel(XrInstance InInstance, XrPath InInteractionProfile, XrPath InDevicePath, FSoftObjectPath& OutPath)
{
if (InDevicePath == XR_NULL_PATH)
{
return false;
}
const FString DevicePath = FOpenXRPath(InDevicePath).ToString();
OutPath = FindDeviceMesh(DevicePath, SystemDeviceType);
return OutPath.IsValid();
}
void FSystemInfoExtensionPlugin::GetControllerModelsForCooking(TArray<FSoftObjectPath>& OutPaths)
{
OutPaths.Append(FOculusAssetDirectory::AssetListing, OculusAssetTotal);
}
EOculusXRDeviceType FSystemInfoExtensionPlugin::GetDeviceType()
{
return SystemDeviceType;
}
EOculusXRDeviceType FSystemInfoExtensionPlugin::GetSystemHeadsetType()
{
// Magic legacy conversion paths from ovrplugin
#if defined(__ANDROID__)
constexpr XrUuidEXT XR_HEADSET_ID_QUEST = {
{ 0x4c, 0xa2, 0xf7, 0x94, 0xaf, 0x6a, 0x46, 0x9d, 0xbb, 0xe5, 0x1f, 0x7f, 0xce, 0xef, 0x7b, 0xad }
};
constexpr XrUuidEXT XR_HEADSET_ID_QUEST2 = {
{ 0x30, 0x64, 0xf2, 0x39, 0x15, 0xed, 0x47, 0x22, 0xa6, 0x59, 0x5e, 0xa1, 0xec, 0xd8, 0x99, 0xc2 }
};
// Quest Pro
constexpr XrUuidEXT XR_HEADSET_ID_QUEST_PRO = {
{ 0xb5, 0x84, 0x43, 0xc9, 0xdf, 0x86, 0x40, 0xcd, 0x89, 0x18, 0x2a, 0x27, 0x8e, 0x18, 0xab, 0x71 }
};
// Quest 3
constexpr XrUuidEXT XR_HEADSET_ID_QUEST_3 = {
{ 0x08, 0x27, 0x46, 0xba, 0xa0, 0x71, 0x4c, 0xf7, 0x8f, 0x87, 0xba, 0xde, 0x5c, 0xce, 0x43, 0xce }
};
// Quest 3S
constexpr XrUuidEXT XR_HEADSET_ID_QUEST_3S = {
{ 0x95, 0x10, 0x49, 0x2b, 0x0d, 0x42, 0x48, 0x72, 0xbc, 0xf4, 0xfb, 0x1a, 0x8d, 0xef, 0x6f, 0x0e }
};
#else
// PC Headsets
constexpr XrUuidEXT XR_HEADSET_ID_RIFT_CV1 = {
{ 0xbe, 0x4f, 0x8d, 0x7c, 0x8e, 0x33, 0x4d, 0xa2, 0xbf, 0x54, 0xda, 0xd9, 0xea, 0x57, 0x54, 0x9f }
};
constexpr XrUuidEXT XR_HEADSET_ID_RIFT_S = {
{ 0x0d, 0x42, 0xaa, 0xc6, 0x7f, 0x1c, 0x46, 0x27, 0x9c, 0xc5, 0x2c, 0xba, 0x8b, 0x0b, 0xaf, 0xfc }
};
constexpr XrUuidEXT XR_HEADSET_ID_QUEST_LINK = {
{ 0x2f, 0x03, 0x26, 0xc0, 0x09, 0xeb, 0x4d, 0xa9, 0x9a, 0x88, 0x4c, 0xc2, 0x93, 0x53, 0x0a, 0xb2 }
};
constexpr XrUuidEXT XR_HEADSET_ID_QUEST2_LINK = {
{ 0x52, 0x50, 0x78, 0xa5, 0x5a, 0xab, 0x4d, 0xc9, 0xb8, 0x02, 0x1e, 0xbd, 0x6c, 0x7c, 0xff, 0xf4 }
};
// Quest Pro
constexpr XrUuidEXT XR_HEADSET_ID_QUEST_PRO_LINK = {
{ 0x99, 0x09, 0x86, 0xc7, 0x9b, 0xad, 0x47, 0x5f, 0x89, 0x83, 0xc1, 0xc7, 0xbd, 0x49, 0xad, 0x51 }
};
// Quest 3
constexpr XrUuidEXT XR_HEADSET_ID_QUEST_3_LINK = {
{ 0x64, 0x09, 0xe3, 0xb3, 0x50, 0x89, 0x4e, 0xd6, 0x86, 0x8e, 0xaa, 0xed, 0x82, 0xda, 0x36, 0x76 }
};
// Quest 3S
constexpr XrUuidEXT XR_HEADSET_ID_QUEST_3S_LINK = {
{ 0x8c, 0x2e, 0xc4, 0x47, 0xac, 0x73, 0x41, 0x9f, 0x90, 0x96, 0x36, 0xa7, 0x72, 0x33, 0x78, 0x62 }
};
#endif // defined(__ANDROID__)
#if defined(__ANDROID__)
if (bSystemHeadsetIdValid)
{
UE_LOG(LogOculusSystemInfoExtensionPlugin, Log, TEXT("UID %x %x %x %x - %x %x %x %x - %x %x %x %x - %x %x %x %x"),
SystemHeadsetId.data[0],
SystemHeadsetId.data[1],
SystemHeadsetId.data[2],
SystemHeadsetId.data[3],
SystemHeadsetId.data[4],
SystemHeadsetId.data[5],
SystemHeadsetId.data[6],
SystemHeadsetId.data[7],
SystemHeadsetId.data[8],
SystemHeadsetId.data[9],
SystemHeadsetId.data[10],
SystemHeadsetId.data[11],
SystemHeadsetId.data[12],
SystemHeadsetId.data[13],
SystemHeadsetId.data[14],
SystemHeadsetId.data[15]);
if (IsEqualUuid(SystemHeadsetId, XR_HEADSET_ID_QUEST))
{
return EOculusXRDeviceType::OculusQuest_Deprecated;
}
else if (IsEqualUuid(SystemHeadsetId, XR_HEADSET_ID_QUEST2))
{
return EOculusXRDeviceType::OculusQuest2;
}
else if (IsEqualUuid(SystemHeadsetId, XR_HEADSET_ID_QUEST_PRO))
{
return EOculusXRDeviceType::MetaQuestPro;
}
else if (IsEqualUuid(SystemHeadsetId, XR_HEADSET_ID_QUEST_3))
{
return EOculusXRDeviceType::MetaQuest3;
}
else if (IsEqualUuid(SystemHeadsetId, XR_HEADSET_ID_QUEST_3S))
{
return EOculusXRDeviceType::MetaQuest3S;
}
else
{
return EOculusXRDeviceType::OculusQuest2; // return Quest 2 as the default headset type
}
}
else
{
if (SystemProductName == TEXT("Oculus Quest"))
{
return EOculusXRDeviceType::OculusQuest_Deprecated;
}
else if (SystemProductName == TEXT("Oculus Quest2"))
{
return EOculusXRDeviceType::OculusQuest2;
}
else if (SystemProductName == TEXT("Meta Quest Pro") || SystemProductName == TEXT("Oculus Headset1"))
{
return EOculusXRDeviceType::MetaQuestPro;
}
else if (SystemProductName == TEXT("Meta Quest 3") || SystemProductName == TEXT("Oculus Headset2"))
{
return EOculusXRDeviceType::MetaQuest3;
}
else if (SystemProductName == TEXT("Meta Quest 3S") || SystemProductName == TEXT("Oculus Headset3"))
{
return EOculusXRDeviceType::MetaQuest3S;
}
else
{
return EOculusXRDeviceType::OculusQuest2; // return Quest 2 as the default headset type
}
}
#else
if (bSystemHeadsetIdValid)
{
if (IsEqualUuid(SystemHeadsetId, XR_HEADSET_ID_RIFT_CV1))
{
return EOculusXRDeviceType::Rift;
}
else if (IsEqualUuid(SystemHeadsetId, XR_HEADSET_ID_RIFT_S))
{
return EOculusXRDeviceType::Rift_S;
}
else if (IsEqualUuid(SystemHeadsetId, XR_HEADSET_ID_QUEST_LINK))
{
return EOculusXRDeviceType::Quest_Link_Deprecated;
}
else if (IsEqualUuid(SystemHeadsetId, XR_HEADSET_ID_QUEST2_LINK))
{
return EOculusXRDeviceType::Quest2_Link;
}
else if (IsEqualUuid(SystemHeadsetId, XR_HEADSET_ID_QUEST_PRO_LINK))
{
return EOculusXRDeviceType::MetaQuestProLink;
}
else if (IsEqualUuid(SystemHeadsetId, XR_HEADSET_ID_QUEST_3_LINK))
{
return EOculusXRDeviceType::MetaQuest3Link;
}
else if (IsEqualUuid(SystemHeadsetId, XR_HEADSET_ID_QUEST_3S_LINK))
{
return EOculusXRDeviceType::MetaQuest3SLink;
}
else
{
return EOculusXRDeviceType::Quest2_Link; // return Quest 2 (over Link) as the default headset type
}
}
else
{
if (SystemProductName == TEXT("Oculus Rift S"))
{
return EOculusXRDeviceType::Rift_S;
}
else if (SystemProductName == TEXT("Oculus Rift CV1"))
{
return EOculusXRDeviceType::Rift;
}
else
{
return EOculusXRDeviceType::Quest2_Link; // return Quest 2 (over Link) as the default headset type
}
}
#endif // defined(__ANDROID__)
}
TArray<float> FSystemInfoExtensionPlugin::GetSystemDisplayAvailableFrequencies()
{
TArray<float> DisplayFrequencies;
if (bExtDisplayRefreshAvailible)
{
check(xrEnumerateDisplayRefreshRatesFB.GetValue() != nullptr);
uint32 TotalRates = 0;
ENSURE_XRCMD(xrEnumerateDisplayRefreshRatesFB.GetValue()(Session, 0, &TotalRates, nullptr));
DisplayFrequencies.Init(0, TotalRates);
ENSURE_XRCMD(xrEnumerateDisplayRefreshRatesFB.GetValue()(Session, TotalRates, &TotalRates, DisplayFrequencies.GetData()));
}
return DisplayFrequencies;
}
float FSystemInfoExtensionPlugin::GetSystemDisplayFrequency()
{
float Frequency = 0.0f;
if (bExtDisplayRefreshAvailible)
{
check(xrGetDisplayRefreshRateFB.GetValue() != nullptr);
ENSURE_XRCMD(xrGetDisplayRefreshRateFB.GetValue()(Session, &Frequency));
}
return Frequency;
}
void FSystemInfoExtensionPlugin::SetSystemDisplayFrequency(float DisplayFrequency)
{
if (bExtDisplayRefreshAvailible)
{
check(xrRequestDisplayRefreshRateFB.GetValue() != nullptr);
ENSURE_XRCMD(xrRequestDisplayRefreshRateFB.GetValue()(Session, DisplayFrequency));
}
}
void FSystemInfoExtensionPlugin::SetColorSpace(EOculusXRColorSpace ColorSpace)
{
if (bExtColorspaceAvailable)
{
XrColorSpaceFB XrColorSpace = ToXrColorSpace(ColorSpace);
check(xrSetColorSpaceFB.GetValue() != nullptr);
ENSURE_XRCMD(xrSetColorSpaceFB.GetValue()(Session, XrColorSpace));
}
}
EOculusXRColorSpace FSystemInfoExtensionPlugin::GetColorSpace()
{
EOculusXRColorSpace ColorSpace = EOculusXRColorSpace::Unknown;
if (bExtColorspaceAvailable)
{
XrSystemColorSpacePropertiesFB ColorSpaceProperties = { XR_TYPE_SYSTEM_COLOR_SPACE_PROPERTIES_FB };
XrSystemProperties SystemProperties = { XR_TYPE_SYSTEM_PROPERTIES, &ColorSpaceProperties };
ENSURE_XRCMD(xrGetSystemProperties(Instance, SystemId, &SystemProperties));
ColorSpace = ToOculusXRColorSpace(ColorSpaceProperties.colorSpace);
}
return ColorSpace;
}
bool FSystemInfoExtensionPlugin::IsPassthroughSupported()
{
if (bExtPassthroughAvailable)
{
XrSystemPassthroughProperties2FB PassthroughProperties = { XR_TYPE_SYSTEM_PASSTHROUGH_PROPERTIES2_FB };
XrSystemProperties SystemProperties = { XR_TYPE_SYSTEM_PROPERTIES, &PassthroughProperties };
XR_ENSURE(xrGetSystemProperties(Instance, SystemId, &SystemProperties));
return (PassthroughProperties.capabilities & XR_PASSTHROUGH_CAPABILITY_BIT_FB) == XR_PASSTHROUGH_CAPABILITY_BIT_FB;
}
return false;
}
bool FSystemInfoExtensionPlugin::IsColorPassthroughSupported()
{
if (bExtPassthroughAvailable)
{
XrSystemPassthroughProperties2FB PassthroughProperties = { XR_TYPE_SYSTEM_PASSTHROUGH_PROPERTIES2_FB };
XrSystemProperties SystemProperties = { XR_TYPE_SYSTEM_PROPERTIES, &PassthroughProperties };
XR_ENSURE(xrGetSystemProperties(Instance, SystemId, &SystemProperties));
return (PassthroughProperties.capabilities & XR_PASSTHROUGH_CAPABILITY_COLOR_BIT_FB) == XR_PASSTHROUGH_CAPABILITY_COLOR_BIT_FB;
}
return false;
}
bool FSystemInfoExtensionPlugin::IsPassthroughRecommended()
{
if (bExtPassthroughPreferencesAvailable && OculusXR::xrGetPassthroughPreferencesMETA.IsSet())
{
XrPassthroughPreferencesMETA PassthroughPreferences = { XR_TYPE_PASSTHROUGH_PREFERENCES_META };
ENSURE_XRCMD(OculusXR::xrGetPassthroughPreferencesMETA.GetValue()(Session, &PassthroughPreferences));
return (PassthroughPreferences.flags & XR_PASSTHROUGH_PREFERENCE_DEFAULT_TO_ACTIVE_BIT_META) == XR_PASSTHROUGH_PREFERENCE_DEFAULT_TO_ACTIVE_BIT_META;
}
return false;
}
EOculusXRControllerType FSystemInfoExtensionPlugin::GetControllerType(EControllerHand DeviceHand)
{
EOculusXRControllerType ControllerType = EOculusXRControllerType::Unknown;
IXRTrackingSystem* TrackingSystem = GetOpenXRTrackingSystem();
if (TrackingSystem != nullptr)
{
if (DeviceHand != EControllerHand::Left || DeviceHand != EControllerHand::Right)
{
XrInteractionProfileState Profile;
Profile.type = XR_TYPE_INTERACTION_PROFILE_STATE;
Profile.next = nullptr;
const XrPath TopLevelUserPath = DeviceHand != EControllerHand::Left ? ControllerPaths.LeftHandPath : ControllerPaths.RightHandPath;
XR_ENSURE(xrGetCurrentInteractionProfile(Session, TopLevelUserPath, &Profile));
const XrPath InteractionProfile = Profile.interactionProfile;
if (InteractionProfile == ControllerPaths.TouchControllerPath)
{
ControllerType = EOculusXRControllerType::MetaQuestTouch;
}
else if (InteractionProfile == ControllerPaths.TouchControllerProPath)
{
ControllerType = EOculusXRControllerType::MetaQuestTouchPro;
}
else if (InteractionProfile == ControllerPaths.TouchControllerPlusPath)
{
ControllerType = EOculusXRControllerType::MetaQuestTouchPlus;
}
}
}
return ControllerType;
}
} // namespace OculusXR

View File

@@ -0,0 +1,75 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "OculusXRHMDTypes.h"
#include "OpenXR/IOculusXRExtensionPlugin.h"
DECLARE_LOG_CATEGORY_EXTERN(LogOculusSystemInfoExtensionPlugin, Log, All);
namespace OculusXR
{
class FSystemInfoExtensionPlugin : public IOculusXRExtensionPlugin
{
public:
FSystemInfoExtensionPlugin();
// IOpenXRExtensionPlugin
virtual bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
virtual bool GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
virtual const void* OnCreateInstance(class IOpenXRHMDModule* InModule, const void* InNext);
virtual void PostCreateInstance(XrInstance InInstance) override;
virtual void PostCreateSession(XrSession InSession) override;
virtual bool GetControllerModel(XrInstance InInstance, XrPath InInteractionProfile, XrPath InDevicePath, FSoftObjectPath& OutPath) override;
virtual void GetControllerModelsForCooking(TArray<FSoftObjectPath>& OutPaths) override;
FString GetSystemProductName();
EOculusXRDeviceType GetDeviceType();
TArray<float> GetSystemDisplayAvailableFrequencies();
float GetSystemDisplayFrequency();
void SetSystemDisplayFrequency(float DisplayFrequency);
void SetColorSpace(EOculusXRColorSpace ColorSpace);
EOculusXRColorSpace GetColorSpace();
EOculusXRControllerType GetControllerType(EControllerHand DeviceHand);
bool IsPassthroughSupported();
bool IsColorPassthroughSupported();
bool IsPassthroughRecommended();
private:
EOculusXRDeviceType GetSystemHeadsetType();
XrInstance Instance;
XrSession Session;
XrSystemId SystemId;
bool bExtHeadsetIdAvailable;
XrUuidEXT SystemHeadsetId;
bool bSystemHeadsetIdValid;
FString SystemProductName;
EOculusXRDeviceType SystemDeviceType;
bool bExtDisplayRefreshAvailible;
bool bExtColorspaceAvailable;
bool bExtPassthroughAvailable;
bool bExtPassthroughPreferencesAvailable;
struct FControllerPaths
{
FControllerPaths()
: TouchControllerPath(XR_NULL_PATH)
, TouchControllerProPath(XR_NULL_PATH)
, TouchControllerPlusPath(XR_NULL_PATH)
, LeftHandPath(XR_NULL_PATH)
, RightHandPath(XR_NULL_PATH){};
XrPath TouchControllerPath;
XrPath TouchControllerProPath;
XrPath TouchControllerPlusPath;
XrPath LeftHandPath;
XrPath RightHandPath;
};
FControllerPaths ControllerPaths;
};
} // namespace OculusXR

View File

@@ -0,0 +1,34 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRXRFunctions.h"
#include "OpenXRCore.h"
#include "OculusXROpenXRUtilities.h"
namespace OculusXR
{
TOptional<PFN_xrPerfSettingsSetPerformanceLevelEXT> xrPerfSettingsSetPerformanceLevelEXT = nullptr;
TOptional<PFN_xrQueryPerformanceMetricsCounterMETA> xrQueryPerformanceMetricsCounterMETA = nullptr;
TOptional<PFN_xrEnumeratePerformanceMetricsCounterPathsMETA> xrEnumeratePerformanceMetricsCounterPathsMETA = nullptr;
TOptional<PFN_xrSetPerformanceMetricsStateMETA> xrSetPerformanceMetricsStateMETA = nullptr;
TOptional<PFN_xrGetDisplayRefreshRateFB> xrGetDisplayRefreshRateFB = nullptr;
TOptional<PFN_xrRequestDisplayRefreshRateFB> xrRequestDisplayRefreshRateFB = nullptr;
TOptional<PFN_xrEnumerateDisplayRefreshRatesFB> xrEnumerateDisplayRefreshRatesFB = nullptr;
TOptional<PFN_xrSetColorSpaceFB> xrSetColorSpaceFB = nullptr;
TOptional<PFN_xrGetPassthroughPreferencesMETA> xrGetPassthroughPreferencesMETA = nullptr;
TOptional<PFN_xrGetRecommendedLayerResolutionMETA> xrGetRecommendedLayerResolutionMETA = nullptr;
void InitOpenXRFunctions(XrInstance InInstance)
{
OculusXR::XRGetInstanceProcAddr(InInstance, "xrPerfSettingsSetPerformanceLevelEXT", &xrPerfSettingsSetPerformanceLevelEXT);
OculusXR::XRGetInstanceProcAddr(InInstance, "xrQueryPerformanceMetricsCounterMETA", &xrQueryPerformanceMetricsCounterMETA);
OculusXR::XRGetInstanceProcAddr(InInstance, "xrEnumeratePerformanceMetricsCounterPathsMETA", &xrEnumeratePerformanceMetricsCounterPathsMETA);
OculusXR::XRGetInstanceProcAddr(InInstance, "xrSetPerformanceMetricsStateMETA", &xrSetPerformanceMetricsStateMETA);
OculusXR::XRGetInstanceProcAddr(InInstance, "xrGetDisplayRefreshRateFB", &xrGetDisplayRefreshRateFB);
OculusXR::XRGetInstanceProcAddr(InInstance, "xrRequestDisplayRefreshRateFB", &xrRequestDisplayRefreshRateFB);
OculusXR::XRGetInstanceProcAddr(InInstance, "xrEnumerateDisplayRefreshRatesFB", &xrEnumerateDisplayRefreshRatesFB);
OculusXR::XRGetInstanceProcAddr(InInstance, "xrSetColorSpaceFB", &xrSetColorSpaceFB);
OculusXR::XRGetInstanceProcAddr(InInstance, "xrGetPassthroughPreferencesMETA", &xrGetPassthroughPreferencesMETA);
OculusXR::XRGetInstanceProcAddr(InInstance, "xrGetRecommendedLayerResolutionMETA", &xrGetRecommendedLayerResolutionMETA);
}
} // namespace OculusXR

View File

@@ -0,0 +1,22 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "khronos/openxr/openxr.h"
#include "Misc/Optional.h"
namespace OculusXR
{
extern TOptional<PFN_xrPerfSettingsSetPerformanceLevelEXT> xrPerfSettingsSetPerformanceLevelEXT;
extern TOptional<PFN_xrQueryPerformanceMetricsCounterMETA> xrQueryPerformanceMetricsCounterMETA;
extern TOptional<PFN_xrEnumeratePerformanceMetricsCounterPathsMETA> xrEnumeratePerformanceMetricsCounterPathsMETA;
extern TOptional<PFN_xrSetPerformanceMetricsStateMETA> xrSetPerformanceMetricsStateMETA;
extern TOptional<PFN_xrGetDisplayRefreshRateFB> xrGetDisplayRefreshRateFB;
extern TOptional<PFN_xrRequestDisplayRefreshRateFB> xrRequestDisplayRefreshRateFB;
extern TOptional<PFN_xrEnumerateDisplayRefreshRatesFB> xrEnumerateDisplayRefreshRatesFB;
extern TOptional<PFN_xrSetColorSpaceFB> xrSetColorSpaceFB;
extern TOptional<PFN_xrGetPassthroughPreferencesMETA> xrGetPassthroughPreferencesMETA;
extern TOptional<PFN_xrGetRecommendedLayerResolutionMETA> xrGetRecommendedLayerResolutionMETA;
void InitOpenXRFunctions(XrInstance InInstance);
} // namespace OculusXR