Android build settings + metaxr
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user