Android build settings + metaxr

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

View File

@@ -0,0 +1,65 @@
#include "Common.ush"
void MainVertexShader(
float4 InPosition : ATTRIBUTE0,
float2 InUV : ATTRIBUTE1,
out float2 OutUV : TEXCOORD0,
out float4 OutPosition : SV_POSITION
)
{
OutPosition = InPosition;
OutUV = InUV;
}
Texture2D<uint> TextureParameter;
#define Zoom 2
#define Pan float2(0.5, 0)
#define Aspect 1
#define Iterations int(128*PSVariables.IterationsMultiplier)
#define JuliaSeed float2(0.39, 0.2)
#define ColorScale float3(4, 5, 6)
float ComputeValue(float2 v, float2 offset)
{
float vxsquare = 0;
float vysquare = 0;
int iteration = 0;
int lastIteration = Iterations;
do
{
vxsquare = v.x * v.x;
vysquare = v.y * v.y;
v = float2(vxsquare - vysquare, v.x * v.y * 2) + offset;
iteration++;
if ((lastIteration == Iterations) && (vxsquare + vysquare) > 4.0)
{
lastIteration = iteration + 1;
}
} while (iteration < lastIteration);
return (float(iteration) - (log(log(sqrt(vxsquare + vysquare))) / log(2.0))) / float(Iterations);
}
float4 Mandelbrot_Func(float2 texCoord : TEXCOORD0) : COLOR0
{
float2 v = (texCoord - 0.5) * Zoom * float2(1, Aspect) - Pan;
float val = ComputeValue(v, v);
return float4(sin(val * ColorScale.x), sin(val * ColorScale.y), sin(val * ColorScale.z), 1);
}
void MainPixelShader(
in float2 uv : TEXCOORD0,
out float4 OutColor : SV_Target0
)
{
OutColor = Mandelbrot_Func(uv);
}

View File

@@ -0,0 +1,294 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRAssetManager.h"
#include "OculusXRHMDPrivate.h"
#include "OculusXRHMDModule.h"
#include "Engine/StaticMesh.h"
#include "Components/StaticMeshComponent.h"
#include "UObject/SoftObjectPath.h"
#include "Engine/SkeletalMesh.h"
#include "Components/SkeletalMeshComponent.h"
#include "OculusXRAssetDirectory.h"
#include "UObject/GCObject.h"
/* FOculusAssetDirectory
*****************************************************************************/
enum EOculusAsset
{
LeftTouchRiftS,
RightTouchRiftS,
LeftTouchQuest2,
RightTouchQuest2,
LeftTouchQuestPro,
RightTouchQuestPro,
LeftTouchQuest3,
RightTouchQuest3,
OculusAssetTotal
};
FSoftObjectPath FOculusAssetDirectory::AssetListing[OculusAssetTotal] = {
FString(TEXT("/OculusXR/Meshes/LeftTouchForQuestRiftSController.LeftTouchForQuestRiftSController")),
FString(TEXT("/OculusXR/Meshes/RightTouchForQuestRiftSController.RightTouchForQuestRiftSController")),
FString(TEXT("/OculusXR/Meshes/LeftTouchForQuest2.LeftTouchForQuest2")),
FString(TEXT("/OculusXR/Meshes/RightTouchForQuest2.RightTouchForQuest2")),
FString(TEXT("/OculusXR/Meshes/LeftMetaQuestTouchPro.LeftMetaQuestTouchPro")),
FString(TEXT("/OculusXR/Meshes/RightMetaQuestTouchPro.RightMetaQuestTouchPro")),
FString(TEXT("/OculusXR/Meshes/LeftMetaQuestTouchPlus.LeftMetaQuestTouchPlus")),
FString(TEXT("/OculusXR/Meshes/RightMetaQuestTouchPlus.RightMetaQuestTouchPlus")),
};
#if WITH_EDITORONLY_DATA
class FOculusAssetRepo : public FGCObject, public TArray<TObjectPtr<UObject>>
{
public:
// made an on-demand singleton rather than a static global, to avoid issues with FGCObject initialization
static FOculusAssetRepo& Get()
{
static FOculusAssetRepo AssetRepository;
return AssetRepository;
}
UObject* LoadAndAdd(const FSoftObjectPath& AssetPath)
{
UObject* AssetObj = AssetPath.TryLoad();
if (AssetObj != nullptr)
{
AddUnique(AssetObj);
}
return AssetObj;
}
public:
//~ FGCObject interface
virtual void AddReferencedObjects(FReferenceCollector& Collector) override
{
Collector.AddReferencedObjects(*this);
}
virtual FString GetReferencerName() const override
{
return TEXT("FOculusAssetRepo");
}
};
void FOculusAssetDirectory::LoadForCook()
{
FOculusAssetRepo& AssetRepro = FOculusAssetRepo::Get();
for (int32 AssetIndex = 0; AssetIndex < UE_ARRAY_COUNT(FOculusAssetDirectory::AssetListing); ++AssetIndex)
{
AssetRepro.LoadAndAdd(FOculusAssetDirectory::AssetListing[AssetIndex]);
}
}
void FOculusAssetDirectory::ReleaseAll()
{
FOculusAssetRepo::Get().Empty();
}
#endif // WITH_EDITORONLY_DATA
/* OculusAssetManager_Impl
*****************************************************************************/
namespace OculusAssetManager_Impl
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
struct FRenderableDevice
{
ovrpNode OVRNode;
ovrpSystemHeadset MinDeviceRange;
ovrpSystemHeadset MaxDeviceRange;
FSoftObjectPath MeshAssetRef;
};
static FRenderableDevice RenderableDevices[] = {
#if PLATFORM_ANDROID
// Quest 1 & 2
{ ovrpNode_HandLeft, ovrpSystemHeadset_Oculus_Quest, ovrpSystemHeadset_Oculus_Quest_2, FOculusAssetDirectory::AssetListing[LeftTouchQuest2] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Oculus_Quest, ovrpSystemHeadset_Oculus_Quest_2, FOculusAssetDirectory::AssetListing[RightTouchQuest2] },
// Quest Pro
{ ovrpNode_HandLeft, ovrpSystemHeadset_Meta_Quest_Pro, ovrpSystemHeadset_Meta_Quest_Pro, FOculusAssetDirectory::AssetListing[LeftTouchQuestPro] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Meta_Quest_Pro, ovrpSystemHeadset_Meta_Quest_Pro, FOculusAssetDirectory::AssetListing[RightTouchQuestPro] },
// Quest 3 & 3s
{ ovrpNode_HandLeft, ovrpSystemHeadset_Meta_Quest_3, ovrpSystemHeadset_Meta_Quest_3S, FOculusAssetDirectory::AssetListing[LeftTouchQuest3] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Meta_Quest_3, ovrpSystemHeadset_Meta_Quest_3S, FOculusAssetDirectory::AssetListing[RightTouchQuest3] },
#else
// PC - Rift S
{ ovrpNode_HandLeft, ovrpSystemHeadset_Rift_S, ovrpSystemHeadset_Rift_S, FOculusAssetDirectory::AssetListing[LeftTouchRiftS] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Rift_S, ovrpSystemHeadset_Rift_S, FOculusAssetDirectory::AssetListing[RightTouchRiftS] },
// PC - Quest 1 & 2
{ ovrpNode_HandLeft, ovrpSystemHeadset_Oculus_Link_Quest, ovrpSystemHeadset_Oculus_Link_Quest_2, FOculusAssetDirectory::AssetListing[LeftTouchQuest2] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Oculus_Link_Quest, ovrpSystemHeadset_Oculus_Link_Quest_2, FOculusAssetDirectory::AssetListing[RightTouchQuest2] },
// PC - Quest Pro
{ ovrpNode_HandLeft, ovrpSystemHeadset_Meta_Link_Quest_Pro, ovrpSystemHeadset_Meta_Link_Quest_Pro, FOculusAssetDirectory::AssetListing[LeftTouchQuestPro] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Meta_Link_Quest_Pro, ovrpSystemHeadset_Meta_Link_Quest_Pro, FOculusAssetDirectory::AssetListing[RightTouchQuestPro] },
// Quest 3 & 3s
{ ovrpNode_HandLeft, ovrpSystemHeadset_Meta_Link_Quest_3, ovrpSystemHeadset_Meta_Link_Quest_3S, FOculusAssetDirectory::AssetListing[LeftTouchQuest3] },
{ ovrpNode_HandRight, ovrpSystemHeadset_Meta_Link_Quest_3, ovrpSystemHeadset_Meta_Link_Quest_3S, FOculusAssetDirectory::AssetListing[RightTouchQuest3] },
#endif
};
static uint32 RenderableDeviceCount = sizeof(RenderableDevices) / sizeof(RenderableDevices[0]);
#endif // #if OCULUS_HMD_SUPPORTED_PLATFORMS
static UObject* FindDeviceMesh(const int32 DeviceID);
}; // namespace OculusAssetManager_Impl
static UObject* OculusAssetManager_Impl::FindDeviceMesh(const int32 DeviceID)
{
UObject* DeviceMesh = nullptr;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
const ovrpNode DeviceOVRNode = OculusXRHMD::ToOvrpNode(DeviceID);
bool bUseSystemHeadsetType = false;
ovrpSystemHeadset HeadsetType;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemHeadsetType2(&HeadsetType)))
{
bUseSystemHeadsetType = true;
}
if (DeviceOVRNode != ovrpNode_None)
{
for (uint32 DeviceIndex = 0; DeviceIndex < RenderableDeviceCount; ++DeviceIndex)
{
const FRenderableDevice& RenderableDevice = RenderableDevices[DeviceIndex];
if (RenderableDevice.OVRNode == DeviceOVRNode)
{
// If we have information about the current headset, load the model based of the headset information, otherwise load defaults.
if (bUseSystemHeadsetType)
{
if (HeadsetType >= RenderableDevice.MinDeviceRange && HeadsetType <= RenderableDevice.MaxDeviceRange)
{
DeviceMesh = RenderableDevice.MeshAssetRef.TryLoad();
break;
}
}
else
{
DeviceMesh = RenderableDevice.MeshAssetRef.TryLoad();
break;
}
}
}
}
#endif
return DeviceMesh;
}
/* FOculusAssetManager
*****************************************************************************/
FOculusAssetManager::FOculusAssetManager()
{
IModularFeatures::Get().RegisterModularFeature(IXRSystemAssets::GetModularFeatureName(), this);
ResourceHolder = NewObject<UOculusXRResourceHolder>();
ResourceHolder->AddToRoot();
}
FOculusAssetManager::~FOculusAssetManager()
{
if (ResourceHolder)
{
ResourceHolder->ConditionalBeginDestroy();
ResourceHolder = nullptr;
}
IModularFeatures::Get().UnregisterModularFeature(IXRSystemAssets::GetModularFeatureName(), this);
}
bool FOculusAssetManager::EnumerateRenderableDevices(TArray<int32>& DeviceListOut)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
using namespace OculusAssetManager_Impl;
DeviceListOut.Empty(RenderableDeviceCount);
for (uint32 DeviceIndex = 0; DeviceIndex < RenderableDeviceCount; ++DeviceIndex)
{
const FRenderableDevice& RenderableDevice = RenderableDevices[DeviceIndex];
const int32 ExternalDeviceId = OculusXRHMD::ToExternalDeviceId(RenderableDevice.OVRNode);
DeviceListOut.Add(ExternalDeviceId);
}
return true;
#else
return false;
#endif
}
int32 FOculusAssetManager::GetDeviceId(EControllerHand ControllerHand)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
ovrpNode ControllerOVRNode = ovrpNode_None;
switch (ControllerHand)
{
case EControllerHand::AnyHand:
// @TODO: maybe check if the right is tracking, if not choose left (if tracking)?
case EControllerHand::Right:
ControllerOVRNode = ovrpNode_HandRight;
break;
case EControllerHand::Left:
ControllerOVRNode = ovrpNode_HandLeft;
break;
case EControllerHand::ExternalCamera:
ControllerOVRNode = ovrpNode_TrackerZero;
break;
// case EControllerHand::Special_1:
// ControllerOVRNode = ovrpNode_TrackerOne;
// break;
// case EControllerHand::Special_2:
// ControllerOVRNode = ovrpNode_TrackerTwo;
// break;
// case EControllerHand::Special_3:
// ControllerOVRNode = ovrpNode_TrackerThree;
// break;
// case EControllerHand::Special_4:
// ControllerOVRNode = ovrpNode_DeviceObjectZero;
// break;
default:
// ControllerOVRNode = ovrpNode_None => returns -1
break;
}
return OculusXRHMD::ToExternalDeviceId(ControllerOVRNode);
#else
return INDEX_NONE;
#endif
}
UPrimitiveComponent* FOculusAssetManager::CreateRenderComponent(const int32 DeviceId, AActor* Owner, EObjectFlags Flags, const bool /*bForceSynchronous*/, const FXRComponentLoadComplete& OnLoadComplete)
{
UPrimitiveComponent* NewRenderComponent = nullptr;
if (UObject* DeviceMesh = OculusAssetManager_Impl::FindDeviceMesh(DeviceId))
{
if (UStaticMesh* AsStaticMesh = Cast<UStaticMesh>(DeviceMesh))
{
const FName ComponentName = MakeUniqueObjectName(Owner, UStaticMeshComponent::StaticClass(), *FString::Printf(TEXT("%s_Device%d"), TEXT("Oculus"), DeviceId));
UStaticMeshComponent* MeshComponent = NewObject<UStaticMeshComponent>(Owner, ComponentName, Flags);
MeshComponent->SetStaticMesh(AsStaticMesh);
NewRenderComponent = MeshComponent;
}
else if (USkeletalMesh* AsSkeletalMesh = Cast<USkeletalMesh>(DeviceMesh))
{
const FName ComponentName = MakeUniqueObjectName(Owner, USkeletalMeshComponent::StaticClass(), *FString::Printf(TEXT("%s_Device%d"), TEXT("Oculus"), DeviceId));
USkeletalMeshComponent* SkelMeshComponent = NewObject<USkeletalMeshComponent>(Owner, ComponentName, Flags);
SkelMeshComponent->SetSkeletalMesh(AsSkeletalMesh);
NewRenderComponent = SkelMeshComponent;
}
NewRenderComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
OnLoadComplete.ExecuteIfBound(NewRenderComponent);
return NewRenderComponent;
}

View File

@@ -0,0 +1,30 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "IXRSystemAssets.h"
#include "OculusXRResourceHolder.h"
#include "UObject/SoftObjectPtr.h"
/**
*
*/
class FOculusAssetManager : public IXRSystemAssets
{
public:
FOculusAssetManager();
virtual ~FOculusAssetManager();
public:
UOculusXRResourceHolder* GetResourceHolder() { return ResourceHolder; }
//~ IXRSystemAssets interface
virtual bool EnumerateRenderableDevices(TArray<int32>& DeviceListOut) override;
virtual int32 GetDeviceId(EControllerHand ControllerHand) override;
virtual UPrimitiveComponent* CreateRenderComponent(const int32 DeviceId, AActor* Owner, EObjectFlags Flags, const bool bForceSynchronous, const FXRComponentLoadComplete& OnLoadComplete) override;
protected:
UOculusXRResourceHolder* ResourceHolder;
};

View File

@@ -0,0 +1,8 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRDelegates.h"
FOculusEventDelegates::FOculusDisplayRefreshRateChangedEvent FOculusEventDelegates::OculusDisplayRefreshRateChanged;
FOculusEventDelegates::FOculusEyeTrackingStateChangedEvent FOculusEventDelegates::OculusEyeTrackingStateChanged;

View File

@@ -0,0 +1,19 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreTypes.h"
#include "Delegates/Delegate.h"
class FOculusEventDelegates
{
public:
/** When the display refresh rate is changed */
DECLARE_MULTICAST_DELEGATE_TwoParams(FOculusDisplayRefreshRateChangedEvent, float /*fromRefreshRate*/, float /*toRefreshRate*/);
static FOculusDisplayRefreshRateChangedEvent OculusDisplayRefreshRateChanged;
/** When the eye tracking status changes */
DECLARE_MULTICAST_DELEGATE_OneParam(FOculusEyeTrackingStateChangedEvent, bool /*bIsEyeTrackingOn*/);
static FOculusEyeTrackingStateChangedEvent OculusEyeTrackingStateChanged;
};

View File

@@ -0,0 +1,21 @@
// @lint-ignore-every LICENSELINT
// Copyright 1998-2020 Epic Games, Inc. All Rights Reserved.
#include "OculusXREventComponent.h"
#include "OculusXRHMD.h"
#include "OculusXRDelegates.h"
void UOculusXREventComponent::OnRegister()
{
Super::OnRegister();
FOculusEventDelegates::OculusDisplayRefreshRateChanged.AddUObject(this, &UOculusXREventComponent::OculusDisplayRefreshRateChanged_Handler);
FOculusEventDelegates::OculusEyeTrackingStateChanged.AddUObject(this, &UOculusXREventComponent::OculusEyeTrackingStateChanged_Handler);
}
void UOculusXREventComponent::OnUnregister()
{
Super::OnUnregister();
FOculusEventDelegates::OculusDisplayRefreshRateChanged.RemoveAll(this);
FOculusEventDelegates::OculusEyeTrackingStateChanged.RemoveAll(this);
}

View File

@@ -0,0 +1,768 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRFunctionLibrary.h"
#include "OculusXRFunctionLibraryOpenXR.h"
#include "OculusXRFunctionLibraryOVR.h"
#include "OculusXRHMDPrivate.h"
#include "OculusXRHMD.h"
#include "OpenXR/OculusXROpenXRUtilities.h"
#include "Logging/MessageLog.h"
#define LOCTEXT_NAMESPACE "OculusFunctionLibrary"
//-------------------------------------------------------------------------------------------------
// UOculusXRFunctionLibrary
//-------------------------------------------------------------------------------------------------
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> UOculusXRFunctionLibrary::FunctionLibraryImpl = nullptr;
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> UOculusXRFunctionLibrary::GetOculusXRFunctionImpl()
{
if (FunctionLibraryImpl == nullptr)
{
if (OculusXR::IsOpenXRSystem())
{
FunctionLibraryImpl = MakeShared<OculusXRHMD::FOculusXRFunctionLibraryOpenXR>();
}
else
{
FunctionLibraryImpl = MakeShared<OculusXRHMD::FOculusXRFunctionLibraryOVR>();
}
}
return FunctionLibraryImpl;
}
void UOculusXRFunctionLibrary::ShutdownXRFunctionLibrary()
{
FunctionLibraryImpl = nullptr;
}
UOculusXRFunctionLibrary::UOculusXRFunctionLibrary(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
OculusXRHMD::FOculusXRHMD* UOculusXRFunctionLibrary::GetOculusXRHMD()
{
return OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
}
void UOculusXRFunctionLibrary::GetPose(FRotator& DeviceRotation, FVector& DevicePosition, FVector& NeckPosition, bool bUseOrienationForPlayerCamera, bool bUsePositionForPlayerCamera, const FVector PositionScale)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->GetPose(DeviceRotation, DevicePosition, NeckPosition);
}
else
#endif
{
DeviceRotation = FRotator::ZeroRotator;
DevicePosition = FVector::ZeroVector;
NeckPosition = FVector::ZeroVector;
}
}
void UOculusXRFunctionLibrary::SetBaseRotationAndBaseOffsetInMeters(FRotator Rotation, FVector BaseOffsetInMeters, EOrientPositionSelector::Type Options)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->SetBaseRotationAndBaseOffsetInMeters(Rotation, BaseOffsetInMeters, Options);
}
#endif
}
void UOculusXRFunctionLibrary::GetBaseRotationAndBaseOffsetInMeters(FRotator& OutRotation, FVector& OutBaseOffsetInMeters)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->GetBaseRotationAndBaseOffsetInMeters(OutRotation, OutBaseOffsetInMeters);
}
#endif
}
void UOculusXRFunctionLibrary::GetRawSensorData(FVector& AngularAcceleration, FVector& LinearAcceleration, FVector& AngularVelocity, FVector& LinearVelocity, float& TimeInSeconds, EOculusXRTrackedDeviceType DeviceType)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->GetRawSensorData(AngularAcceleration, LinearAcceleration, AngularVelocity, LinearVelocity, TimeInSeconds, DeviceType);
}
#endif
}
bool UOculusXRFunctionLibrary::IsDeviceTracked(EOculusXRTrackedDeviceType DeviceType)
{
bool bIsDeviceTracked = false;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
bIsDeviceTracked = Impl->IsDeviceTracked(DeviceType);
}
#endif
return bIsDeviceTracked;
}
void UOculusXRFunctionLibrary::GetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel& CpuPerfLevel, EOculusXRProcessorPerformanceLevel& GpuPerfLevel)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->GetSuggestedCpuAndGpuPerformanceLevels(CpuPerfLevel, GpuPerfLevel);
}
#endif
}
void UOculusXRFunctionLibrary::SetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel CpuPerfLevel, EOculusXRProcessorPerformanceLevel GpuPerfLevel)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->SetSuggestedCpuAndGpuPerformanceLevels(CpuPerfLevel, GpuPerfLevel);
}
#endif
}
void UOculusXRFunctionLibrary::SetCPUAndGPULevels(int CPULevel, int GPULevel)
{
// Deprecated. Please use Get/SetSuggestedCpuAndGpuPerformanceLevels instead.
}
bool UOculusXRFunctionLibrary::GetUserProfile(FOculusXRHmdUserProfile& Profile)
{
bool bGetUserProfile = false;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
bGetUserProfile = Impl->GetUserProfile(Profile);
}
#endif
return bGetUserProfile;
}
void UOculusXRFunctionLibrary::SetBaseRotationAndPositionOffset(FRotator BaseRot, FVector PosOffset, EOrientPositionSelector::Type Options)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->SetBaseRotationAndPositionOffset(BaseRot, PosOffset, Options);
}
#endif
}
void UOculusXRFunctionLibrary::GetBaseRotationAndPositionOffset(FRotator& OutRot, FVector& OutPosOffset)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->GetBaseRotationAndPositionOffset(OutRot, OutPosOffset);
}
#endif
}
void UOculusXRFunctionLibrary::AddLoadingSplashScreen(class UTexture2D* Texture, FVector TranslationInMeters, FRotator Rotation, FVector2D SizeInMeters, FRotator DeltaRotation, bool bClearBeforeAdd)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->AddLoadingSplashScreen(Texture, TranslationInMeters, Rotation, SizeInMeters, DeltaRotation, bClearBeforeAdd);
}
#endif
}
void UOculusXRFunctionLibrary::ClearLoadingSplashScreens()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->ClearLoadingSplashScreens();
}
#endif
}
bool UOculusXRFunctionLibrary::HasInputFocus()
{
bool bHasInputFocus = false;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
bHasInputFocus = Impl->HasInputFocus();
}
#endif
return bHasInputFocus;
}
bool UOculusXRFunctionLibrary::HasSystemOverlayPresent()
{
bool bHasSystemOverlayPresent = false;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
bHasSystemOverlayPresent = Impl->HasSystemOverlayPresent();
}
#endif
return bHasSystemOverlayPresent;
}
void UOculusXRFunctionLibrary::GetGPUUtilization(bool& IsGPUAvailable, float& GPUUtilization)
{
GPUUtilization = 0.0f;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->GetGPUUtilization(IsGPUAvailable, GPUUtilization);
}
#endif
}
float UOculusXRFunctionLibrary::GetGPUFrameTime()
{
float FrameTime = 0;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
FrameTime = Impl->GetGPUFrameTime();
}
#endif
return FrameTime;
}
void UOculusXRFunctionLibrary::GetPerformanceMetrics(FOculusXRPerformanceMetrics& PerformanceMetrics)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->GetPerformanceMetrics(PerformanceMetrics);
}
#endif
}
EOculusXRFoveatedRenderingMethod UOculusXRFunctionLibrary::GetFoveatedRenderingMethod()
{
EOculusXRFoveatedRenderingMethod FoveatedRenderingMethod = EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
FoveatedRenderingMethod = Impl->GetFoveatedRenderingMethod();
}
#endif
return FoveatedRenderingMethod;
}
void UOculusXRFunctionLibrary::SetFoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod Method)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->SetFoveatedRenderingMethod(Method);
}
#endif
}
void UOculusXRFunctionLibrary::SetFoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel level, bool isDynamic)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->SetFoveatedRenderingLevel(level, isDynamic);
}
#endif
}
EOculusXRFoveatedRenderingLevel UOculusXRFunctionLibrary::GetFoveatedRenderingLevel()
{
EOculusXRFoveatedRenderingLevel FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::Off;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
FoveatedRenderingLevel = Impl->GetFoveatedRenderingLevel();
}
#endif
return FoveatedRenderingLevel;
}
bool UOculusXRFunctionLibrary::GetEyeTrackedFoveatedRenderingSupported()
{
bool bEyeTrackedFoveatedRenderingSupported = false;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
bEyeTrackedFoveatedRenderingSupported = Impl->GetEyeTrackedFoveatedRenderingSupported();
}
#endif
return bEyeTrackedFoveatedRenderingSupported;
}
FString UOculusXRFunctionLibrary::GetDeviceName()
{
FString DeviceName;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
DeviceName = Impl->GetDeviceName();
}
#endif
return DeviceName;
}
EOculusXRDeviceType UOculusXRFunctionLibrary::GetDeviceType()
{
EOculusXRDeviceType DeviceType = EOculusXRDeviceType::OculusUnknown;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
DeviceType = Impl->GetDeviceType();
}
#endif
return DeviceType;
}
EOculusXRControllerType UOculusXRFunctionLibrary::GetControllerType(EControllerHand deviceHand)
{
EOculusXRControllerType ControllerType = EOculusXRControllerType::Unknown;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
ControllerType = Impl->GetControllerType(deviceHand);
}
#endif
return ControllerType;
}
TArray<float> UOculusXRFunctionLibrary::GetAvailableDisplayFrequencies()
{
TArray<float> FreqArray;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
FreqArray = Impl->GetAvailableDisplayFrequencies();
}
#endif
return FreqArray;
}
float UOculusXRFunctionLibrary::GetCurrentDisplayFrequency()
{
float Frequency = 0.0f;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Frequency = Impl->GetCurrentDisplayFrequency();
}
#endif
return Frequency;
}
void UOculusXRFunctionLibrary::SetDisplayFrequency(float RequestedFrequency)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->SetDisplayFrequency(RequestedFrequency);
}
#endif
}
void UOculusXRFunctionLibrary::EnablePositionTracking(bool bPositionTracking)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->EnablePositionTracking(bPositionTracking);
}
#endif
}
void UOculusXRFunctionLibrary::EnableOrientationTracking(bool bOrientationTracking)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->EnableOrientationTracking(bOrientationTracking);
}
#endif
}
void UOculusXRFunctionLibrary::SetColorScaleAndOffset(FLinearColor ColorScale, FLinearColor ColorOffset, bool bApplyToAllLayers)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->SetColorScaleAndOffset(ColorScale, ColorOffset, bApplyToAllLayers);
}
#endif
}
class IStereoLayers* UOculusXRFunctionLibrary::GetStereoLayers()
{
IStereoLayers* Layers = nullptr;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Layers = Impl->GetStereoLayers();
}
#endif
return Layers;
}
bool UOculusXRFunctionLibrary::IsGuardianConfigured()
{
bool bIsGuardianConfigured = false;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
bIsGuardianConfigured = Impl->IsGuardianConfigured();
}
#endif
return bIsGuardianConfigured;
}
bool UOculusXRFunctionLibrary::IsGuardianDisplayed()
{
bool bIsGuardianDisplayed = false;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
bIsGuardianDisplayed = Impl->IsGuardianDisplayed();
}
#endif
return bIsGuardianDisplayed;
}
TArray<FVector> UOculusXRFunctionLibrary::GetGuardianPoints(EOculusXRBoundaryType BoundaryType, bool UsePawnSpace /* = false */)
{
TArray<FVector> BoundaryPointList;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
BoundaryPointList = Impl->GetGuardianPoints(BoundaryType, UsePawnSpace);
}
#endif
return BoundaryPointList;
}
FVector UOculusXRFunctionLibrary::GetGuardianDimensions(EOculusXRBoundaryType BoundaryType)
{
FVector Dimensions = FVector::ZeroVector;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Dimensions = Impl->GetGuardianDimensions(BoundaryType);
}
#endif
return Dimensions;
}
FTransform UOculusXRFunctionLibrary::GetPlayAreaTransform()
{
FTransform Transform;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Transform = Impl->GetPlayAreaTransform();
}
#endif
return Transform;
}
FOculusXRGuardianTestResult UOculusXRFunctionLibrary::GetPointGuardianIntersection(const FVector Point, EOculusXRBoundaryType BoundaryType)
{
FOculusXRGuardianTestResult InteractionInfo;
memset(&InteractionInfo, 0, sizeof(FOculusXRGuardianTestResult));
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
InteractionInfo = Impl->GetPointGuardianIntersection(Point, BoundaryType);
}
#endif
return InteractionInfo;
}
FOculusXRGuardianTestResult UOculusXRFunctionLibrary::GetNodeGuardianIntersection(EOculusXRTrackedDeviceType DeviceType, EOculusXRBoundaryType BoundaryType)
{
FOculusXRGuardianTestResult InteractionInfo;
memset(&InteractionInfo, 0, sizeof(FOculusXRGuardianTestResult));
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
InteractionInfo = Impl->GetNodeGuardianIntersection(DeviceType, BoundaryType);
}
#endif
return InteractionInfo;
}
void UOculusXRFunctionLibrary::SetGuardianVisibility(bool GuardianVisible)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->SetGuardianVisibility(GuardianVisible);
}
#endif
}
bool UOculusXRFunctionLibrary::GetSystemHmd3DofModeEnabled()
{
bool bGetSystemHmd3DofModeEnabled = false;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
bGetSystemHmd3DofModeEnabled = Impl->GetSystemHmd3DofModeEnabled();
}
#endif
return bGetSystemHmd3DofModeEnabled;
}
EOculusXRColorSpace UOculusXRFunctionLibrary::GetHmdColorDesc()
{
EOculusXRColorSpace HmdColorSpace = EOculusXRColorSpace::Unknown;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
HmdColorSpace = Impl->GetHmdColorDesc();
}
#endif
return HmdColorSpace;
}
void UOculusXRFunctionLibrary::SetClientColorDesc(EOculusXRColorSpace ColorSpace)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->SetClientColorDesc(ColorSpace);
}
#endif
}
void UOculusXRFunctionLibrary::SetLocalDimmingOn(bool LocalDimmingOn)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->SetLocalDimmingOn(LocalDimmingOn);
}
#endif
}
bool UOculusXRFunctionLibrary::IsPassthroughSupported()
{
bool bIsPassthroughSupported = false;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
bIsPassthroughSupported = Impl->IsPassthroughSupported();
}
#endif
return bIsPassthroughSupported;
}
bool UOculusXRFunctionLibrary::IsColorPassthroughSupported()
{
bool bIsColorPassthroughSupported = false;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
bIsColorPassthroughSupported = Impl->IsColorPassthroughSupported();
}
#endif
return bIsColorPassthroughSupported;
}
void UOculusXRFunctionLibrary::StartEnvironmentDepth()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->StartEnvironmentDepth();
}
#endif
}
void UOculusXRFunctionLibrary::StopEnvironmentDepth()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->StopEnvironmentDepth();
}
#endif
}
bool UOculusXRFunctionLibrary::IsEnvironmentDepthStarted()
{
bool bIsEnvironmentDepthStarted = false;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
bIsEnvironmentDepthStarted = Impl->IsEnvironmentDepthStarted();
}
#endif
return bIsEnvironmentDepthStarted;
}
void UOculusXRFunctionLibrary::SetEnvironmentDepthHandRemoval(bool RemoveHands)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->SetEnvironmentDepthHandRemoval(RemoveHands);
}
#endif
}
void UOculusXRFunctionLibrary::SetXROcclusionsMode(UObject* WorldContextObject, EOculusXROcclusionsMode Mode)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->SetXROcclusionsMode(WorldContextObject, Mode);
}
#endif
}
void UOculusXRFunctionLibrary::SetEyeBufferSharpenType(EOculusXREyeBufferSharpenType EyeBufferSharpenType)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->SetEyeBufferSharpenType(EyeBufferSharpenType);
}
#endif
}
bool UOculusXRFunctionLibrary::IsPassthroughRecommended()
{
bool bIsPassthroughRecommended = false;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
bIsPassthroughRecommended = Impl->IsPassthroughRecommended();
}
#endif
return bIsPassthroughRecommended;
}
void UOculusXRFunctionLibrary::GetOpenXRInstanceProcAddrFunc(void** func)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->GetOpenXRInstanceProcAddrFunc(func);
}
#endif
}
void UOculusXRFunctionLibrary::GetNativeOpenXRHandles(uint64_t* OutXrInstance, uint64_t* OutXrSession)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->GetNativeOpenXRHandles(OutXrInstance, OutXrSession);
}
#endif
}
void UOculusXRFunctionLibrary::GetAppSpace(uint64_t* OutAppSpace)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->GetAppSpace(OutAppSpace);
}
#endif
}
void UOculusXRFunctionLibrary::GetNextPredictedDisplayTime(uint64_t* NextPredictedDisplayTime)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->GetNextPredictedDisplayTime(NextPredictedDisplayTime);
}
#endif
}
void UOculusXRFunctionLibrary::RegisterOpenXrEventHandler(void (*OpenXrEventHandler)(void* data, void* context), void* Context)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->RegisterOpenXrEventHandler(OpenXrEventHandler, Context);
}
#endif
}
void UOculusXRFunctionLibrary::UnregisterOpenXrEventHandler(void (*OpenXrEventHandler)(void* data, void* context))
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
TSharedPtr<OculusXRHMD::IOculusXRFunctionLibrary> Impl = GetOculusXRFunctionImpl();
if (Impl != nullptr)
{
Impl->UnregisterOpenXrEventHandler(OpenXrEventHandler);
}
#endif
}
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,903 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRFunctionLibraryOVR.h"
#include "OculusXRHMD.h"
#include "OpenXR/OculusXROpenXRUtilities.h"
namespace OculusXRHMD
{
void FOculusXRFunctionLibraryOVR::GetPose(FRotator& DeviceRotation, FVector& DevicePosition, FVector& NeckPosition)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD && OculusXRHMD->IsHeadTrackingAllowed())
{
FQuat HeadOrientation = FQuat::Identity;
FVector HeadPosition = FVector::ZeroVector;
OculusXRHMD->GetCurrentPose(OculusXRHMD->HMDDeviceId, HeadOrientation, HeadPosition);
DeviceRotation = HeadOrientation.Rotator();
DevicePosition = HeadPosition;
NeckPosition = OculusXRHMD->GetNeckPosition(HeadOrientation, HeadPosition);
}
else
{
DeviceRotation = FRotator::ZeroRotator;
DevicePosition = FVector::ZeroVector;
NeckPosition = FVector::ZeroVector;
}
}
void FOculusXRFunctionLibraryOVR::SetBaseRotationAndBaseOffsetInMeters(FRotator Rotation, FVector BaseOffsetInMeters, EOrientPositionSelector::Type Options)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
if ((Options == EOrientPositionSelector::Orientation) || (Options == EOrientPositionSelector::OrientationAndPosition))
{
OculusXRHMD->SetBaseRotation(Rotation);
}
if ((Options == EOrientPositionSelector::Position) || (Options == EOrientPositionSelector::OrientationAndPosition))
{
OculusXRHMD->SetBaseOffsetInMeters(BaseOffsetInMeters);
}
}
}
void FOculusXRFunctionLibraryOVR::GetBaseRotationAndBaseOffsetInMeters(FRotator& OutRotation, FVector& OutBaseOffsetInMeters)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OutRotation = OculusXRHMD->GetBaseRotation();
OutBaseOffsetInMeters = OculusXRHMD->GetBaseOffsetInMeters();
}
else
{
OutRotation = FRotator::ZeroRotator;
OutBaseOffsetInMeters = FVector::ZeroVector;
}
}
void FOculusXRFunctionLibraryOVR::GetRawSensorData(FVector& AngularAcceleration, FVector& LinearAcceleration, FVector& AngularVelocity, FVector& LinearVelocity, float& TimeInSeconds, EOculusXRTrackedDeviceType DeviceType)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
ovrpPoseStatef state;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetNodePoseState3(ovrpStep_Render, OVRP_CURRENT_FRAMEINDEX, OculusXRHMD::ToOvrpNode(DeviceType), &state)))
{
AngularAcceleration = OculusXRHMD::ToFVector(state.AngularAcceleration);
LinearAcceleration = OculusXRHMD::ToFVector(state.Acceleration);
AngularVelocity = OculusXRHMD::ToFVector(state.AngularVelocity);
LinearVelocity = OculusXRHMD::ToFVector(state.Velocity);
TimeInSeconds = state.Time;
}
}
}
bool FOculusXRFunctionLibraryOVR::IsDeviceTracked(EOculusXRTrackedDeviceType DeviceType)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
ovrpBool Present;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetNodePresent2(OculusXRHMD::ToOvrpNode(DeviceType), &Present)))
{
return Present != ovrpBool_False;
}
else
{
return false;
}
}
return false;
}
void FOculusXRFunctionLibraryOVR::GetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel& CpuPerfLevel, EOculusXRProcessorPerformanceLevel& GpuPerfLevel)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
OculusXRHMD->GetSuggestedCpuAndGpuPerformanceLevels(CpuPerfLevel, GpuPerfLevel);
}
}
void FOculusXRFunctionLibraryOVR::SetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel CpuPerfLevel, EOculusXRProcessorPerformanceLevel GpuPerfLevel)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
OculusXRHMD->SetSuggestedCpuAndGpuPerformanceLevels(CpuPerfLevel, GpuPerfLevel);
}
}
bool FOculusXRFunctionLibraryOVR::GetUserProfile(FOculusXRHmdUserProfile& Profile)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD::FOculusXRHMD::UserProfile Data;
if (OculusXRHMD->GetUserProfile(Data))
{
Profile.Name = "";
Profile.Gender = "Unknown";
Profile.PlayerHeight = 0.0f;
Profile.EyeHeight = Data.EyeHeight;
Profile.IPD = Data.IPD;
Profile.NeckToEyeDistance = FVector2D(Data.EyeDepth, 0.0f);
return true;
}
}
return false;
}
void FOculusXRFunctionLibraryOVR::SetBaseRotationAndPositionOffset(FRotator BaseRot, FVector PosOffset, EOrientPositionSelector::Type Options)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
if (Options == EOrientPositionSelector::Orientation || Options == EOrientPositionSelector::OrientationAndPosition)
{
OculusXRHMD->SetBaseRotation(BaseRot);
}
}
}
void FOculusXRFunctionLibraryOVR::GetBaseRotationAndPositionOffset(FRotator& OutRot, FVector& OutPosOffset)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OutRot = OculusXRHMD->GetBaseRotation();
OutPosOffset = FVector::ZeroVector;
}
}
void FOculusXRFunctionLibraryOVR::AddLoadingSplashScreen(class UTexture2D* Texture, FVector TranslationInMeters, FRotator Rotation, FVector2D SizeInMeters, FRotator DeltaRotation, bool bClearBeforeAdd)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD::FSplash* Splash = OculusXRHMD->GetSplash();
if (Splash)
{
if (bClearBeforeAdd)
{
Splash->ClearSplashes();
}
FOculusXRSplashDesc Desc;
Desc.LoadingTexture = Texture;
Desc.QuadSizeInMeters = SizeInMeters;
Desc.TransformInMeters = FTransform(Rotation, TranslationInMeters);
Desc.DeltaRotation = FQuat(DeltaRotation);
Splash->AddSplash(Desc);
}
}
}
void FOculusXRFunctionLibraryOVR::ClearLoadingSplashScreens()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD::FSplash* Splash = OculusXRHMD->GetSplash();
if (Splash)
{
Splash->ClearSplashes();
}
}
}
bool FOculusXRFunctionLibraryOVR::HasInputFocus()
{
const OculusXRHMD::FOculusXRHMD* const OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
ovrpBool HasFocus;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetAppHasInputFocus(&HasFocus)))
{
return HasFocus != ovrpBool_False;
}
}
return false;
}
bool FOculusXRFunctionLibraryOVR::HasSystemOverlayPresent()
{
const OculusXRHMD::FOculusXRHMD* const OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr && OculusXRHMD->IsHMDActive())
{
ovrpBool HasFocus;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetAppHasInputFocus(&HasFocus)))
{
return HasFocus == ovrpBool_False;
}
}
return false;
}
void FOculusXRFunctionLibraryOVR::GetGPUUtilization(bool& IsGPUAvailable, float& GPUUtilization)
{
GPUUtilization = 0.0f;
const OculusXRHMD::FOculusXRHMD* const OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool bIsSupported = ovrpBool_False;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().IsPerfMetricsSupported(ovrpPerfMetrics_System_GpuUtilPercentage_Float, &bIsSupported)) && bIsSupported == ovrpBool_True)
{
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetPerfMetricsFloat(ovrpPerfMetrics_System_GpuUtilPercentage_Float, &GPUUtilization)))
{
IsGPUAvailable = true;
GPUUtilization *= 100;
}
}
}
}
float FOculusXRFunctionLibraryOVR::GetGPUFrameTime()
{
float FrameTime = 0;
const OculusXRHMD::FOculusXRHMD* const OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool bIsSupported = ovrpBool_False;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().IsPerfMetricsSupported(ovrpPerfMetrics_App_GpuTime_Float, &bIsSupported)) && bIsSupported == ovrpBool_True)
{
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetPerfMetricsFloat(ovrpPerfMetrics_App_GpuTime_Float, &FrameTime)))
{
return FrameTime * 1000;
}
}
}
return 0.0f;
}
void FOculusXRFunctionLibraryOVR::GetPerformanceMetrics(FOculusXRPerformanceMetrics& PerformanceMetrics)
{
const OculusXRHMD::FOculusXRHMD* const OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
PerformanceMetrics = OculusXRHMD->GetPerformanceMetrics();
}
}
EOculusXRFoveatedRenderingMethod FOculusXRFunctionLibraryOVR::GetFoveatedRenderingMethod()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool enabled;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetFoveationEyeTracked(&enabled)))
{
return enabled == ovrpBool_True ? EOculusXRFoveatedRenderingMethod::EyeTrackedFoveatedRendering : EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering;
}
}
return EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering;
}
void FOculusXRFunctionLibraryOVR::SetFoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod Method)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->SetFoveatedRenderingMethod(Method);
}
}
void FOculusXRFunctionLibraryOVR::SetFoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel level, bool isDynamic)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->SetFoveatedRenderingLevel(level, isDynamic);
}
}
EOculusXRFoveatedRenderingLevel FOculusXRFunctionLibraryOVR::GetFoveatedRenderingLevel()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpTiledMultiResLevel Lvl;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetTiledMultiResLevel(&Lvl)))
{
return (EOculusXRFoveatedRenderingLevel)Lvl;
}
}
return EOculusXRFoveatedRenderingLevel::Off;
}
bool FOculusXRFunctionLibraryOVR::GetEyeTrackedFoveatedRenderingSupported()
{
// Always return false on other engine releases, since they don't have FDM offset support
#ifdef WITH_OCULUS_BRANCH
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool Supported;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetFoveationEyeTrackedSupported(&Supported)))
{
return Supported == ovrpBool_True;
}
}
#endif // WITH_OCULUS_BRANCH
return false;
}
FString FOculusXRFunctionLibraryOVR::GetDeviceName()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
const char* NameString;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemProductName2(&NameString)) && NameString)
{
return FString(NameString);
}
}
return FString();
}
EOculusXRDeviceType FOculusXRFunctionLibraryOVR::GetDeviceType()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
if (OculusXRHMD->GetSettings())
{
switch (OculusXRHMD->GetSettings()->SystemHeadset)
{
case ovrpSystemHeadset_Oculus_Quest:
return EOculusXRDeviceType::OculusQuest_Deprecated;
case ovrpSystemHeadset_Oculus_Quest_2:
return EOculusXRDeviceType::OculusQuest2;
case ovrpSystemHeadset_Meta_Quest_Pro:
return EOculusXRDeviceType::MetaQuestPro;
case ovrpSystemHeadset_Meta_Quest_3:
return EOculusXRDeviceType::MetaQuest3;
case ovrpSystemHeadset_Meta_Quest_3S:
return EOculusXRDeviceType::MetaQuest3S;
case ovrpSystemHeadset_Rift_CV1:
return EOculusXRDeviceType::Rift;
case ovrpSystemHeadset_Rift_S:
return EOculusXRDeviceType::Rift_S;
case ovrpSystemHeadset_Oculus_Link_Quest:
return EOculusXRDeviceType::Quest_Link_Deprecated;
case ovrpSystemHeadset_Oculus_Link_Quest_2:
return EOculusXRDeviceType::Quest2_Link;
case ovrpSystemHeadset_Meta_Link_Quest_Pro:
return EOculusXRDeviceType::MetaQuestProLink;
case ovrpSystemHeadset_Meta_Link_Quest_3:
return EOculusXRDeviceType::MetaQuest3Link;
case ovrpSystemHeadset_Meta_Link_Quest_3S:
return EOculusXRDeviceType::MetaQuest3SLink;
default:
break;
}
}
}
return EOculusXRDeviceType::OculusUnknown;
}
EOculusXRControllerType FOculusXRFunctionLibraryOVR::GetControllerType(EControllerHand deviceHand)
{
auto getOVRPHand = [](EControllerHand hand) {
switch (hand)
{
case EControllerHand::Left:
return ovrpHand::ovrpHand_Left;
case EControllerHand::Right:
return ovrpHand::ovrpHand_Right;
default:
break;
}
return ovrpHand::ovrpHand_None;
};
auto getEControllerType = [](ovrpInteractionProfile profile) {
switch (profile)
{
case ovrpInteractionProfile::ovrpInteractionProfile_Touch:
return EOculusXRControllerType::MetaQuestTouch;
case ovrpInteractionProfile::ovrpInteractionProfile_TouchPro:
return EOculusXRControllerType::MetaQuestTouchPro;
case ovrpInteractionProfile::ovrpInteractionProfile_TouchPlus:
return EOculusXRControllerType::MetaQuestTouchPlus;
default:
break;
}
return EOculusXRControllerType::None;
};
ovrpInteractionProfile interactionProfile = ovrpInteractionProfile::ovrpInteractionProfile_None;
ovrpHand hand = getOVRPHand(deviceHand);
if (hand == ovrpHand::ovrpHand_None)
return EOculusXRControllerType::Unknown;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetCurrentInteractionProfile(hand, &interactionProfile)))
{
return getEControllerType(interactionProfile);
}
return EOculusXRControllerType::Unknown;
}
TArray<float> FOculusXRFunctionLibraryOVR::GetAvailableDisplayFrequencies()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
int NumberOfFrequencies;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemDisplayAvailableFrequencies(nullptr, &NumberOfFrequencies)))
{
TArray<float> freqArray;
freqArray.SetNum(NumberOfFrequencies);
FOculusXRHMDModule::GetPluginWrapper().GetSystemDisplayAvailableFrequencies(freqArray.GetData(), &NumberOfFrequencies);
return freqArray;
}
}
return TArray<float>();
}
float FOculusXRFunctionLibraryOVR::GetCurrentDisplayFrequency()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
float Frequency;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemDisplayFrequency2(&Frequency)))
{
return Frequency;
}
}
return 0.0f;
}
void FOculusXRFunctionLibraryOVR::SetDisplayFrequency(float RequestedFrequency)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
FOculusXRHMDModule::GetPluginWrapper().SetSystemDisplayFrequency(RequestedFrequency);
}
}
void FOculusXRFunctionLibraryOVR::EnablePositionTracking(bool bPositionTracking)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
FOculusXRHMDModule::GetPluginWrapper().SetTrackingPositionEnabled2(bPositionTracking);
}
}
void FOculusXRFunctionLibraryOVR::EnableOrientationTracking(bool bOrientationTracking)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
FOculusXRHMDModule::GetPluginWrapper().SetTrackingOrientationEnabled2(bOrientationTracking);
}
}
void FOculusXRFunctionLibraryOVR::SetColorScaleAndOffset(FLinearColor ColorScale, FLinearColor ColorOffset, bool bApplyToAllLayers)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->SetColorScaleAndOffset(ColorScale, ColorOffset, bApplyToAllLayers);
}
}
class IStereoLayers* FOculusXRFunctionLibraryOVR::GetStereoLayers()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
return OculusXRHMD;
}
return nullptr;
}
static ovrpBoundaryType ToOvrpBoundaryType(EOculusXRBoundaryType Source)
{
switch (Source)
{
case EOculusXRBoundaryType::Boundary_PlayArea:
return ovrpBoundary_PlayArea;
case EOculusXRBoundaryType::Boundary_Outer:
default:
return ovrpBoundary_Outer;
}
}
bool FOculusXRFunctionLibraryOVR::IsGuardianConfigured()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool boundaryConfigured;
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryConfigured2(&boundaryConfigured)) && boundaryConfigured;
}
return false;
}
bool FOculusXRFunctionLibraryOVR::IsGuardianDisplayed()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool boundaryVisible;
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryVisible2(&boundaryVisible)) && boundaryVisible;
}
return false;
}
TArray<FVector> FOculusXRFunctionLibraryOVR::GetGuardianPoints(EOculusXRBoundaryType BoundaryType, bool UsePawnSpace /* = false */)
{
TArray<FVector> BoundaryPointList;
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool bBoundaryConfigured = false;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryConfigured2(&bBoundaryConfigured)) && bBoundaryConfigured)
{
ovrpBoundaryType obt = ToOvrpBoundaryType(BoundaryType);
int NumPoints = 0;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryGeometry3(obt, nullptr, &NumPoints)))
{
// allocate points
const int BufferSize = NumPoints;
ovrpVector3f* BoundaryPoints = new ovrpVector3f[BufferSize];
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryGeometry3(obt, BoundaryPoints, &NumPoints)))
{
NumPoints = FMath::Min(BufferSize, NumPoints);
check(NumPoints <= BufferSize); // For static analyzer
BoundaryPointList.Reserve(NumPoints);
for (int i = 0; i < NumPoints; i++)
{
FVector point;
if (UsePawnSpace)
{
point = OculusXRHMD->ConvertVector_M2U(BoundaryPoints[i]);
}
else
{
point = OculusXRHMD->ScaleAndMovePointWithPlayer(BoundaryPoints[i]);
}
BoundaryPointList.Add(point);
}
}
delete[] BoundaryPoints;
}
}
}
return BoundaryPointList;
}
FVector FOculusXRFunctionLibraryOVR::GetGuardianDimensions(EOculusXRBoundaryType BoundaryType)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBoundaryType obt = ToOvrpBoundaryType(BoundaryType);
ovrpVector3f Dimensions;
if (OVRP_FAILURE(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryDimensions2(obt, &Dimensions)))
return FVector::ZeroVector;
Dimensions.z *= -1.0;
return OculusXRHMD->ConvertVector_M2U(Dimensions);
}
return FVector::ZeroVector;
}
FTransform FOculusXRFunctionLibraryOVR::GetPlayAreaTransform()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool bBoundaryConfigured = false;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryConfigured2(&bBoundaryConfigured)) && bBoundaryConfigured)
{
int NumPoints = 4;
ovrpVector3f BoundaryPoints[4];
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetBoundaryGeometry3(ovrpBoundary_PlayArea, BoundaryPoints, &NumPoints)))
{
FVector ConvertedPoints[4];
for (int i = 0; i < NumPoints; i++)
{
ConvertedPoints[i] = OculusXRHMD->ScaleAndMovePointWithPlayer(BoundaryPoints[i]);
}
float metersScale = OculusXRHMD->GetWorldToMetersScale();
FVector Edge = ConvertedPoints[1] - ConvertedPoints[0];
float Angle = FMath::Acos((Edge).GetSafeNormal() | FVector::RightVector);
FQuat Rotation(FVector::UpVector, Edge.X < 0 ? Angle : -Angle);
FVector Position = (ConvertedPoints[0] + ConvertedPoints[1] + ConvertedPoints[2] + ConvertedPoints[3]) / 4;
FVector Scale(FVector::Distance(ConvertedPoints[3], ConvertedPoints[0]) / metersScale, FVector::Distance(ConvertedPoints[1], ConvertedPoints[0]) / metersScale, 1.0);
return FTransform(Rotation, Position, Scale);
}
}
}
return FTransform();
}
FOculusXRGuardianTestResult FOculusXRFunctionLibraryOVR::GetPointGuardianIntersection(const FVector Point, EOculusXRBoundaryType BoundaryType)
{
FOculusXRGuardianTestResult InteractionInfo;
memset(&InteractionInfo, 0, sizeof(FOculusXRGuardianTestResult));
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpVector3f OvrpPoint = OculusXRHMD->WorldLocationToOculusPoint(Point);
ovrpBoundaryType OvrpBoundaryType = ToOvrpBoundaryType(BoundaryType);
ovrpBoundaryTestResult InteractionResult;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().TestBoundaryPoint2(OvrpPoint, OvrpBoundaryType, &InteractionResult)))
{
InteractionInfo.IsTriggering = (InteractionResult.IsTriggering != 0);
InteractionInfo.ClosestDistance = OculusXRHMD->ConvertFloat_M2U(InteractionResult.ClosestDistance);
InteractionInfo.ClosestPoint = OculusXRHMD->ScaleAndMovePointWithPlayer(InteractionResult.ClosestPoint);
InteractionInfo.ClosestPointNormal = OculusXRHMD->ConvertVector_M2U(InteractionResult.ClosestPointNormal);
InteractionInfo.DeviceType = EOculusXRTrackedDeviceType::None;
}
}
return InteractionInfo;
}
FOculusXRGuardianTestResult FOculusXRFunctionLibraryOVR::GetNodeGuardianIntersection(EOculusXRTrackedDeviceType DeviceType, EOculusXRBoundaryType BoundaryType)
{
FOculusXRGuardianTestResult InteractionInfo;
memset(&InteractionInfo, 0, sizeof(FOculusXRGuardianTestResult));
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpNode OvrpNode = OculusXRHMD::ToOvrpNode(DeviceType);
ovrpBoundaryType OvrpBoundaryType = ToOvrpBoundaryType(BoundaryType);
ovrpBoundaryTestResult TestResult;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().TestBoundaryNode2(OvrpNode, ovrpBoundary_PlayArea, &TestResult)) && TestResult.IsTriggering)
{
InteractionInfo.IsTriggering = true;
InteractionInfo.DeviceType = OculusXRHMD::ToEOculusXRTrackedDeviceType(OvrpNode);
InteractionInfo.ClosestDistance = OculusXRHMD->ConvertFloat_M2U(TestResult.ClosestDistance);
InteractionInfo.ClosestPoint = OculusXRHMD->ScaleAndMovePointWithPlayer(TestResult.ClosestPoint);
InteractionInfo.ClosestPointNormal = OculusXRHMD->ConvertVector_M2U(TestResult.ClosestPointNormal);
}
}
return InteractionInfo;
}
void FOculusXRFunctionLibraryOVR::SetGuardianVisibility(bool GuardianVisible)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
FOculusXRHMDModule::GetPluginWrapper().SetBoundaryVisible2(GuardianVisible);
}
}
bool FOculusXRFunctionLibraryOVR::GetSystemHmd3DofModeEnabled()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpBool enabled;
return OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemHmd3DofModeEnabled(&enabled)) && enabled;
}
return false;
}
EOculusXRColorSpace FOculusXRFunctionLibraryOVR::GetHmdColorDesc()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpColorSpace HmdColorSpace;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetHmdColorDesc(&HmdColorSpace)))
{
return (EOculusXRColorSpace)HmdColorSpace;
}
}
return EOculusXRColorSpace::Unknown;
}
void FOculusXRFunctionLibraryOVR::SetClientColorDesc(EOculusXRColorSpace ColorSpace)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpColorSpace ClientColorSpace = (ovrpColorSpace)ColorSpace;
#if PLATFORM_ANDROID
if (ClientColorSpace == ovrpColorSpace_Unknown)
{
ClientColorSpace = ovrpColorSpace_Quest;
}
#endif
FOculusXRHMDModule::GetPluginWrapper().SetClientColorDesc(ClientColorSpace);
}
}
void FOculusXRFunctionLibraryOVR::SetLocalDimmingOn(bool LocalDimmingOn)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
UE_LOG(LogHMD, Log, TEXT("SetLocalDimmingOn %d"), LocalDimmingOn);
FOculusXRHMDModule::GetPluginWrapper().SetLocalDimming(LocalDimmingOn);
}
}
bool FOculusXRFunctionLibraryOVR::IsPassthroughSupported()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpInsightPassthroughCapabilityFlags capabilities;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetPassthroughCapabilityFlags(&capabilities)))
{
return (capabilities & ovrpInsightPassthroughCapabilityFlags::ovrpInsightPassthroughCapabilityFlags_Passthrough)
== ovrpInsightPassthroughCapabilityFlags::ovrpInsightPassthroughCapabilityFlags_Passthrough;
}
return false;
}
return false;
}
bool FOculusXRFunctionLibraryOVR::IsColorPassthroughSupported()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
ovrpInsightPassthroughCapabilityFlags capabilities;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetPassthroughCapabilityFlags(&capabilities)))
{
return (capabilities & ovrpInsightPassthroughCapabilityFlags::ovrpInsightPassthroughCapabilityFlags_Color)
== ovrpInsightPassthroughCapabilityFlags::ovrpInsightPassthroughCapabilityFlags_Color;
}
return false;
}
return false;
}
void FOculusXRFunctionLibraryOVR::StartEnvironmentDepth()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
int CreateFlags = 0;
OculusXRHMD->StartEnvironmentDepth(CreateFlags);
}
}
void FOculusXRFunctionLibraryOVR::StopEnvironmentDepth()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->StopEnvironmentDepth();
}
}
bool FOculusXRFunctionLibraryOVR::IsEnvironmentDepthStarted()
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
return OculusXRHMD->IsEnvironmentDepthStarted();
}
return false;
}
void FOculusXRFunctionLibraryOVR::SetEnvironmentDepthHandRemoval(bool RemoveHands)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->SetEnvironmentDepthHandRemoval(RemoveHands);
}
}
void FOculusXRFunctionLibraryOVR::SetXROcclusionsMode(UObject* WorldContextObject, EOculusXROcclusionsMode Mode)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
OculusXRHMD->EnableHardOcclusions(Mode == EOculusXROcclusionsMode::HardOcclusions_Deprecated);
OculusXRHMD->EnableSoftOcclusions(Mode == EOculusXROcclusionsMode::SoftOcclusions);
}
#if defined(WITH_OCULUS_BRANCH)
WorldContextObject->GetWorld()->Scene->SetEnableXRPassthroughSoftOcclusions(Mode == EOculusXROcclusionsMode::SoftOcclusions);
#else
ensureMsgf(Mode != EOculusXROcclusionsMode::SoftOcclusions, TEXT("Soft occlusions are only supported with the Oculus branch of the Unreal Engine"));
#endif
}
void FOculusXRFunctionLibraryOVR::SetEyeBufferSharpenType(EOculusXREyeBufferSharpenType EyeBufferSharpenType)
{
OculusXRHMD::FOculusXRHMD* OculusXRHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusXRHMD != nullptr)
{
switch (EyeBufferSharpenType)
{
case EOculusXREyeBufferSharpenType::SLST_Normal:
FOculusXRHMDModule::GetPluginWrapper().SetEyeBufferSharpenType(ovrpLayerSubmitFlag_EfficientSharpen);
break;
case EOculusXREyeBufferSharpenType::SLST_Quality:
FOculusXRHMDModule::GetPluginWrapper().SetEyeBufferSharpenType(ovrpLayerSubmitFlag_QualitySharpen);
break;
case EOculusXREyeBufferSharpenType::SLST_Auto:
FOculusXRHMDModule::GetPluginWrapper().SetEyeBufferSharpenType(ovrpLayerSubmitFlag_AutoLayerFilter);
break;
default:
FOculusXRHMDModule::GetPluginWrapper().SetEyeBufferSharpenType(ovrpLayerSubmitFlags(0));
break;
}
}
}
bool FOculusXRFunctionLibraryOVR::IsPassthroughRecommended()
{
const OculusXRHMD::FOculusXRHMD* OculusHMD = OculusXRHMD::FOculusXRHMD::GetOculusXRHMD();
if (OculusHMD != nullptr)
{
ovrpPassthroughPreferences Preferences;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetPassthroughPreferences(&Preferences)))
{
return (Preferences.Flags & ovrpPassthroughPreferenceFlags::ovrpPassthroughPreferenceFlags_DefaultToActive)
== ovrpPassthroughPreferenceFlags::ovrpPassthroughPreferenceFlags_DefaultToActive;
};
}
return false;
}
void FOculusXRFunctionLibraryOVR::GetOpenXRInstanceProcAddrFunc(void** func)
{
FOculusXRHMDModule::GetPluginWrapper().GetOpenXRInstanceProcAddrFunc(func);
}
void FOculusXRFunctionLibraryOVR::GetNativeOpenXRHandles(uint64_t* OutXrInstance, uint64_t* OutXrSession)
{
FOculusXRHMDModule::GetPluginWrapper().GetNativeOpenXRHandles((ovrpUInt64*)OutXrInstance, (ovrpUInt64*)OutXrSession);
}
void FOculusXRFunctionLibraryOVR::GetAppSpace(uint64_t* OutAppSpace)
{
FOculusXRHMDModule::GetPluginWrapper().GetAppSpace((ovrpUInt64*)OutAppSpace);
}
void FOculusXRFunctionLibraryOVR::GetNextPredictedDisplayTime(uint64_t* NextPredictedDisplayTime)
{
double Time = 0;
FOculusXRHMDModule::GetPluginWrapper().GetPredictedDisplayTime(OVRP_CURRENT_FRAMEINDEX, &Time);
*NextPredictedDisplayTime = OculusXR::ToXrTime(Time);
}
void FOculusXRFunctionLibraryOVR::RegisterOpenXrEventHandler(void (*OpenXrEventHandler)(void* data, void* context), void* Context)
{
FOculusXRHMDModule::GetPluginWrapper().RegisterOpenXREventHandler(OpenXrEventHandler, Context);
}
void FOculusXRFunctionLibraryOVR::UnregisterOpenXrEventHandler(void (*OpenXrEventHandler)(void* data, void* context))
{
FOculusXRHMDModule::GetPluginWrapper().UnregisterOpenXREventHandler(OpenXrEventHandler);
}
} // namespace OculusXRHMD

View File

@@ -0,0 +1,74 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "OculusXRFunctionLibrary.h"
namespace OculusXRHMD
{
class FOculusXRFunctionLibraryOVR : public IOculusXRFunctionLibrary
{
public:
virtual void GetPose(FRotator& DeviceRotation, FVector& DevicePosition, FVector& NeckPosition) override;
virtual void SetBaseRotationAndBaseOffsetInMeters(FRotator Rotation, FVector BaseOffsetInMeters, EOrientPositionSelector::Type Options) override;
virtual void GetBaseRotationAndBaseOffsetInMeters(FRotator& OutRotation, FVector& OutBaseOffsetInMeters) override;
virtual void GetRawSensorData(FVector& AngularAcceleration, FVector& LinearAcceleration, FVector& AngularVelocity, FVector& LinearVelocity, float& TimeInSeconds, EOculusXRTrackedDeviceType DeviceType) override;
virtual bool IsDeviceTracked(EOculusXRTrackedDeviceType DeviceType) override;
virtual void GetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel& CpuPerfLevel, EOculusXRProcessorPerformanceLevel& GpuPerfLevel) override;
virtual void SetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel CpuPerfLevel, EOculusXRProcessorPerformanceLevel GpuPerfLevel) override;
virtual bool GetUserProfile(FOculusXRHmdUserProfile& Profile) override;
virtual void SetBaseRotationAndPositionOffset(FRotator BaseRot, FVector PosOffset, EOrientPositionSelector::Type Options) override;
virtual void GetBaseRotationAndPositionOffset(FRotator& OutRot, FVector& OutPosOffset) override;
virtual void AddLoadingSplashScreen(class UTexture2D* Texture, FVector TranslationInMeters, FRotator Rotation, FVector2D SizeInMeters, FRotator DeltaRotation, bool bClearBeforeAdd) override;
virtual void ClearLoadingSplashScreens() override;
virtual bool HasInputFocus() override;
virtual bool HasSystemOverlayPresent() override;
virtual void GetGPUUtilization(bool& IsGPUAvailable, float& GPUUtilization) override;
virtual float GetGPUFrameTime() override;
virtual void GetPerformanceMetrics(FOculusXRPerformanceMetrics& PerformanceMetrics) override;
virtual EOculusXRFoveatedRenderingMethod GetFoveatedRenderingMethod() override;
virtual void SetFoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod Method) override;
virtual void SetFoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel level, bool isDynamic) override;
virtual EOculusXRFoveatedRenderingLevel GetFoveatedRenderingLevel() override;
virtual bool GetEyeTrackedFoveatedRenderingSupported() override;
virtual FString GetDeviceName() override;
virtual EOculusXRDeviceType GetDeviceType() override;
virtual EOculusXRControllerType GetControllerType(EControllerHand deviceHand) override;
virtual TArray<float> GetAvailableDisplayFrequencies() override;
virtual float GetCurrentDisplayFrequency() override;
virtual void SetDisplayFrequency(float RequestedFrequency) override;
virtual void EnablePositionTracking(bool bPositionTracking) override;
virtual void EnableOrientationTracking(bool bOrientationTracking) override;
virtual void SetColorScaleAndOffset(FLinearColor ColorScale, FLinearColor ColorOffset, bool bApplyToAllLayers) override;
virtual class IStereoLayers* GetStereoLayers() override;
virtual bool IsGuardianConfigured() override;
virtual bool IsGuardianDisplayed() override;
virtual TArray<FVector> GetGuardianPoints(EOculusXRBoundaryType BoundaryType, bool UsePawnSpace /* = false */) override;
virtual FVector GetGuardianDimensions(EOculusXRBoundaryType BoundaryType) override;
virtual FTransform GetPlayAreaTransform() override;
virtual FOculusXRGuardianTestResult GetPointGuardianIntersection(const FVector Point, EOculusXRBoundaryType BoundaryType) override;
virtual FOculusXRGuardianTestResult GetNodeGuardianIntersection(EOculusXRTrackedDeviceType DeviceType, EOculusXRBoundaryType BoundaryType) override;
virtual void SetGuardianVisibility(bool GuardianVisible) override;
virtual bool GetSystemHmd3DofModeEnabled() override;
virtual EOculusXRColorSpace GetHmdColorDesc() override;
virtual void SetClientColorDesc(EOculusXRColorSpace ColorSpace) override;
virtual void SetLocalDimmingOn(bool LocalDimmingOn) override;
virtual bool IsPassthroughSupported() override;
virtual bool IsColorPassthroughSupported() override;
virtual void StartEnvironmentDepth() override;
virtual void StopEnvironmentDepth() override;
virtual bool IsEnvironmentDepthStarted() override;
virtual void SetEnvironmentDepthHandRemoval(bool RemoveHands) override;
virtual void SetXROcclusionsMode(UObject* WorldContextObject, EOculusXROcclusionsMode Mode) override;
virtual void SetEyeBufferSharpenType(EOculusXREyeBufferSharpenType EyeBufferSharpenType) override;
virtual bool IsPassthroughRecommended() override;
virtual void GetOpenXRInstanceProcAddrFunc(void** Func) override;
virtual void GetNativeOpenXRHandles(uint64_t* OutXrInstance, uint64_t* OutXrSession) override;
virtual void GetAppSpace(uint64_t* OutAppSpace) override;
virtual void GetNextPredictedDisplayTime(uint64_t* NextPredictedDisplayTime) override;
virtual void RegisterOpenXrEventHandler(void (*OpenXrEventHandler)(void* data, void* context), void* Context) override;
virtual void UnregisterOpenXrEventHandler(void (*OpenXrEventHandler)(void* data, void* context)) override;
};
} // namespace OculusXRHMD

View File

@@ -0,0 +1,632 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRFunctionLibraryOpenXR.h"
#include "OpenXR\OculusXRXRFunctions.h"
#include "OculusXRHMD.h"
#include "IOpenXRHMD.h"
#include "OpenXR\OculusXROpenXRUtilities.h"
#include "IOpenXRHMD.h"
#include "IOpenXRHMDModule.h"
#include "OpenXRBlueprintFunctionLibrary.h"
namespace
{
struct HeadModelParms
{
float HeadModelDepth;
float HeadModelHeight;
};
// CompositorOpenXR Constants
const HeadModelParms DefaultHeadModel = {
0.0805f,
0.0750f,
};
FVector2D GetUserNeckEyeDistance()
{
return FVector2D(DefaultHeadModel.HeadModelDepth, DefaultHeadModel.HeadModelHeight);
}
} // namespace
#define NOT_IMPLEMENTED() \
UE_LOG(LogHMD, Log, TEXT("%s Not Implemented In OpenXR"), StringCast<TCHAR>(__FUNCTION__).Get());
namespace OculusXRHMD
{
void FOculusXRFunctionLibraryOpenXR::GetPose(FRotator& DeviceRotation, FVector& DevicePosition, FVector& NeckPosition)
{
FQuat HeadOrientation = FQuat::Identity;
FVector HeadPosition = FVector::ZeroVector;
GEngine->XRSystem->GetCurrentPose(IXRTrackingSystem::HMDDeviceId, HeadOrientation, HeadPosition);
FVector LocalNeckPosition = HeadOrientation.Inverse().RotateVector(HeadPosition);
const FVector2D NeckEyeDistance = GetUserNeckEyeDistance();
const float WorldToMetersScale = GEngine->XRSystem->GetWorldToMetersScale();
LocalNeckPosition.X -= NeckEyeDistance.X * WorldToMetersScale;
LocalNeckPosition.Z -= NeckEyeDistance.Y * WorldToMetersScale;
DeviceRotation = HeadOrientation.Rotator();
DevicePosition = HeadPosition;
NeckPosition = LocalNeckPosition;
}
void FOculusXRFunctionLibraryOpenXR::SetBaseRotationAndBaseOffsetInMeters(FRotator Rotation, FVector BaseOffsetInMeters, EOrientPositionSelector::Type Options)
{
if (GEngine->XRSystem.IsValid())
{
if ((Options == EOrientPositionSelector::Orientation) || (Options == EOrientPositionSelector::OrientationAndPosition))
{
GEngine->XRSystem->SetBaseRotation(Rotation);
}
if ((Options == EOrientPositionSelector::Position) || (Options == EOrientPositionSelector::OrientationAndPosition))
{
GEngine->XRSystem->SetBasePosition(BaseOffsetInMeters);
}
}
}
void FOculusXRFunctionLibraryOpenXR::GetBaseRotationAndBaseOffsetInMeters(FRotator& OutRotation, FVector& OutBaseOffsetInMeters)
{
if (GEngine->XRSystem.IsValid())
{
OutRotation = GEngine->XRSystem->GetBaseRotation();
OutBaseOffsetInMeters = GEngine->XRSystem->GetBasePosition();
}
else
{
OutRotation = FRotator::ZeroRotator;
OutBaseOffsetInMeters = FVector::ZeroVector;
}
}
void FOculusXRFunctionLibraryOpenXR::GetRawSensorData(FVector& AngularAcceleration, FVector& LinearAcceleration, FVector& AngularVelocity, FVector& LinearVelocity, float& TimeInSeconds, EOculusXRTrackedDeviceType DeviceType)
{
NOT_IMPLEMENTED();
}
static IMotionController* GetMotionController()
{
TArray<IMotionController*> MotionControllers;
MotionControllers = IModularFeatures::Get().GetModularFeatureImplementations<IMotionController>(IMotionController::GetModularFeatureName());
for (auto MotionController : MotionControllers)
{
FName MotionControllerName("OpenXR");
if (MotionController != nullptr && MotionController->GetMotionControllerDeviceTypeName() == MotionControllerName)
{
return MotionController;
}
}
return nullptr;
}
static bool IsControllerTracked(FName MotionSource)
{
IMotionController* MotionController = GetMotionController();
if (MotionController != nullptr)
{
const int32_t ControllerIndex = 0;
return MotionController->GetControllerTrackingStatus(ControllerIndex, MotionSource) == ETrackingStatus::Tracked;
}
return false;
}
bool FOculusXRFunctionLibraryOpenXR::IsDeviceTracked(EOculusXRTrackedDeviceType DeviceType)
{
bool bIsDeviceTracked = false;
if (GEngine->XRSystem.IsValid())
{
switch (DeviceType)
{
case EOculusXRTrackedDeviceType::None:
break;
case EOculusXRTrackedDeviceType::HMD:
if (GEngine->XRSystem.IsValid())
{
bIsDeviceTracked = GEngine->XRSystem->IsTracking(IXRTrackingSystem::HMDDeviceId);
}
break;
case EOculusXRTrackedDeviceType::LTouch:
{
bIsDeviceTracked = IsControllerTracked(FName("LeftAim"));
}
break;
case EOculusXRTrackedDeviceType::RTouch:
{
bIsDeviceTracked = IsControllerTracked(FName("RightAim"));
}
break;
default:
break;
}
}
return bIsDeviceTracked;
}
void FOculusXRFunctionLibraryOpenXR::GetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel& CpuPerfLevel, EOculusXRProcessorPerformanceLevel& GpuPerfLevel)
{
OculusXR::FPerformanceExtensionPlugin& PerfPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetPerformanceExtensionPlugin();
PerfPlugin.GetSuggestedCpuAndGpuPerformanceLevels(CpuPerfLevel, GpuPerfLevel);
}
void FOculusXRFunctionLibraryOpenXR::SetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel CpuPerfLevel, EOculusXRProcessorPerformanceLevel GpuPerfLevel)
{
OculusXR::FPerformanceExtensionPlugin& PerfPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetPerformanceExtensionPlugin();
PerfPlugin.SetSuggestedCpuAndGpuPerformanceLevels(CpuPerfLevel, GpuPerfLevel);
}
bool FOculusXRFunctionLibraryOpenXR::GetUserProfile(FOculusXRHmdUserProfile& Profile)
{
IXRTrackingSystem* TrackingSystem = OculusXR::GetOpenXRTrackingSystem();
if (TrackingSystem != nullptr)
{
const IHeadMountedDisplay* Hmd = TrackingSystem->GetHMDDevice();
if (Hmd != nullptr)
{
const float IPD = Hmd->GetInterpupillaryDistance();
Profile.Name = "";
Profile.Gender = "Unknown";
Profile.PlayerHeight = 0.0f;
Profile.EyeHeight = DefaultHeadModel.HeadModelHeight;
Profile.IPD = IPD;
Profile.NeckToEyeDistance = FVector2D(DefaultHeadModel.HeadModelDepth, 0.0f);
}
return true;
}
return false;
}
void FOculusXRFunctionLibraryOpenXR::SetBaseRotationAndPositionOffset(FRotator BaseRot, FVector PosOffset, EOrientPositionSelector::Type Options)
{
NOT_IMPLEMENTED();
}
void FOculusXRFunctionLibraryOpenXR::GetBaseRotationAndPositionOffset(FRotator& OutRot, FVector& OutPosOffset)
{
NOT_IMPLEMENTED();
}
void FOculusXRFunctionLibraryOpenXR::AddLoadingSplashScreen(class UTexture2D* Texture, FVector TranslationInMeters, FRotator Rotation, FVector2D SizeInMeters, FRotator DeltaRotation, bool bClearBeforeAdd)
{
NOT_IMPLEMENTED();
}
void FOculusXRFunctionLibraryOpenXR::ClearLoadingSplashScreens()
{
NOT_IMPLEMENTED();
}
bool FOculusXRFunctionLibraryOpenXR::HasInputFocus()
{
IXRTrackingSystem* TrackingSystem = OculusXR::GetOpenXRTrackingSystem();
if (TrackingSystem != nullptr)
{
IOpenXRHMD* OpenXRHMD = TrackingSystem->GetIOpenXRHMD();
if (OpenXRHMD != nullptr)
{
return OpenXRHMD->IsFocused();
}
}
return false;
}
bool FOculusXRFunctionLibraryOpenXR::HasSystemOverlayPresent()
{
IXRTrackingSystem* TrackingSystem = OculusXR::GetOpenXRTrackingSystem();
if (TrackingSystem != nullptr)
{
IOpenXRHMD* OpenXRHMD = TrackingSystem->GetIOpenXRHMD();
if (OpenXRHMD != nullptr)
{
return !OpenXRHMD->IsFocused();
}
}
return false;
}
void FOculusXRFunctionLibraryOpenXR::GetGPUUtilization(bool& IsGPUAvailable, float& GPUUtilization)
{
OculusXR::FPerformanceExtensionPlugin& PerfPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetPerformanceExtensionPlugin();
const FOculusXRPerformanceMetrics& PerformanceMetrics = PerfPlugin.GetPerformanceMetrics();
if (PerfPlugin.IsPerformanceMetricsSupported(OculusXR::GPUUtilizationFloat))
{
GPUUtilization = PerformanceMetrics.GpuUtil;
IsGPUAvailable = true;
}
IsGPUAvailable = false;
}
float FOculusXRFunctionLibraryOpenXR::GetGPUFrameTime()
{
OculusXR::FPerformanceExtensionPlugin& PerfPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetPerformanceExtensionPlugin();
const FOculusXRPerformanceMetrics& PerformanceMetrics = PerfPlugin.GetPerformanceMetrics();
return PerformanceMetrics.AppGpuTime;
}
void FOculusXRFunctionLibraryOpenXR::GetPerformanceMetrics(FOculusXRPerformanceMetrics& PerformanceMetrics)
{
OculusXR::FPerformanceExtensionPlugin& PerfPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetPerformanceExtensionPlugin();
PerformanceMetrics = PerfPlugin.GetPerformanceMetrics();
}
EOculusXRFoveatedRenderingMethod FOculusXRFunctionLibraryOpenXR::GetFoveatedRenderingMethod()
{
NOT_IMPLEMENTED();
return EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering;
}
void FOculusXRFunctionLibraryOpenXR::SetFoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod Method)
{
NOT_IMPLEMENTED();
}
void FOculusXRFunctionLibraryOpenXR::SetFoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel level, bool isDynamic)
{
if (IConsoleVariable* FoveationLevelCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("xr.OpenXRFBFoveationLevel")))
{
FoveationLevelCVar->Set(static_cast<int>(level));
}
if (IConsoleVariable* FoveationDynamicCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("xr.OpenXRFBFoveationDynamic")))
{
FoveationDynamicCVar->Set(isDynamic);
}
}
EOculusXRFoveatedRenderingLevel FOculusXRFunctionLibraryOpenXR::GetFoveatedRenderingLevel()
{
const auto FoveationLevelCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("xr.OpenXRFBFoveationLevel"));
return FoveationLevelCVar ? static_cast<EOculusXRFoveatedRenderingLevel>(FoveationLevelCVar->GetValueOnAnyThread()) : EOculusXRFoveatedRenderingLevel::Off;
}
bool FOculusXRFunctionLibraryOpenXR::GetEyeTrackedFoveatedRenderingSupported()
{
return false;
}
FString FOculusXRFunctionLibraryOpenXR::GetDeviceName()
{
// deprecated
return FString();
}
EOculusXRDeviceType FOculusXRFunctionLibraryOpenXR::GetDeviceType()
{
#if defined(WITH_OCULUS_BRANCH)
OculusXR::FSystemInfoExtensionPlugin& SystemPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetSystemInfoExtensionPlugin();
return SystemPlugin.GetDeviceType();
#else // defined(WITH_OCULUS_BRANCH)
NOT_IMPLEMENTED();
return EOculusXRDeviceType::OculusUnknown;
#endif // defined(WITH_OCULUS_BRANCH)
}
EOculusXRControllerType FOculusXRFunctionLibraryOpenXR::GetControllerType(EControllerHand deviceHand)
{
#if defined(WITH_OCULUS_BRANCH)
OculusXR::FSystemInfoExtensionPlugin& SystemPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetSystemInfoExtensionPlugin();
return SystemPlugin.GetControllerType(deviceHand);
#else // defined(WITH_OCULUS_BRANCH)
NOT_IMPLEMENTED();
return EOculusXRControllerType::None;
#endif // defined(WITH_OCULUS_BRANCH)
}
TArray<float> FOculusXRFunctionLibraryOpenXR::GetAvailableDisplayFrequencies()
{
#if defined(WITH_OCULUS_BRANCH)
OculusXR::FSystemInfoExtensionPlugin& SystemPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetSystemInfoExtensionPlugin();
return SystemPlugin.GetSystemDisplayAvailableFrequencies();
#else // defined(WITH_OCULUS_BRANCH)
NOT_IMPLEMENTED();
return TArray<float>();
#endif // defined(WITH_OCULUS_BRANCH)
}
float FOculusXRFunctionLibraryOpenXR::GetCurrentDisplayFrequency()
{
#if defined(WITH_OCULUS_BRANCH)
OculusXR::FSystemInfoExtensionPlugin& SystemPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetSystemInfoExtensionPlugin();
return SystemPlugin.GetSystemDisplayFrequency();
#else // defined(WITH_OCULUS_BRANCH)
NOT_IMPLEMENTED();
return 0.0f;
#endif // defined(WITH_OCULUS_BRANCH)
}
void FOculusXRFunctionLibraryOpenXR::SetDisplayFrequency(float RequestedFrequency)
{
#if defined(WITH_OCULUS_BRANCH)
OculusXR::FSystemInfoExtensionPlugin& SystemPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetSystemInfoExtensionPlugin();
SystemPlugin.SetSystemDisplayFrequency(RequestedFrequency);
#else // defined(WITH_OCULUS_BRANCH)
NOT_IMPLEMENTED();
#endif // defined(WITH_OCULUS_BRANCH)
}
void FOculusXRFunctionLibraryOpenXR::EnablePositionTracking(bool bPositionTracking)
{
NOT_IMPLEMENTED();
}
void FOculusXRFunctionLibraryOpenXR::EnableOrientationTracking(bool bOrientationTracking)
{
NOT_IMPLEMENTED();
}
void FOculusXRFunctionLibraryOpenXR::SetColorScaleAndOffset(FLinearColor ColorScale, FLinearColor ColorOffset, bool bApplyToAllLayers)
{
OculusXR::FLayerExtensionPlugin& Plugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetLayerExtensionPlugin();
Plugin.SetColorScaleAndOffset(ColorScale, ColorOffset, bApplyToAllLayers);
}
class IStereoLayers* FOculusXRFunctionLibraryOpenXR::GetStereoLayers()
{
NOT_IMPLEMENTED();
return nullptr;
}
bool FOculusXRFunctionLibraryOpenXR::IsGuardianConfigured()
{
OculusXR::FGuardianExtensionPlugin& Plugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetGuardianExtensionPlugin();
return Plugin.IsGuardianConfigured();
}
bool FOculusXRFunctionLibraryOpenXR::IsGuardianDisplayed()
{
// deprecated
return false;
}
TArray<FVector> FOculusXRFunctionLibraryOpenXR::GetGuardianPoints(EOculusXRBoundaryType BoundaryType, bool UsePawnSpace /* = false */)
{
if (BoundaryType != EOculusXRBoundaryType::Boundary_PlayArea)
{
UE_LOG(LogHMD, Log, TEXT("GetGuardianPoints: Only Boundary_PlayArea is applicable in OpenXR"));
return TArray<FVector>();
}
TArray<FVector> BoundaryPoints;
IXRTrackingSystem* TrackingSystem = OculusXR::GetOpenXRTrackingSystem();
if (TrackingSystem != nullptr)
{
OculusXR::FGuardianExtensionPlugin& Plugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetGuardianExtensionPlugin();
Plugin.GetGuardianPoints(BoundaryPoints);
}
return BoundaryPoints;
}
FVector FOculusXRFunctionLibraryOpenXR::GetGuardianDimensions(EOculusXRBoundaryType BoundaryType)
{
OculusXR::FGuardianExtensionPlugin& Plugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetGuardianExtensionPlugin();
return Plugin.GetGuardianDimensions();
}
FTransform FOculusXRFunctionLibraryOpenXR::GetPlayAreaTransform()
{
IXRTrackingSystem* TrackingSystem = OculusXR::GetOpenXRTrackingSystem();
if (TrackingSystem != nullptr)
{
TArray<FVector> BoundaryPoints;
OculusXR::FGuardianExtensionPlugin& Plugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetGuardianExtensionPlugin();
if (Plugin.GetGuardianPoints(BoundaryPoints))
{
check(BoundaryPoints.Num() == 4);
const float WorldToMetersScale = TrackingSystem->GetWorldToMetersScale();
const FVector Edge = BoundaryPoints[1] - BoundaryPoints[0];
const float Angle = FMath::Acos((Edge).GetSafeNormal() | FVector::RightVector);
const FQuat Rotation(FVector::UpVector, Edge.X < 0 ? Angle : -Angle);
const FVector Position = (BoundaryPoints[0] + BoundaryPoints[1] + BoundaryPoints[2] + BoundaryPoints[3]) / 4;
const FVector Scale(FVector::Distance(BoundaryPoints[3], BoundaryPoints[0]) / WorldToMetersScale, FVector::Distance(BoundaryPoints[1], BoundaryPoints[0]) / WorldToMetersScale, 1.0);
return FTransform(Rotation, Position, Scale);
}
}
return FTransform();
}
FOculusXRGuardianTestResult FOculusXRFunctionLibraryOpenXR::GetPointGuardianIntersection(const FVector Point, EOculusXRBoundaryType BoundaryType)
{
// deprecated
FOculusXRGuardianTestResult InteractionInfo;
memset(&InteractionInfo, 0, sizeof(FOculusXRGuardianTestResult));
return InteractionInfo;
}
FOculusXRGuardianTestResult FOculusXRFunctionLibraryOpenXR::GetNodeGuardianIntersection(EOculusXRTrackedDeviceType DeviceType, EOculusXRBoundaryType BoundaryType)
{
// deprecated
FOculusXRGuardianTestResult InteractionInfo;
memset(&InteractionInfo, 0, sizeof(FOculusXRGuardianTestResult));
return InteractionInfo;
}
void FOculusXRFunctionLibraryOpenXR::SetGuardianVisibility(bool GuardianVisible)
{
// deprecated
}
bool FOculusXRFunctionLibraryOpenXR::GetSystemHmd3DofModeEnabled()
{
NOT_IMPLEMENTED();
return false;
}
EOculusXRColorSpace FOculusXRFunctionLibraryOpenXR::GetHmdColorDesc()
{
#if defined(WITH_OCULUS_BRANCH)
OculusXR::FSystemInfoExtensionPlugin& SystemPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetSystemInfoExtensionPlugin();
return SystemPlugin.GetColorSpace();
#else // defined(WITH_OCULUS_BRANCH)
NOT_IMPLEMENTED();
return EOculusXRColorSpace::Unknown;
#endif // defined(WITH_OCULUS_BRANCH)
}
void FOculusXRFunctionLibraryOpenXR::SetClientColorDesc(EOculusXRColorSpace ColorSpace)
{
#if defined(WITH_OCULUS_BRANCH)
OculusXR::FSystemInfoExtensionPlugin& SystemPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetSystemInfoExtensionPlugin();
SystemPlugin.SetColorSpace(ColorSpace);
#else // defined(WITH_OCULUS_BRANCH)
NOT_IMPLEMENTED();
#endif // defined(WITH_OCULUS_BRANCH)
}
void FOculusXRFunctionLibraryOpenXR::SetLocalDimmingOn(bool LocalDimmingOn)
{
OculusXR::FLayerExtensionPlugin& Plugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetLayerExtensionPlugin();
return Plugin.SetEnableLocalDimming(LocalDimmingOn);
}
bool FOculusXRFunctionLibraryOpenXR::IsPassthroughSupported()
{
#if defined(WITH_OCULUS_BRANCH)
OculusXR::FSystemInfoExtensionPlugin& SystemPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetSystemInfoExtensionPlugin();
return SystemPlugin.IsPassthroughSupported();
#else // defined(WITH_OCULUS_BRANCH)
NOT_IMPLEMENTED();
return false;
#endif // defined(WITH_OCULUS_BRANCH)
}
bool FOculusXRFunctionLibraryOpenXR::IsColorPassthroughSupported()
{
#if defined(WITH_OCULUS_BRANCH)
OculusXR::FSystemInfoExtensionPlugin& SystemPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetSystemInfoExtensionPlugin();
return SystemPlugin.IsColorPassthroughSupported();
#else // defined(WITH_OCULUS_BRANCH)
NOT_IMPLEMENTED();
return false;
#endif // defined(WITH_OCULUS_BRANCH)
}
void FOculusXRFunctionLibraryOpenXR::StartEnvironmentDepth()
{
#ifdef WITH_OCULUS_BRANCH
auto& EnvDepthPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetEnvironmentDepthExtensionPlugin();
EnvDepthPlugin.StartEnvironmentDepth();
#endif
}
void FOculusXRFunctionLibraryOpenXR::StopEnvironmentDepth()
{
#ifdef WITH_OCULUS_BRANCH
auto& EnvDepthPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetEnvironmentDepthExtensionPlugin();
EnvDepthPlugin.StopEnvironmentDepth();
#endif
}
bool FOculusXRFunctionLibraryOpenXR::IsEnvironmentDepthStarted()
{
#ifdef WITH_OCULUS_BRANCH
const auto& EnvDepthPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetEnvironmentDepthExtensionPlugin();
return !EnvDepthPlugin.IsEnvironmentDepthStarted();
#else
return false;
#endif
}
void FOculusXRFunctionLibraryOpenXR::SetEnvironmentDepthHandRemoval(bool RemoveHands)
{
#ifdef WITH_OCULUS_BRANCH
auto& EnvDepthPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetEnvironmentDepthExtensionPlugin();
EnvDepthPlugin.SetEnvironmentDepthHandRemoval_RenderThread(RemoveHands);
#endif
}
void FOculusXRFunctionLibraryOpenXR::SetXROcclusionsMode(UObject* WorldContextObject, EOculusXROcclusionsMode Mode)
{
#ifdef WITH_OCULUS_BRANCH
auto& EnvDepthPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetEnvironmentDepthExtensionPlugin();
EnvDepthPlugin.SetXROcclusionsMode(WorldContextObject, Mode);
#endif
}
void FOculusXRFunctionLibraryOpenXR::SetEyeBufferSharpenType(EOculusXREyeBufferSharpenType EyeBufferSharpenType)
{
OculusXR::FLayerExtensionPlugin& Plugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetLayerExtensionPlugin();
Plugin.SetEyeBufferSharpenType(EyeBufferSharpenType);
}
bool FOculusXRFunctionLibraryOpenXR::IsPassthroughRecommended()
{
#if defined(WITH_OCULUS_BRANCH)
OculusXR::FSystemInfoExtensionPlugin& SystemPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetSystemInfoExtensionPlugin();
return SystemPlugin.IsPassthroughRecommended();
#else // defined(WITH_OCULUS_BRANCH)
NOT_IMPLEMENTED();
return false;
#endif // defined(WITH_OCULUS_BRANCH)
}
void FOculusXRFunctionLibraryOpenXR::GetOpenXRInstanceProcAddrFunc(void** func)
{
*func = (void*)OpenXRDynamicAPI::xrGetInstanceProcAddr;
}
void FOculusXRFunctionLibraryOpenXR::GetNativeOpenXRHandles(uint64_t* OutXrInstance, uint64_t* OutXrSession)
{
IXRTrackingSystem* TrackingSystem = OculusXR::GetOpenXRTrackingSystem();
if (TrackingSystem)
{
IOpenXRHMD* OpenXRHMD = TrackingSystem->GetIOpenXRHMD();
if (OpenXRHMD)
{
if (OutXrInstance)
{
*OutXrInstance = (uint64_t)OpenXRHMD->GetInstance();
}
if (OutXrSession)
{
*OutXrSession = (uint64_t)OpenXRHMD->GetSession();
}
}
}
}
void FOculusXRFunctionLibraryOpenXR::GetAppSpace(uint64_t* OutAppSpace)
{
IXRTrackingSystem* TrackingSystem = OculusXR::GetOpenXRTrackingSystem();
if (TrackingSystem)
{
IOpenXRHMD* OpenXRHMD = TrackingSystem->GetIOpenXRHMD();
if (OpenXRHMD)
{
*OutAppSpace = (uint64_t)OpenXRHMD->GetTrackingSpace();
}
}
}
void FOculusXRFunctionLibraryOpenXR::GetNextPredictedDisplayTime(uint64_t* NextPredictedDisplayTime)
{
IXRTrackingSystem* TrackingSystem = OculusXR::GetOpenXRTrackingSystem();
if (TrackingSystem)
{
IOpenXRHMD* OpenXRHMD = TrackingSystem->GetIOpenXRHMD();
if (OpenXRHMD)
{
*NextPredictedDisplayTime = OpenXRHMD->GetDisplayTime();
}
}
}
void FOculusXRFunctionLibraryOpenXR::RegisterOpenXrEventHandler(void (*OpenXrEventHandler)(void* data, void* context), void* Context)
{
ensureMsgf(false, TEXT("RegisterOpenXrEventHandler is only available with OvrPlugin"));
}
void FOculusXRFunctionLibraryOpenXR::UnregisterOpenXrEventHandler(void (*OpenXrEventHandler)(void* data, void* context))
{
ensureMsgf(false, TEXT("UnregisterOpenXrEventHandler is only available with OvrPlugin"));
}
} // namespace OculusXRHMD

View File

@@ -0,0 +1,73 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "OculusXRFunctionLibrary.h"
namespace OculusXRHMD
{
class FOculusXRFunctionLibraryOpenXR : public IOculusXRFunctionLibrary
{
public:
virtual void GetPose(FRotator& DeviceRotation, FVector& DevicePosition, FVector& NeckPosition) override;
virtual void SetBaseRotationAndBaseOffsetInMeters(FRotator Rotation, FVector BaseOffsetInMeters, EOrientPositionSelector::Type Options) override;
virtual void GetBaseRotationAndBaseOffsetInMeters(FRotator& OutRotation, FVector& OutBaseOffsetInMeters) override;
virtual void GetRawSensorData(FVector& AngularAcceleration, FVector& LinearAcceleration, FVector& AngularVelocity, FVector& LinearVelocity, float& TimeInSeconds, EOculusXRTrackedDeviceType DeviceType) override;
virtual bool IsDeviceTracked(EOculusXRTrackedDeviceType DeviceType) override;
virtual void GetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel& CpuPerfLevel, EOculusXRProcessorPerformanceLevel& GpuPerfLevel) override;
virtual void SetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel CpuPerfLevel, EOculusXRProcessorPerformanceLevel GpuPerfLevel) override;
virtual bool GetUserProfile(FOculusXRHmdUserProfile& Profile) override;
virtual void SetBaseRotationAndPositionOffset(FRotator BaseRot, FVector PosOffset, EOrientPositionSelector::Type Options) override;
virtual void GetBaseRotationAndPositionOffset(FRotator& OutRot, FVector& OutPosOffset) override;
virtual void AddLoadingSplashScreen(class UTexture2D* Texture, FVector TranslationInMeters, FRotator Rotation, FVector2D SizeInMeters, FRotator DeltaRotation, bool bClearBeforeAdd) override;
virtual void ClearLoadingSplashScreens() override;
virtual bool HasInputFocus() override;
virtual bool HasSystemOverlayPresent() override;
virtual void GetGPUUtilization(bool& IsGPUAvailable, float& GPUUtilization) override;
virtual float GetGPUFrameTime() override;
virtual void GetPerformanceMetrics(FOculusXRPerformanceMetrics& PerformanceMetrics) override;
virtual EOculusXRFoveatedRenderingMethod GetFoveatedRenderingMethod() override;
virtual void SetFoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod Method) override;
virtual void SetFoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel level, bool isDynamic) override;
virtual EOculusXRFoveatedRenderingLevel GetFoveatedRenderingLevel() override;
virtual bool GetEyeTrackedFoveatedRenderingSupported() override;
virtual FString GetDeviceName() override;
virtual EOculusXRDeviceType GetDeviceType() override;
virtual EOculusXRControllerType GetControllerType(EControllerHand deviceHand) override;
virtual TArray<float> GetAvailableDisplayFrequencies() override;
virtual float GetCurrentDisplayFrequency() override;
virtual void SetDisplayFrequency(float RequestedFrequency) override;
virtual void EnablePositionTracking(bool bPositionTracking) override;
virtual void EnableOrientationTracking(bool bOrientationTracking) override;
virtual void SetColorScaleAndOffset(FLinearColor ColorScale, FLinearColor ColorOffset, bool bApplyToAllLayers) override;
virtual class IStereoLayers* GetStereoLayers() override;
virtual bool IsGuardianConfigured() override;
virtual bool IsGuardianDisplayed() override;
virtual TArray<FVector> GetGuardianPoints(EOculusXRBoundaryType BoundaryType, bool UsePawnSpace /* = false */) override;
virtual FVector GetGuardianDimensions(EOculusXRBoundaryType BoundaryType) override;
virtual FTransform GetPlayAreaTransform() override;
virtual FOculusXRGuardianTestResult GetPointGuardianIntersection(const FVector Point, EOculusXRBoundaryType BoundaryType) override;
virtual FOculusXRGuardianTestResult GetNodeGuardianIntersection(EOculusXRTrackedDeviceType DeviceType, EOculusXRBoundaryType BoundaryType) override;
virtual void SetGuardianVisibility(bool GuardianVisible) override;
virtual bool GetSystemHmd3DofModeEnabled() override;
virtual EOculusXRColorSpace GetHmdColorDesc() override;
virtual void SetClientColorDesc(EOculusXRColorSpace ColorSpace) override;
virtual void SetLocalDimmingOn(bool LocalDimmingOn) override;
virtual bool IsPassthroughSupported() override;
virtual bool IsColorPassthroughSupported() override;
virtual void StartEnvironmentDepth() override;
virtual void StopEnvironmentDepth() override;
virtual bool IsEnvironmentDepthStarted() override;
virtual void SetEnvironmentDepthHandRemoval(bool RemoveHands) override;
virtual void SetXROcclusionsMode(UObject* WorldContextObject, EOculusXROcclusionsMode Mode) override;
virtual void SetEyeBufferSharpenType(EOculusXREyeBufferSharpenType EyeBufferSharpenType) override;
virtual bool IsPassthroughRecommended() override;
virtual void GetOpenXRInstanceProcAddrFunc(void** func) override;
virtual void GetNativeOpenXRHandles(uint64_t* OutXrInstance, uint64_t* OutXrSession) override;
virtual void GetAppSpace(uint64_t* OutAppSpace) override;
virtual void GetNextPredictedDisplayTime(uint64_t* NextPredictedDisplayTime) override;
virtual void RegisterOpenXrEventHandler(void (*OpenXrEventHandler)(void* data, void* context), void* Context);
virtual void UnregisterOpenXrEventHandler(void (*OpenXrEventHandler)(void* data, void* context));
};
} // namespace OculusXRHMD

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,734 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDModule.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_Settings.h"
#include "OculusXRHMD_GameFrame.h"
#include "OculusXRHMD_CustomPresent.h"
#include "OculusXRHMD_Layer.h"
#include "OculusXRHMD_Splash.h"
#include "OculusXRHMD_StressTester.h"
#include "OculusXRHMD_ConsoleCommands.h"
#include "OculusXRHMD_SpectatorScreenController.h"
#include "OculusXRHMD_DynamicResolutionState.h"
#include "OculusXRHMD_DeferredDeletionQueue.h"
#include "OculusXRAssetManager.h"
#include "HeadMountedDisplayBase.h"
#include "HeadMountedDisplay.h"
#include "XRRenderTargetManager.h"
#include "XRRenderBridge.h"
#include "IStereoLayers.h"
#include "Stats/Stats.h"
#include "SceneViewExtension.h"
#include "Engine/Engine.h"
#include "Engine/StaticMeshActor.h"
#include "XRThreadUtils.h"
#include "ProceduralMeshComponent.h"
#include "Shader.h"
#include "GlobalShader.h"
#include "Misc/EngineVersionComparison.h"
#include "OculusXRHMD_FoveatedRendering.h"
namespace OculusXRHMD
{
class FHardOcclusionsPS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FHardOcclusionsPS, Global);
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment);
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters);
/** Default constructor. */
FHardOcclusionsPS();
/** Initialization constructor. */
FHardOcclusionsPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer);
void SetParameters(
FRHIBatchedShaderParameters& BatchedParameters,
FRHISamplerState* Sampler,
FRHITexture* Texture,
const FVector2f& Factors,
const FMatrix44f ScreenToDepth[ovrpEye_Count],
const int ViewId);
private:
LAYOUT_FIELD(FShaderResourceParameter, EnvironmentDepthTexture);
LAYOUT_FIELD(FShaderResourceParameter, EnvironmentDepthSampler);
LAYOUT_FIELD(FShaderParameter, DepthFactors);
LAYOUT_FIELD(FShaderParameter, ScreenToDepthMatrices);
LAYOUT_FIELD(FShaderParameter, DepthViewId);
};
/**
* A pixel shader for rendering occlusions, this is the min/max preprocessing step.
*/
template <bool bEnableMultiView>
class FScreenPSEnvironmentDepthMinMax : public FGlobalShader
{
DECLARE_SHADER_TYPE(FScreenPSEnvironmentDepthMinMax, Global);
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return !bEnableMultiView || Parameters.Platform == SP_VULKAN_ES3_1_ANDROID || Parameters.Platform == SP_VULKAN_SM5;
}
static void ModifyCompilationEnvironment(const FPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
OutEnvironment.SetDefine(TEXT("ENABLE_MULTI_VIEW"), bEnableMultiView ? 1 : 0);
}
FScreenPSEnvironmentDepthMinMax(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
InTexture.Bind(Initializer.ParameterMap, TEXT("InTexture"), SPF_Mandatory);
InTextureSampler.Bind(Initializer.ParameterMap, TEXT("InTextureSampler"));
if (!bEnableMultiView)
{
InArraySliceParameter.Bind(Initializer.ParameterMap, TEXT("ArraySlice"));
}
}
FScreenPSEnvironmentDepthMinMax() {}
void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, FRHISamplerState* SamplerStateRHI, FRHITexture* TextureRHI, int ArraySlice)
{
SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, SamplerStateRHI, TextureRHI);
if (!bEnableMultiView)
{
SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice);
}
}
private:
LAYOUT_FIELD(FShaderResourceParameter, InTexture);
LAYOUT_FIELD(FShaderResourceParameter, InTextureSampler);
LAYOUT_FIELD(FShaderParameter, InArraySliceParameter);
};
DECLARE_DELEGATE_TwoParams(FOculusXRHMDEventPollingDelegate, ovrpEventDataBuffer*, bool&);
//-------------------------------------------------------------------------------------------------
// FPerformanceStats
//-------------------------------------------------------------------------------------------------
struct FPerformanceStats
{
uint64 Frames;
double Seconds;
FPerformanceStats(uint32 InFrames = 0, double InSeconds = 0.0)
: Frames(InFrames)
, Seconds(InSeconds)
{
}
FPerformanceStats operator-(const FPerformanceStats& PerformanceStats) const
{
return FPerformanceStats(
Frames - PerformanceStats.Frames,
Seconds - PerformanceStats.Seconds);
}
};
enum FRecenterTypes
{
RecenterOrientation = 0x1,
RecenterPosition = 0x2,
RecenterOrientationAndPosition = 0x3
};
//-------------------------------------------------------------------------------------------------
// FOculusXRHMD - Oculus Rift Head Mounted Display
//-------------------------------------------------------------------------------------------------
class FOculusXRHMD : public FHeadMountedDisplayBase, public FXRRenderTargetManager, public IStereoLayers, public FHMDSceneViewExtension, public FOculusAssetManager
{
friend class UOculusXRFunctionLibrary;
friend FOculusXRHMDModule;
friend class FSplash;
friend class FConsoleCommands;
public:
// IXRSystemIdentifier
virtual FName GetSystemName() const override;
virtual int32 GetXRSystemFlags() const override;
// IXRTrackingSystem
virtual FString GetVersionString() const override;
virtual bool DoesSupportPositionalTracking() const override;
virtual bool HasValidTrackingPosition() override;
virtual bool EnumerateTrackedDevices(TArray<int32>& OutDevices, EXRTrackedDeviceType Type = EXRTrackedDeviceType::Any) override;
virtual bool GetCurrentPose(int32 InDeviceId, FQuat& OutOrientation, FVector& OutPosition) override;
virtual bool GetRelativeEyePose(int32 InDeviceId, int32 ViewIndex, FQuat& OutOrientation, FVector& OutPosition) override;
virtual bool GetTrackingSensorProperties(int32 InDeviceId, FQuat& OutOrientation, FVector& OutPosition, FXRSensorProperties& OutSensorProperties) override;
virtual void SetTrackingOrigin(EHMDTrackingOrigin::Type NewOrigin) override;
virtual EHMDTrackingOrigin::Type GetTrackingOrigin() const override;
virtual bool GetFloorToEyeTrackingTransform(FTransform& OutFloorToEye) const override;
// virtual FVector GetAudioListenerOffset(int32 InDeviceId = HMDDeviceId) const override;
virtual void ResetOrientationAndPosition(float Yaw = 0.f) override;
virtual void ResetOrientation(float Yaw = 0.f) override;
virtual void ResetPosition() override;
virtual void SetBaseRotation(const FRotator& BaseRot) override;
virtual FRotator GetBaseRotation() const override;
virtual void SetBaseOrientation(const FQuat& BaseOrient) override;
virtual FQuat GetBaseOrientation() const override;
// virtual TSharedPtr<class IXRCamera, ESPMode::ThreadSafe> GetXRCamera(int32 DeviceId = HMDDeviceId) override;
virtual class IHeadMountedDisplay* GetHMDDevice() override { return this; }
virtual class TSharedPtr<class IStereoRendering, ESPMode::ThreadSafe> GetStereoRenderingDevice() override
{
return SharedThis(this);
}
// virtual class IXRInput* GetXRInput() override;
virtual bool
IsHeadTrackingEnforced() const override;
virtual void SetHeadTrackingEnforced(bool bEnabled) override;
virtual bool IsHeadTrackingAllowed() const override;
virtual void OnBeginPlay(FWorldContext& InWorldContext) override;
virtual void OnEndPlay(FWorldContext& InWorldContext) override;
virtual bool OnStartGameFrame(FWorldContext& WorldContext) override;
virtual bool OnEndGameFrame(FWorldContext& WorldContext) override;
virtual void OnBeginRendering_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneViewFamily& ViewFamily) override;
virtual void OnBeginRendering_GameThread() override;
virtual class IXRLoadingScreen* CreateLoadingScreen() override { return GetSplash(); }
virtual FVector2D GetPlayAreaBounds(EHMDTrackingOrigin::Type Origin) const override;
// IHeadMountedDisplay
virtual bool IsHMDConnected() override { return true; }
virtual bool IsHMDEnabled() const override;
virtual EHMDWornState::Type GetHMDWornState() override;
virtual void EnableHMD(bool bEnable = true) override;
virtual bool GetHMDMonitorInfo(MonitorInfo&) override;
virtual void GetFieldOfView(float& InOutHFOVInDegrees, float& InOutVFOVInDegrees) const override;
virtual void SetInterpupillaryDistance(float NewInterpupillaryDistance) override;
virtual float GetInterpupillaryDistance() const override;
// virtual void SetClippingPlanes(float NCP, float FCP) override;
// virtual FVector GetAudioListenerOffset() const override;
virtual bool GetHMDDistortionEnabled(EShadingPath ShadingPath) const override;
// virtual void BeginRendering_RenderThread(const FTransform& NewRelativeTransform, FRHICommandListImmediate& RHICmdList, FSceneViewFamily& ViewFamily) override;
// virtual bool IsSpectatorScreenActive() const override;
// virtual class ISpectatorScreenController* GetSpectatorScreenController() override;
// virtual class ISpectatorScreenController const* GetSpectatorScreenController() const override;
// virtual float GetDistortionScalingFactor() const override;
// virtual float GetLensCenterOffset() const override;
// virtual void GetDistortionWarpValues(FVector4& K) const override;
virtual bool IsChromaAbCorrectionEnabled() const override;
// virtual bool GetChromaAbCorrectionValues(FVector4& K) const override;
virtual bool HasHiddenAreaMesh() const override;
virtual bool HasVisibleAreaMesh() const override;
#if UE_VERSION_OLDER_THAN(5, 5, 0)
#ifdef WITH_OCULUS_BRANCH
virtual void DrawHiddenAreaMesh(class FRHICommandList& RHICmdList, int32 ViewIndex, int32 InstanceCount = 1) const override;
virtual void DrawVisibleAreaMesh(class FRHICommandList& RHICmdList, int32 ViewIndex, int32 InstanceCount = 1) const override;
#else
virtual void DrawHiddenAreaMesh(class FRHICommandList& RHICmdList, int32 ViewIndex) const override;
virtual void DrawVisibleAreaMesh(class FRHICommandList& RHICmdList, int32 ViewIndex) const override;
#endif // WITH_OCULUS_BRANCH
#else
virtual void DrawHiddenAreaMesh(class FRHICommandList& RHICmdList, int32 ViewIndex) const override final;
virtual void DrawVisibleAreaMesh(class FRHICommandList& RHICmdList, int32 ViewIndex) const override final;
virtual void DrawHiddenAreaMesh(class FRHICommandList& RHICmdList, int32 ViewIndex, int32 InstanceCount) const override final;
virtual void DrawVisibleAreaMesh(class FRHICommandList& RHICmdList, int32 ViewIndex, int32 InstanceCount) const override final;
#endif
// virtual void DrawDistortionMesh_RenderThread(struct FHeadMountedDisplayPassContext& Context, const FIntPoint& TextureSize) override;
// virtual void UpdateScreenSettings(const FViewport* InViewport) override;
// virtual void UpdatePostProcessSettings(FPostProcessSettings*) override;
// virtual FTexture* GetDistortionTextureLeft() const override;
// virtual FTexture* GetDistortionTextureRight() const override;
// virtual FVector2D GetTextureOffsetLeft() const override;
// virtual FVector2D GetTextureOffsetRight() const override;
// virtual FVector2D GetTextureScaleLeft() const override;
// virtual FVector2D GetTextureScaleRight() const override;
// virtual const float* GetRedDistortionParameters() const override;
// virtual const float* GetGreenDistortionParameters() const override;
// virtual const float* GetBlueDistortionParameters() const override;
// virtual bool NeedsUpscalePostProcessPass() override;
// virtual void RecordAnalytics() override;
// virtual bool DoesAppUseVRFocus() const override;
// virtual bool DoesAppHaveVRFocus() const override;
virtual float GetPixelDenity() const override;
virtual void SetPixelDensity(const float NewPixelDensity) override;
virtual FIntPoint GetIdealRenderTargetSize() const override;
virtual void GetMotionControllerData(UObject* WorldContext, const EControllerHand Hand, FXRMotionControllerData& MotionControllerData) override;
#if !UE_VERSION_OLDER_THAN(5, 5, 0)
virtual void GetMotionControllerState(UObject* WorldContext, const EXRSpaceType XRSpaceType, const EControllerHand Hand, const EXRControllerPoseType XRControllerPoseType, FXRMotionControllerState& MotionControllerState) override;
#endif
// IStereoRendering interface
virtual bool IsStereoEnabled() const override;
virtual bool IsStereoEnabledOnNextFrame() const override;
virtual bool EnableStereo(bool stereo = true) override;
virtual void AdjustViewRect(int32 ViewIndex, int32& X, int32& Y, uint32& SizeX, uint32& SizeY) const override;
virtual void SetFinalViewRect(FRHICommandListImmediate& RHICmdList, const int32 ViewIndex, const FIntRect& FinalViewRect) override;
// virtual FVector2D GetTextSafeRegionBounds() const override;
virtual void CalculateStereoViewOffset(const int32 ViewIndex, FRotator& ViewRotation, const float WorldToMeters, FVector& ViewLocation) override;
virtual FMatrix GetStereoProjectionMatrix(const int32 ViewIndex) const override;
virtual void InitCanvasFromView(class FSceneView* InView, class UCanvas* Canvas) override;
// virtual void GetEyeRenderParams_RenderThread(const struct FRenderingCompositePassContext& Context, FVector2D& EyeToSrcUVScaleValue, FVector2D& EyeToSrcUVOffsetValue) const override;
virtual void RenderTexture_RenderThread(class FRHICommandListImmediate& RHICmdList, class FRHITexture* BackBuffer, class FRHITexture* SrcTexture, FVector2D WindowSize) const override;
// virtual void SetClippingPlanes(float NCP, float FCP) override;
virtual IStereoRenderTargetManager* GetRenderTargetManager() override { return this; }
virtual IStereoLayers* GetStereoLayers() override { return this; }
// virtual void UseImplicitHmdPosition(bool bInImplicitHmdPosition) override;
// virtual bool GetUseImplicitHmdPosition() override;
virtual bool IsStandaloneStereoOnlyDevice() const override { return bIsStandaloneStereoOnlyDevice; }
bool SupportsSpaceWarp() const;
#ifdef WITH_OCULUS_BRANCH
virtual void CalculateScissorRect(const int32 ViewIndex, const FIntRect& ViewRect, FIntRect& OutRect) override;
#endif // WITH_OCULUS_BRANCH
// FHeadMountedDisplayBase interface
virtual FVector2D GetEyeCenterPoint_RenderThread(int32 ViewIndex) const override;
virtual FIntRect GetFullFlatEyeRect_RenderThread(FTextureRHIRef EyeTexture) const override;
virtual void CopyTexture_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture* SrcTexture, FIntRect SrcRect, FRHITexture* DstTexture, FIntRect DstRect, bool bClearBlack, bool bNoAlpha) const override;
virtual bool PopulateAnalyticsAttributes(TArray<struct FAnalyticsEventAttribute>& EventAttributes) override;
// FXRRenderTargetManager interface
virtual bool ShouldUseSeparateRenderTarget() const override;
#ifdef WITH_OCULUS_BRANCH
virtual void CalculateRenderTargetSize(uint32& InOutSizeX, uint32& InOutSizeY) override;
#else // WITH_OCULUS_BRANCH
virtual void CalculateRenderTargetSize(const FViewport& Viewport, uint32& InOutSizeX, uint32& InOutSizeY) override;
#endif // WITH_OCULUS_BRANCH
virtual bool NeedReAllocateViewportRenderTarget(const class FViewport& Viewport) override;
virtual bool NeedReAllocateDepthTexture(const TRefCountPtr<IPooledRenderTarget>& DepthTarget) override;
virtual bool NeedReAllocateShadingRateTexture(const TRefCountPtr<IPooledRenderTarget>& FoveationTarget) override;
virtual bool AllocateRenderTargetTexture(uint32 Index, uint32 SizeX, uint32 SizeY, uint8 Format, uint32 NumMips, ETextureCreateFlags InTexFlags, ETextureCreateFlags InTargetableTextureFlags, FTextureRHIRef& OutTargetableTexture, FTextureRHIRef& OutShaderResourceTexture, uint32 NumSamples = 1) override;
virtual bool AllocateDepthTexture(uint32 Index, uint32 SizeX, uint32 SizeY, uint8 Format, uint32 NumMips, ETextureCreateFlags InTexFlags, ETextureCreateFlags TargetableTextureFlags, FTextureRHIRef& OutTargetableTexture, FTextureRHIRef& OutShaderResourceTexture, uint32 NumSamples = 1) override;
virtual bool AllocateShadingRateTexture(uint32 Index, uint32 RenderSizeX, uint32 RenderSizeY, uint8 Format, uint32 NumMips, ETextureCreateFlags InTexFlags, ETextureCreateFlags InTargetableTextureFlags, FTextureRHIRef& OutTexture, FIntPoint& OutTextureSize) override;
#if defined(WITH_OCULUS_BRANCH)
virtual bool GetRecommendedVelocityTextureSize(FIntPoint& OutTextureSize) override;
virtual bool AllocateVelocityTexture(uint32 Index, const FIntPoint& Size, uint8 Format, uint32 NumMips, ETextureCreateFlags TexFlags, FTextureRHIRef& OutTexture, uint32 NumSamples = 1) override;
virtual bool AllocateVelocityDepthTexture(uint32 Index, const FIntPoint& Size, uint8 Format, uint32 NumMips, ETextureCreateFlags TexFlags, FTextureRHIRef& OutTexture, uint32 NumSamples = 1) override;
virtual bool FindEnvironmentDepthTexture_RenderThread(FTextureRHIRef& OutTexture, FTextureRHIRef& OutMinMaxTexture, FVector2f& OutDepthFactors, FMatrix44f OutScreenToDepthMatrices[2], FMatrix44f OutDepthViewProjMatrices[2]) override;
#endif // defined(WITH_OCULUS_BRANCH)
virtual EPixelFormat GetActualColorSwapchainFormat() const override;
virtual void UpdateViewportWidget(bool bUseSeparateRenderTarget, const class FViewport& Viewport, class SViewport* ViewportWidget) override;
virtual FXRRenderBridge* GetActiveRenderBridge_GameThread(bool bUseSeparateRenderTarget);
void AllocateEyeBuffer();
// IStereoLayers interface
virtual uint32 CreateLayer(const IStereoLayers::FLayerDesc& InLayerDesc) override;
virtual void DestroyLayer(uint32 LayerId) override;
virtual void SetLayerDesc(uint32 LayerId, const IStereoLayers::FLayerDesc& InLayerDesc) override;
virtual bool GetLayerDesc(uint32 LayerId, IStereoLayers::FLayerDesc& OutLayerDesc) override;
virtual void MarkTextureForUpdate(uint32 LayerId) override;
virtual IStereoLayers::FLayerDesc GetDebugCanvasLayerDesc(FTextureRHIRef Texture) override;
virtual void GetAllocatedTexture(uint32 LayerId, FTextureRHIRef& Texture, FTextureRHIRef& LeftTexture) override;
virtual bool ShouldCopyDebugLayersToSpectatorScreen() const override { return true; }
virtual void PushLayerState(bool) override
{ /* Todo */
}
virtual void PopLayerState() override
{ /* Todo */
}
// ISceneViewExtension
virtual void SetupViewFamily(FSceneViewFamily& InViewFamily) override;
virtual void SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) override;
virtual void BeginRenderViewFamily(FSceneViewFamily& InViewFamily) override;
virtual void PreRenderViewFamily_RenderThread(FRDGBuilder& GraphBuilder, FSceneViewFamily& InViewFamily) override;
virtual void PreRenderView_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView) override;
virtual void PostRenderViewFamily_RenderThread(FRDGBuilder& GraphBuilder, FSceneViewFamily& InViewFamily) override;
#if UE_VERSION_OLDER_THAN(5, 3, 0)
virtual void PostRenderBasePassMobile_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneView& InView) override;
#ifdef WITH_OCULUS_BRANCH
virtual void PostSceneColorRenderingMobile_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneView& InView) override;
#endif
#else
virtual void PostRenderBasePassMobile_RenderThread(FRHICommandList& RHICmdList, FSceneView& InView) override;
#ifdef WITH_OCULUS_BRANCH
virtual void PostSceneColorRenderingMobile_RenderThread(FRHICommandList& RHICmdList, FSceneView& InView) override;
#endif
#endif
virtual void PostRenderBasePassDeferred_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView, const FRenderTargetBindingSlots& RenderTargets, TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTextures) override;
virtual int32 GetPriority() const override;
#ifdef WITH_OCULUS_BRANCH
virtual bool LateLatchingEnabled() const override;
virtual void PreLateLatchingViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneViewFamily& InViewFamily) override;
#endif
// MultiPlayer
void SwitchPrimaryPIE(int PrimaryPIEIndex);
const FOculusXRPerformanceMetrics GetPerformanceMetrics() const;
public:
FOculusXRHMD(const FAutoRegister&);
~FOculusXRHMD();
protected:
bool Startup();
void PreShutdown();
void Shutdown();
bool InitializeSession();
void ShutdownSession();
bool InitDevice();
void ReleaseDevice();
void ApplicationPauseDelegate();
void ApplicationResumeDelegate();
bool CheckEyeTrackingPermission(EOculusXRFoveatedRenderingMethod FoveatedRenderingMethod);
void SetupOcclusionMeshes();
void UpdateStereoRenderingParams();
void UpdateHmdRenderInfo();
void InitializeEyeLayer_RenderThread(FRHICommandListImmediate& RHICmdList);
void ApplySystemOverridesOnStereo(bool force = false);
bool OnOculusStateChange(bool bIsEnabledNow);
bool ShouldDisableHiddenAndVisibileAreaMeshForSpectatorScreen_RenderThread() const;
void Recenter(FRecenterTypes RecenterType, float Yaw);
FIntRect GetAsymmetricViewRect(const int32 ViewIndex, const FIntRect& ViewRect);
#if !UE_BUILD_SHIPPING
void DrawDebug(UCanvas* InCanvas, APlayerController* InPlayerController);
#endif
class FSceneViewport* FindSceneViewport();
FOculusXRSplashDesc GetUESplashScreenDesc();
void EyeTrackedFoveatedRenderingFallback();
public:
OCULUSXRHMD_API static FOculusXRHMD* GetOculusXRHMD();
bool IsHMDActive() const;
FSplash* GetSplash() const { return Splash.Get(); }
FCustomPresent* GetCustomPresent_Internal() const { return CustomPresent; }
float GetWorldToMetersScale() const;
ESpectatorScreenMode GetSpectatorScreenMode_RenderThread() const;
FVector GetNeckPosition(const FQuat& HeadOrientation, const FVector& HeadPosition);
/**
* Sets base position offset (in meters). The base position offset is the distance from the physical (0, 0, 0) position
* to current HMD position (bringing the (0, 0, 0) point to the current HMD position)
* Note, this vector is set by ResetPosition call; use this method with care.
* The axis of the vector are the same as in Unreal: X - forward, Y - right, Z - up.
*
* @param BaseOffset (in) the vector to be set as base offset, in meters.
*/
void SetBaseOffsetInMeters(const FVector& BaseOffset);
/**
* Returns the currently used base position offset, previously set by the
* ResetPosition or SetBasePositionOffset calls. It represents a vector that translates the HMD's position
* into (0,0,0) point, in meters.
* The axis of the vector are the same as in Unreal: X - forward, Y - right, Z - up.
*
* @return Base position offset, in meters.
*/
FVector GetBaseOffsetInMeters() const;
OCULUSXRHMD_API bool ConvertPose(const ovrpPosef& InPose, FPose& OutPose) const;
OCULUSXRHMD_API bool ConvertPose(const FPose& InPose, ovrpPosef& OutPose) const;
OCULUSXRHMD_API bool ConvertPose_RenderThread(const ovrpPosef& InPose, FPose& OutPose) const;
OCULUSXRHMD_API static bool ConvertPose_Internal(const ovrpPosef& InPose, FPose& OutPose, const FSettings* Settings, float WorldToMetersScale = 100.0f);
OCULUSXRHMD_API static bool ConvertPose_Internal(const FPose& InPose, ovrpPosef& OutPose, const FSettings* Settings, float WorldToMetersScale = 100.0f);
/** Turns ovrVector3f in Unreal World space to a scaled FVector and applies translation and rotation corresponding to player movement */
FVector ScaleAndMovePointWithPlayer(ovrpVector3f& OculusXRHMDPoint);
/** The inverse of ScaleAndMovePointWithPlayer */
ovrpVector3f WorldLocationToOculusPoint(const FVector& InUnrealPosition);
/** Convert dimension of a float (e.g., a distance) from meters to Unreal Units */
float ConvertFloat_M2U(float OculusFloat) const;
FVector ConvertVector_M2U(ovrpVector3f OculusPoint) const;
struct UserProfile
{
float IPD;
float EyeDepth;
float EyeHeight;
};
bool GetUserProfile(UserProfile& OutProfile);
float GetVsyncToNextVsync() const;
FPerformanceStats GetPerformanceStats() const;
bool DoEnableStereo(bool bStereo);
void SendTelemetryData();
void ResetControlRotation() const;
void UpdateFoveationOffsets_RenderThread();
bool ComputeEnvironmentDepthParameters_RenderThread(FVector2f& DepthFactors, FMatrix44f ScreenToDepth[ovrpEye_Count], FMatrix44f DepthViewProj[ovrpEye_Count], int& SwapchainIndex);
FSettingsPtr CreateNewSettings() const;
FGameFramePtr CreateNewGameFrame() const;
FGameFrame* GetFrame()
{
CheckInGameThread();
return Frame.Get();
}
const FGameFrame* GetFrame() const
{
CheckInGameThread();
return Frame.Get();
}
FGameFrame* GetFrame_RenderThread()
{
CheckInRenderThread();
return Frame_RenderThread.Get();
}
const FGameFrame* GetFrame_RenderThread() const
{
CheckInRenderThread();
return Frame_RenderThread.Get();
}
FGameFrame* GetFrame_RHIThread()
{
CheckInRHIThread();
return Frame_RHIThread.Get();
}
const FGameFrame* GetFrame_RHIThread() const
{
CheckInRHIThread();
return Frame_RHIThread.Get();
}
FGameFrame* GetNextFrameToRender()
{
CheckInGameThread();
return NextFrameToRender.Get();
}
const FGameFrame* GetNextFrameToRender() const
{
CheckInGameThread();
return NextFrameToRender.Get();
}
FSettings* GetSettings()
{
CheckInGameThread();
return Settings.Get();
}
const FSettings* GetSettings() const
{
CheckInGameThread();
return Settings.Get();
}
FSettings* GetSettings_RenderThread()
{
CheckInRenderThread();
return Settings_RenderThread.Get();
}
const FSettings* GetSettings_RenderThread() const
{
CheckInRenderThread();
return Settings_RenderThread.Get();
}
FSettings* GetSettings_RHIThread()
{
CheckInRHIThread();
return Settings_RHIThread.Get();
}
const FSettings* GetSettings_RHIThread() const
{
CheckInRHIThread();
return Settings_RHIThread.Get();
}
const int GetNextFrameNumber() const { return NextFrameNumber; }
const FRotator GetSplashRotation() const { return SplashRotation; }
void SetSplashRotationToForward();
OCULUSXRHMD_API void StartGameFrame_GameThread(); // Called from OnStartGameFrame or from FOculusXRInput::SendControllerEvents (first actual call of the frame)
void FinishGameFrame_GameThread(); // Called from OnEndGameFrame
void StartRenderFrame_GameThread(); // Called from BeginRenderViewFamily
void FinishRenderFrame_RenderThread(FRDGBuilder& GraphBuilder); // Called from PostRenderViewFamily_RenderThread
void StartRHIFrame_RenderThread(); // Called from PreRenderViewFamily_RenderThread
void FinishRHIFrame_RHIThread(); // Called from FinishRendering_RHIThread
void GetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel& CpuPerfLevel, EOculusXRProcessorPerformanceLevel& GpuPerfLevel);
void SetSuggestedCpuAndGpuPerformanceLevels(EOculusXRProcessorPerformanceLevel CpuPerfLevel, EOculusXRProcessorPerformanceLevel GpuPerfLevel);
void SetFoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod InFoveationMethod);
void SetFoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel InFoveationLevel, bool isDynamic);
void SetColorScaleAndOffset(FLinearColor ColorScale, FLinearColor ColorOffset, bool bApplyToAllLayers);
void SetEnvironmentDepthHandRemoval(bool RemoveHands);
void StartEnvironmentDepth(int CreateFlags);
void StopEnvironmentDepth();
bool IsEnvironmentDepthStarted();
UE_DEPRECATED(5.5, "Hard occlusions is deprecated, use Soft occlusions instead.")
void EnableHardOcclusions(bool bEnable);
void EnableSoftOcclusions(bool bEnable);
OCULUSXRHMD_API void UpdateRTPoses();
FTransform GetLastTrackingToWorld() const { return LastTrackingToWorld; }
OCULUSXRHMD_API void AddEventPollingDelegate(const FOculusXRHMDEventPollingDelegate& NewDelegate);
OCULUSXRHMD_API uint32 GetLayerIdFromOvrpId(int OvrpId) const;
protected:
FConsoleCommands ConsoleCommands;
void UpdateOnRenderThreadCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
void HQBufferCommandHandler(const TArray<FString>& Args, UWorld*, FOutputDevice& Ar);
void HQDistortionCommandHandler(const TArray<FString>& Args, UWorld*, FOutputDevice& Ar);
void ShowGlobalMenuCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
void ShowQuitMenuCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
#if !UE_BUILD_SHIPPING
void StatsCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
void ShowSettingsCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
void IPDCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
#endif
void LoadFromSettings();
void LogEnabledFeatures() const;
void DoSessionShutdown();
protected:
void UpdateHMDWornState();
EHMDWornState::Type HMDWornState = EHMDWornState::Unknown;
void UpdateHMDEvents();
void EnableInsightPassthrough_RenderThread(bool bEnablePassthrough);
void PrepareAndRenderHardOcclusions_RenderThread(FRHICommandList& RHICmdList, FSceneView& InView);
// MultiPlayer
void InitMultiPlayerPoses(const FPose& CurPose);
void ResetMultiPlayerPoses();
void ReCalcMultiPlayerPoses(FPose& CurHMDHeadPose);
union
{
struct
{
uint64 bApplySystemOverridesOnStereo : 1;
uint64 bNeedEnableStereo : 1;
uint64 bNeedDisableStereo : 1;
};
uint64 Raw;
} Flags;
union
{
struct
{
// set to true when origin was set while OvrSession == null; the origin will be set ASA OvrSession != null
uint64 NeedSetTrackingOrigin : 1;
// enforces exit; used mostly for testing
uint64 EnforceExit : 1;
// set if a game is paused by the plug-in
uint64 AppIsPaused : 1;
// set to indicate that DisplayLost was detected by game thread.
uint64 DisplayLostDetected : 1;
// set to true once new session is created; being handled and reset as soon as session->IsVisible.
uint64 NeedSetFocusToGameViewport : 1;
};
uint64 Raw;
} OCFlags;
TRefCountPtr<FCustomPresent> CustomPresent;
FSplashPtr Splash;
IRendererModule* RendererModule;
FDeferredDeletionQueue DeferredDeletion;
EHMDTrackingOrigin::Type TrackingOrigin;
// Stores difference between ViewRotation and EyeOrientation from previous frame
FQuat LastPlayerOrientation;
// Stores GetFrame()->PlayerLocation (i.e., ViewLocation) from the previous frame
FVector LastPlayerLocation;
FRotator DeltaControlRotation; // used from ApplyHmdRotation
TWeakPtr<SWidget> CachedViewportWidget;
TWeakPtr<SWindow> CachedWindow;
FIntPoint CachedWindowSize;
float CachedWorldToMetersScale;
bool bIsStandaloneStereoOnlyDevice;
// Stores TrackingToWorld from previous frame
FTransform LastTrackingToWorld;
std::atomic<bool> bHardOcclusionsEnabled;
std::atomic<bool> bSoftOcclusionsEnabled;
std::atomic<bool> bEnvironmentDepthHandRemovalEnabled;
// These three properties indicate the current state of foveated rendering, which may differ from what's in Settings
// due to cases such as falling back to FFR when eye tracked foveated rendering isn't enabled. Will allow us to resume
// ETFR from situations such as when ET gets paused.
std::atomic<EOculusXRFoveatedRenderingMethod> FoveatedRenderingMethod;
std::atomic<EOculusXRFoveatedRenderingLevel> FoveatedRenderingLevel;
std::atomic<bool> bDynamicFoveatedRendering;
// Game thread
FSettingsPtr Settings;
uint32 NextFrameNumber;
uint32 WaitFrameNumber;
FGameFramePtr Frame; // Valid from OnStartGameFrame to OnEndGameFrame
FGameFramePtr NextFrameToRender; // Valid from OnStartGameFrame to BeginRenderViewFamily
FGameFramePtr LastFrameToRender; // Valid from OnStartGameFrame to BeginRenderViewFamily
uint32 NextLayerId;
TMap<uint32, FLayerPtr> LayerMap;
bool bNeedReAllocateViewportRenderTarget;
// Render thread
FSettingsPtr Settings_RenderThread;
FGameFramePtr Frame_RenderThread; // Valid from BeginRenderViewFamily to PostRenderViewFamily_RenderThread
TArray<FLayerPtr> Layers_RenderThread;
FLayerPtr EyeLayer_RenderThread; // Valid to be accessed from game thread, since updated only when game thread is waiting
bool bNeedReAllocateDepthTexture_RenderThread;
bool bNeedReAllocateFoveationTexture_RenderThread;
bool bNeedReAllocateMotionVectorTexture_RenderThread;
bool bNeedReAllocateMotionVectorDepthTexture_RenderThread;
#if !UE_VERSION_OLDER_THAN(5, 3, 0)
TSharedPtr<FOculusXRFoveatedRenderingImageGenerator, ESPMode::ThreadSafe> FoveationImageGenerator;
#endif // !UE_VERSION_OLDER_THAN(5, 3, 0)
// RHI thread
FSettingsPtr Settings_RHIThread;
FGameFramePtr Frame_RHIThread; // Valid from PreRenderViewFamily_RenderThread to FinishRendering_RHIThread
TArray<FLayerPtr> Layers_RHIThread;
FHMDViewMesh HiddenAreaMeshes[2];
FHMDViewMesh VisibleAreaMeshes[2];
FPerformanceStats PerformanceStats;
FRotator SplashRotation; // rotation applied to all splash screens (dependent on HMD orientation as the splash is shown)
TArray<FTextureRHIRef> EnvironmentDepthSwapchain;
FTextureRHIRef EnvironmentDepthMinMaxTexture;
int PrevEnvironmentDepthMinMaxSwapchainIndex = -1;
#if !UE_BUILD_SHIPPING
FDelegateHandle DrawDebugDelegateHandle;
#endif
enum class FInsightInitStatus
{
NotInitialized,
Initialized,
Failed,
};
FInsightInitStatus InsightInitStatus;
bool bShutdownRequestQueued;
bool bEyeTrackedFoveatedRenderingSupported;
FOculusXRPerformanceMetrics PerformanceMetrics;
TArray<FOculusXRHMDEventPollingDelegate> EventPollingDelegates;
// MultiPlayer
int CurPlayerIndex;
FPose LastFrameHMDHeadPose;
TArray<FPose> MultiPlayerPoses;
bool bShouldWait_GameThread;
bool bIsRendering_RenderThread;
};
typedef TSharedPtr<FOculusXRHMD, ESPMode::ThreadSafe> FOculusXRHMDPtr;
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,567 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMDModule.h"
#include "OculusXRFunctionLibrary.h"
#include "OculusXRHMD.h"
#include "OculusXRHMDPrivateRHI.h"
#include "OculusXRHMDRuntimeSettings.h"
#include "OculusXRStereoLayersFlagsSupplier.h"
#include "Containers/StringConv.h"
#include "Misc/EngineVersion.h"
#include "Misc/Paths.h"
#if PLATFORM_ANDROID
#include "Android/AndroidApplication.h"
#include "Android/AndroidPlatformMisc.h"
#endif
#include "Interfaces/IPluginManager.h"
#include "ShaderCore.h"
#include "OculusXRTelemetry.h"
#if PLATFORM_WINDOWS
#include "OculusXRSimulator.h"
#include "OculusXRSyntheticEnvironmentServer.h"
#endif
#if !PLATFORM_ANDROID
#if !UE_BUILD_SHIPPING
namespace
{
void __cdecl OvrpLogCallback2(ovrpLogLevel InLevel, const char* Message, int Length)
{
ELogVerbosity::Type OutLevel;
switch (InLevel)
{
case ovrpLogLevel_Debug:
OutLevel = ELogVerbosity::Log;
break;
case ovrpLogLevel_Info:
OutLevel = ELogVerbosity::Display;
break;
case ovrpLogLevel_Error:
OutLevel = ELogVerbosity::Error;
break;
case ovrpLogLevel_Warning:
OutLevel = ELogVerbosity::Warning;
break;
default:
OutLevel = ELogVerbosity::NoLogging;
}
const FString MessageStr(Length, Message);
GLog->CategorizedLogf(TEXT("LogOVRPlugin"), OutLevel, TEXT("%s"), *MessageStr);
}
} // namespace
#endif // !UE_BUILD_SHIPPING
#endif // !PLATFORM_ANDROID
const FName IOculusXRHMDModule::NAME_OculusXRHMD(TEXT("OculusXRHMD"));
//-------------------------------------------------------------------------------------------------
// FOculusXRHMDModule
//-------------------------------------------------------------------------------------------------
OculusPluginWrapper FOculusXRHMDModule::PluginWrapper{};
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OculusPluginWrapper& FOculusXRHMDModule::GetPluginWrapper()
{
return PluginWrapper;
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
FOculusXRHMDModule::FOculusXRHMDModule()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
bPreInit = false;
bPreInitCalled = false;
OVRPluginHandle = nullptr;
GraphicsAdapterLuid = 0;
#endif
}
void FOculusXRHMDModule::StartupModule()
{
IHeadMountedDisplayModule::StartupModule();
FString PluginShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("OculusXR"))->GetBaseDir(), TEXT("Shaders"));
AddShaderSourceDirectoryMapping(TEXT("/Plugin/OculusXR"), PluginShaderDir);
#if !UE_VERSION_OLDER_THAN(5, 4, 0)
auto StereoLayersFlagsSupplier = FOculusXRStereoLayersFlagsSupplier::Get();
if (StereoLayersFlagsSupplier.IsValid())
{
IModularFeatures::Get().RegisterModularFeature(IStereoLayersFlagsSupplier::GetModularFeatureName(), StereoLayersFlagsSupplier.Get());
TArray<IStereoLayersFlagsSupplier*> FlagsSuppliers = IModularFeatures::Get().GetModularFeatureImplementations<IStereoLayersFlagsSupplier>(IStereoLayersFlagsSupplier::GetModularFeatureName());
if (FlagsSuppliers.Num() > 1)
{
UE_LOG(LogHMD, Log, TEXT("OculusXR Stereo Layer Filters CANNOT be used with layer filters!"));
IModularFeatures::Get().UnregisterModularFeature(IStereoLayersFlagsSupplier::GetModularFeatureName(), StereoLayersFlagsSupplier.Get());
}
}
PreInit();
#endif // !UE_VERSION_OLDER_THAN(5, 4, 0)
ExtensionPluginManager.StartupOpenXRPlugins();
}
void FOculusXRHMDModule::ShutdownModule()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#if !UE_VERSION_OLDER_THAN(5, 4, 0)
auto StereoLayersFlagsSupplier = FOculusXRStereoLayersFlagsSupplier::Get();
if (StereoLayersFlagsSupplier.IsValid())
{
IModularFeatures::Get().UnregisterModularFeature(IStereoLayersFlagsSupplier::GetModularFeatureName(), StereoLayersFlagsSupplier.Get());
}
#endif // !UE_VERSION_OLDER_THAN(5, 4, 0)
UOculusXRFunctionLibrary::ShutdownXRFunctionLibrary();
if (PluginWrapper.IsInitialized())
{
OculusXRTelemetry::FTelemetryBackend::OnEditorShutdown();
PluginWrapper.Shutdown2();
OculusPluginWrapper::DestroyOculusPluginWrapper(&PluginWrapper);
}
if (OVRPluginHandle)
{
FPlatformProcess::FreeDllHandle(OVRPluginHandle);
OVRPluginHandle = nullptr;
}
#endif
}
OculusXR::FExtensionPluginManager& FOculusXRHMDModule::GetExtensionPluginManager()
{
return ExtensionPluginManager;
}
#if PLATFORM_ANDROID
extern bool AndroidThunkCpp_IsOculusMobileApplication();
#endif
FString FOculusXRHMDModule::GetModuleKeyName() const
{
return NAME_OculusXRHMD.ToString();
}
void FOculusXRHMDModule::GetModuleAliases(TArray<FString>& AliasesOut) const
{
// Pre-OculusXR rename (5.0.3 v44)
AliasesOut.Add(TEXT("OculusHMD"));
}
bool FOculusXRHMDModule::PreInit()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (!bPreInitCalled)
{
bPreInit = false;
#if PLATFORM_ANDROID
bPreInitCalled = true;
if (!AndroidThunkCpp_IsOculusMobileApplication())
{
UE_LOG(LogHMD, Log, TEXT("App is not packaged for Oculus Mobile"));
return false;
}
#endif
// Init module if app can render
if (FApp::CanEverRender())
{
// Load OVRPlugin
OVRPluginHandle = GetOVRPluginHandle();
if (!OVRPluginHandle)
{
UE_LOG(LogHMD, Log, TEXT("Failed loading OVRPlugin %s"), TEXT(OVRP_VERSION_STR));
return false;
}
if (!OculusPluginWrapper::InitializeOculusPluginWrapper(&PluginWrapper))
{
UE_LOG(LogHMD, Log, TEXT("Failed InitializeOculusPluginWrapper"));
return false;
}
// Initialize OVRPlugin
ovrpRenderAPIType PreinitApiType = ovrpRenderAPI_None;
#if PLATFORM_ANDROID
void* Activity = (void*)FAndroidApplication::GetGameActivityThis();
PreinitApiType = ovrpRenderAPI_Vulkan;
#else
void* Activity = nullptr;
#endif
#if !PLATFORM_ANDROID
#if !UE_BUILD_SHIPPING
PluginWrapper.SetLogCallback2(OvrpLogCallback2);
#endif // !UE_BUILD_SHIPPING
#endif // !PLATFORM_ANDROID
// Determine Preinit flag based on platform
ovrpPreinitializeFlags PreinitFlag = ovrpPreinitializeFlags::ovrpPreinitializeFlag_None;
#if WITH_EDITOR && PLATFORM_WINDOWS
PreinitFlag = ovrpPreinitializeFlags::ovrpPreinitializeFlag_DisableLogSystemError;
#endif
if (OVRP_FAILURE(PluginWrapper.PreInitialize5(Activity, PreinitApiType, PreinitFlag)))
{
UE_LOG(LogHMD, Log, TEXT("Failed initializing OVRPlugin %s"), TEXT(OVRP_VERSION_STR));
#if WITH_EDITOR && PLATFORM_WINDOWS
// In the editor, we want to allow the headset to connect after the editor has booted.
// To do this, we must have PreInit() return true, to prevent the HMD module from being unloaded.
return GIsEditor;
#else
return false;
#endif
}
#if PLATFORM_WINDOWS
bPreInitCalled = true;
const LUID* DisplayAdapterId;
if (OVRP_SUCCESS(PluginWrapper.GetDisplayAdapterId2((const void**)&DisplayAdapterId)) && DisplayAdapterId)
{
SetGraphicsAdapterLuid(*(const uint64*)DisplayAdapterId);
}
else
{
UE_LOG(LogHMD, Log, TEXT("Could not determine HMD display adapter"));
}
const WCHAR* AudioInDeviceId;
if (OVRP_SUCCESS(PluginWrapper.GetAudioInDeviceId2((const void**)&AudioInDeviceId)) && AudioInDeviceId)
{
GConfig->SetString(TEXT("Oculus.Settings"), TEXT("AudioInputDevice"), AudioInDeviceId, GEngineIni);
}
else
{
UE_LOG(LogHMD, Log, TEXT("Could not determine HMD audio input device"));
}
const WCHAR* AudioOutDeviceId;
if (OVRP_SUCCESS(PluginWrapper.GetAudioOutDeviceId2((const void**)&AudioOutDeviceId)) && AudioOutDeviceId)
{
GConfig->SetString(TEXT("Oculus.Settings"), TEXT("AudioOutputDevice"), AudioOutDeviceId, GEngineIni);
}
else
{
UE_LOG(LogHMD, Log, TEXT("Could not determine HMD audio output device"));
}
#endif
float ModulePriority;
if (!GConfig->GetFloat(TEXT("HMDPluginPriority"), *GetModuleKeyName(), ModulePriority, GEngineIni))
{
// if user doesn't set priority set it for them to allow this hmd to be used if enabled
ModulePriority = 45.0f;
GConfig->SetFloat(TEXT("HMDPluginPriority"), *GetModuleKeyName(), ModulePriority, GEngineIni);
}
UE_LOG(LogHMD, Log, TEXT("FOculusXRHMDModule PreInit successfully"));
bPreInit = true;
}
}
return bPreInit;
#else
return false;
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
}
bool FOculusXRHMDModule::IsHMDConnected()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
UOculusXRHMDRuntimeSettings* HMDSettings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
if (FApp::CanEverRender() && HMDSettings->XrApi != EOculusXRXrApi::NativeOpenXR)
{
return true;
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
uint64 FOculusXRHMDModule::GetGraphicsAdapterLuid()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 || OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
if (!GraphicsAdapterLuid)
{
int GraphicsAdapter;
if (GConfig->GetInt(TEXT("Oculus.Settings"), TEXT("GraphicsAdapter"), GraphicsAdapter, GEngineIni) && GraphicsAdapter >= 0)
{
TRefCountPtr<IDXGIFactory> DXGIFactory;
TRefCountPtr<IDXGIAdapter> DXGIAdapter;
DXGI_ADAPTER_DESC DXGIAdapterDesc;
if (SUCCEEDED(CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)DXGIFactory.GetInitReference())) && SUCCEEDED(DXGIFactory->EnumAdapters(GraphicsAdapter, DXGIAdapter.GetInitReference())) && SUCCEEDED(DXGIAdapter->GetDesc(&DXGIAdapterDesc)))
{
FMemory::Memcpy(&GraphicsAdapterLuid, &DXGIAdapterDesc.AdapterLuid, sizeof(GraphicsAdapterLuid));
}
}
}
#endif
#if OCULUS_HMD_SUPPORTED_PLATFORMS
return GraphicsAdapterLuid;
#else
return 0;
#endif
}
FString FOculusXRHMDModule::GetAudioInputDevice()
{
FString AudioInputDevice;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
GConfig->GetString(TEXT("Oculus.Settings"), TEXT("AudioInputDevice"), AudioInputDevice, GEngineIni);
#endif
return AudioInputDevice;
}
FString FOculusXRHMDModule::GetAudioOutputDevice()
{
FString AudioOutputDevice;
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#if PLATFORM_WINDOWS
if (bPreInit)
{
if (FApp::CanEverRender())
{
const WCHAR* audioOutDeviceId;
if (OVRP_SUCCESS(PluginWrapper.GetAudioOutDeviceId2((const void**)&audioOutDeviceId)) && audioOutDeviceId)
{
AudioOutputDevice = audioOutDeviceId;
}
}
}
#else
GConfig->GetString(TEXT("Oculus.Settings"), TEXT("AudioOutputDevice"), AudioOutputDevice, GEngineIni);
#endif
#endif
return AudioOutputDevice;
}
TSharedPtr<class IXRTrackingSystem, ESPMode::ThreadSafe> FOculusXRHMDModule::CreateTrackingSystem()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (bPreInit || (GIsEditor && PLATFORM_WINDOWS))
{
// If -HMDSimulator is used as the command option to launch UE, use simulator runtime instead of the physical HMD runtime (like PC-Link).
if (FParse::Param(FCommandLine::Get(), TEXT("HMDSimulator")) && IsSimulatorInstalled())
{
if (!IsSimulatorActivated())
{
ToggleOpenXRRuntime();
}
}
OculusXRHMD::FOculusXRHMDPtr OculusXRHMD = FSceneViewExtensions::NewExtension<OculusXRHMD::FOculusXRHMD>();
if (OculusXRHMD->Startup())
{
HeadMountedDisplay = OculusXRHMD;
return OculusXRHMD;
}
}
HeadMountedDisplay = nullptr;
#endif
return nullptr;
}
TSharedPtr<IHeadMountedDisplayVulkanExtensions, ESPMode::ThreadSafe> FOculusXRHMDModule::GetVulkanExtensions()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if (bPreInit)
{
if (!VulkanExtensions.IsValid())
{
VulkanExtensions = MakeShareable(new OculusXRHMD::FVulkanExtensions);
}
}
#if WITH_EDITOR && PLATFORM_WINDOWS
else if (GIsEditor)
{
// OpenXR has no ability to query for possible vulkan extensions without connecting a HMD.
// This is a problem, because we need to create our VkInstance and VkDevice to render in 2D and there's no HMD.
// For now, as a workaround, we hardcode the extensions that Oculus's OpenXR implementation needs.
// Eventually, one of three things has to happen for a proper fix:
//
// 1. OculusXRHMD (or, better, OVRPlugin) maintains a separate VkInstance that has the right extensions,
// and uses the vk_external extensions to transfer data between them when needed.
// 2. OpenXR changes to allow querying instance and device extensions without an active HMD.
// It may still require a physical device handle to list device extensions.
// 3. Oculus's Link implementation for OpenXR changes to allow an XrSystemId to be created before a headset
// is connected (possibly as an opt-in OpenXR extension for backwards compatibility).
//
// (2) or (3) are preferable, but if OpenXR is held constant we will have to do (1).
if (!VulkanExtensions.IsValid())
{
VulkanExtensions = MakeShareable(new OculusXRHMD::FEditorVulkanExtensions);
}
}
#endif
return VulkanExtensions;
#endif
return nullptr;
}
FString FOculusXRHMDModule::GetDeviceSystemName()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
ovrpSystemHeadset SystemHeadset;
if (PluginWrapper.IsInitialized() && OVRP_SUCCESS(PluginWrapper.GetSystemHeadsetType2(&SystemHeadset)))
{
switch (SystemHeadset)
{
case ovrpSystemHeadset_Oculus_Quest:
return FString("Oculus Quest");
case ovrpSystemHeadset_Oculus_Quest_2:
default:
return FString("Oculus Quest2");
case ovrpSystemHeadset_Meta_Quest_Pro:
return FString("Meta Quest Pro");
case ovrpSystemHeadset_Meta_Quest_3:
return FString("Meta Quest 3");
case ovrpSystemHeadset_Meta_Quest_3S:
// The marketplace engine does not have the Quest 3S device profile yet, so just represent the system as Quest 3 to use the closest device profile
#ifdef WITH_OCULUS_BRANCH
return FString("Meta Quest 3S");
#else // WITH_OCULUS_BRANCH
return FString("Meta Quest 3");
#endif // WITH_OCULUS_BRANCH
}
}
return FString();
#else
return FString();
#endif
}
bool FOculusXRHMDModule::IsStandaloneStereoOnlyDevice()
{
#if PLATFORM_ANDROID
return FAndroidMisc::GetDeviceMake() == FString("Oculus");
#else
return false;
#endif
}
bool FOculusXRHMDModule::IsSimulatorActivated()
{
#if PLATFORM_WINDOWS
return FMetaXRSimulator::IsSimulatorActivated();
#else
return false;
#endif
}
bool FOculusXRHMDModule::IsSimulatorInstalled()
{
#if PLATFORM_WINDOWS
return FMetaXRSimulator::IsSimulatorInstalled();
#else
return false;
#endif
}
void FOculusXRHMDModule::ToggleOpenXRRuntime()
{
#if PLATFORM_WINDOWS
FMetaXRSimulator::ToggleOpenXRRuntime();
#endif
}
void FOculusXRHMDModule::LaunchEnvironment(int32 EnvironmentIndex)
{
#if PLATFORM_WINDOWS
FMetaXRSES::LaunchEnvironment(EnvironmentIndex);
#endif
}
void FOculusXRHMDModule::StopServer()
{
#if PLATFORM_WINDOWS
FMetaXRSES::StopServer();
#endif
}
#if OCULUS_HMD_SUPPORTED_PLATFORMS
void* FOculusXRHMDModule::GetOVRPluginHandle()
{
void* OVRPluginHandle = nullptr;
#if PLATFORM_WINDOWS
FString XrApi;
if (!FModuleManager::Get().IsModuleLoaded("OpenXRHMD") || !GConfig->GetString(TEXT("/Script/OculusXRHMD.OculusXRHMDRuntimeSettings"), TEXT("XrApi"), XrApi, GEngineIni) || XrApi.Equals(FString("OVRPluginOpenXR")))
{
FString BinariesPath = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("OculusXR"))->GetBaseDir(), TEXT("/Source/ThirdParty/OVRPlugin/OVRPlugin/Lib/Win64"));
FPlatformProcess::PushDllDirectory(*BinariesPath);
OVRPluginHandle = FPlatformProcess::GetDllHandle(*(BinariesPath / "OpenXR/OVRPlugin.dll"));
FPlatformProcess::PopDllDirectory(*BinariesPath);
}
#elif PLATFORM_ANDROID
OVRPluginHandle = FPlatformProcess::GetDllHandle(TEXT("libOVRPlugin.so"));
#endif // PLATFORM_ANDROID
return OVRPluginHandle;
}
bool FOculusXRHMDModule::PoseToOrientationAndPosition(const FQuat& InOrientation, const FVector& InPosition, FQuat& OutOrientation, FVector& OutPosition) const
{
OculusXRHMD::CheckInGameThread();
OculusXRHMD::FOculusXRHMD* OculusXRHMD = static_cast<OculusXRHMD::FOculusXRHMD*>(HeadMountedDisplay.Pin().Get());
if (OculusXRHMD)
{
ovrpPosef InPose;
InPose.Orientation = OculusXRHMD::ToOvrpQuatf(InOrientation);
InPose.Position = OculusXRHMD::ToOvrpVector3f(InPosition);
OculusXRHMD::FPose OutPose;
if (OculusXRHMD->ConvertPose(InPose, OutPose))
{
OutOrientation = OutPose.Orientation;
OutPosition = OutPose.Position;
return true;
}
}
return false;
}
void FOculusXRHMDModule::SetGraphicsAdapterLuid(uint64 InLuid)
{
GraphicsAdapterLuid = InLuid;
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 || OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
TRefCountPtr<IDXGIFactory> DXGIFactory;
if (SUCCEEDED(CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)DXGIFactory.GetInitReference())))
{
for (int32 adapterIndex = 0;; adapterIndex++)
{
TRefCountPtr<IDXGIAdapter> DXGIAdapter;
DXGI_ADAPTER_DESC DXGIAdapterDesc;
if (FAILED(DXGIFactory->EnumAdapters(adapterIndex, DXGIAdapter.GetInitReference())) || FAILED(DXGIAdapter->GetDesc(&DXGIAdapterDesc)))
{
break;
}
if (!FMemory::Memcmp(&GraphicsAdapterLuid, &DXGIAdapterDesc.AdapterLuid, sizeof(GraphicsAdapterLuid)))
{
// Remember this adapterIndex so we use the right adapter, even when we startup without HMD connected
GConfig->SetInt(TEXT("Oculus.Settings"), TEXT("GraphicsAdapter"), adapterIndex, GEngineIni);
break;
}
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 || OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
IMPLEMENT_MODULE(FOculusXRHMDModule, OculusXRHMD)

View File

@@ -0,0 +1,126 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#include "IHeadMountedDisplay.h"
#include "OculusXRFunctionLibrary.h"
#include "OculusXRHMD_VulkanExtensions.h"
#include "OculusXRPluginWrapper.h"
#include "OpenXR/OculusXRExtensionPluginManager.h"
//-------------------------------------------------------------------------------------------------
// FOculusXRHMDModule
//-------------------------------------------------------------------------------------------------
class FOculusXRHMDModule : public IOculusXRHMDModule
{
public:
FOculusXRHMDModule();
static inline FOculusXRHMDModule& Get()
{
return FModuleManager::LoadModuleChecked<FOculusXRHMDModule>(NAME_OculusXRHMD);
}
// IModuleInterface
virtual void StartupModule() override;
virtual void ShutdownModule() override;
// IHeadMountedDisplayModule
virtual FString GetModuleKeyName() const override;
virtual void GetModuleAliases(TArray<FString>& AliasesOut) const override;
virtual bool PreInit() override;
virtual bool IsHMDConnected() override;
virtual uint64 GetGraphicsAdapterLuid() override;
virtual FString GetAudioInputDevice() override;
virtual FString GetAudioOutputDevice() override;
virtual FString GetDeviceSystemName() override;
virtual TSharedPtr<class IXRTrackingSystem, ESPMode::ThreadSafe> CreateTrackingSystem() override;
virtual TSharedPtr<IHeadMountedDisplayVulkanExtensions, ESPMode::ThreadSafe> GetVulkanExtensions() override;
virtual bool IsStandaloneStereoOnlyDevice() override;
virtual OculusXR::FExtensionPluginManager& GetExtensionPluginManager();
// IOculusXRHMDModule
virtual void GetPose(FRotator& DeviceRotation, FVector& DevicePosition, FVector& NeckPosition, bool bUseOrienationForPlayerCamera = false, bool bUsePositionForPlayerCamera = false, const FVector PositionScale = FVector::ZeroVector) override
{
UOculusXRFunctionLibrary::GetPose(DeviceRotation, DevicePosition, NeckPosition, bUseOrienationForPlayerCamera, bUsePositionForPlayerCamera, PositionScale);
}
virtual void GetRawSensorData(FVector& AngularAcceleration, FVector& LinearAcceleration, FVector& AngularVelocity, FVector& LinearVelocity, float& TimeInSeconds) override
{
UOculusXRFunctionLibrary::GetRawSensorData(AngularAcceleration, LinearAcceleration, AngularVelocity, LinearVelocity, TimeInSeconds, EOculusXRTrackedDeviceType::HMD);
}
virtual bool GetUserProfile(struct FOculusXRHmdUserProfile& Profile) override
{
return UOculusXRFunctionLibrary::GetUserProfile(Profile);
}
virtual void SetBaseRotationAndBaseOffsetInMeters(FRotator Rotation, FVector BaseOffsetInMeters, EOrientPositionSelector::Type Options) override
{
UOculusXRFunctionLibrary::SetBaseRotationAndBaseOffsetInMeters(Rotation, BaseOffsetInMeters, Options);
}
virtual void GetBaseRotationAndBaseOffsetInMeters(FRotator& OutRotation, FVector& OutBaseOffsetInMeters) override
{
UOculusXRFunctionLibrary::GetBaseRotationAndBaseOffsetInMeters(OutRotation, OutBaseOffsetInMeters);
}
virtual void SetBaseRotationAndPositionOffset(FRotator BaseRot, FVector PosOffset, EOrientPositionSelector::Type Options) override
{
UOculusXRFunctionLibrary::SetBaseRotationAndPositionOffset(BaseRot, PosOffset, Options);
}
virtual void GetBaseRotationAndPositionOffset(FRotator& OutRot, FVector& OutPosOffset) override
{
UOculusXRFunctionLibrary::GetBaseRotationAndPositionOffset(OutRot, OutPosOffset);
}
virtual class IStereoLayers* GetStereoLayers() override
{
return UOculusXRFunctionLibrary::GetStereoLayers();
}
bool IsOVRPluginAvailable() const
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
return OVRPluginHandle != nullptr;
#else
return false;
#endif
}
// FMetaXRSimulator
OCULUSXRHMD_API static bool IsSimulatorActivated();
OCULUSXRHMD_API static void ToggleOpenXRRuntime();
OCULUSXRHMD_API static bool IsSimulatorInstalled();
// FMetaXRSES
OCULUSXRHMD_API static void LaunchEnvironment(int32 EnvironmentIndex);
OCULUSXRHMD_API static void StopServer();
#if OCULUS_HMD_SUPPORTED_PLATFORMS
OCULUSXRHMD_API static void* GetOVRPluginHandle();
OCULUSXRHMD_API static OculusPluginWrapper& GetPluginWrapper();
virtual bool PoseToOrientationAndPosition(const FQuat& InOrientation, const FVector& InPosition, FQuat& OutOrientation, FVector& OutPosition) const override;
protected:
void SetGraphicsAdapterLuid(uint64 InLuid);
static OculusPluginWrapper PluginWrapper;
bool bPreInit;
bool bPreInitCalled;
void* OVRPluginHandle;
uint64 GraphicsAdapterLuid;
TWeakPtr<IHeadMountedDisplay, ESPMode::ThreadSafe> HeadMountedDisplay;
TSharedPtr<IHeadMountedDisplayVulkanExtensions, ESPMode::ThreadSafe> VulkanExtensions;
OculusXR::FExtensionPluginManager ExtensionPluginManager;
friend class ::OculusXRHMD::FOculusXRHMD;
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
};

View File

@@ -0,0 +1,103 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMDPrivate.h"
#include "RHICommandList.h"
#include "RenderingThread.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// Utility functions
//-------------------------------------------------------------------------------------------------
// TODO: Change in case of parallel game threads
bool InGameThread()
{
if (GIsGameThreadIdInitialized)
{
return FPlatformTLS::GetCurrentThreadId() == GGameThreadId;
}
else
{
return true;
}
}
bool InRenderThread()
{
#if UE_VERSION_OLDER_THAN(5, 5, 0)
if (GIsThreadedRendering && !GIsRenderingThreadSuspended.Load(EMemoryOrder::Relaxed))
#else
if (GIsThreadedRendering)
#endif
{
return IsInParallelRenderingThread();
}
else
{
return InGameThread();
}
}
// TODO: Change in case of parallel RHI threads
bool InRHIThread()
{
#if UE_VERSION_OLDER_THAN(5, 5, 0)
if (GIsThreadedRendering && !GIsRenderingThreadSuspended.Load(EMemoryOrder::Relaxed))
#else
if (GIsThreadedRendering)
#endif
{
if (IsRHIThreadRunning())
{
if (IsInRHIThread())
{
return true;
}
if (IsInParallelRenderingThread())
{
return GetImmediateCommandList_ForRenderCommand().Bypass();
}
return false;
}
else
{
return IsInParallelRenderingThread();
}
}
else
{
return InGameThread();
}
}
bool ConvertPose_Internal(const FPose& InPose, FPose& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale)
{
// apply base orientation correction
OutPose.Orientation = BaseOrientation.Inverse() * InPose.Orientation;
OutPose.Orientation.Normalize();
// correct position according to BaseOrientation and BaseOffset.
OutPose.Position = (InPose.Position - BaseOffset) * WorldToMetersScale;
OutPose.Position = BaseOrientation.Inverse().RotateVector(OutPose.Position);
return true;
}
bool ConvertPose_Internal(const ovrpPosef& InPose, FPose& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale)
{
return ConvertPose_Internal(FPose(ToFQuat(InPose.Orientation), ToFVector(InPose.Position)), OutPose, BaseOrientation, BaseOffset, WorldToMetersScale);
}
bool ConvertPose_Internal(const FPose& InPose, ovrpPosef& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale)
{
OutPose.Orientation = ToOvrpQuatf(BaseOrientation * InPose.Orientation);
OutPose.Position = ToOvrpVector3f(BaseOrientation.RotateVector(InPose.Position) / WorldToMetersScale + BaseOffset);
return true;
}
} // namespace OculusXRHMD

View File

@@ -0,0 +1,317 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameFramework/WorldSettings.h"
#include "IOculusXRHMDModule.h"
#include "OculusXRFunctionLibrary.h"
#include "OculusXRPassthroughLayerShapes.h"
#include "StereoRendering.h"
#include "HAL/RunnableThread.h"
#include "RHI.h"
#include <functional>
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#define OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 PLATFORM_WINDOWS
#define OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12 PLATFORM_WINDOWS
#define OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN (PLATFORM_WINDOWS || PLATFORM_ANDROID)
#else
#define OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11 0
#define OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12 0
#define OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN 0
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
//-------------------------------------------------------------------------------------------------
// OVRPlugin
//-------------------------------------------------------------------------------------------------
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRPluginWrapper.h"
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
//-------------------------------------------------------------------------------------------------
// Utility functions
//-------------------------------------------------------------------------------------------------
namespace OculusXRHMD
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
struct FPose
{
FQuat Orientation;
FVector Position;
FPose()
: Orientation(EForceInit::ForceInit)
, Position(EForceInit::ForceInit)
{
}
FPose(const FQuat& InOrientation, const FVector& InPosition)
: Orientation(InOrientation), Position(InPosition) {}
FPose Inverse() const
{
FQuat InvOrientation = Orientation.Inverse();
FVector InvPosition = InvOrientation.RotateVector(-Position);
return FPose(InvOrientation, InvPosition);
}
FPose operator*(const FPose& other) const
{
return FPose(Orientation * other.Orientation, Orientation.RotateVector(other.Position) + Position);
}
};
/** Converts ovrpQuatf to FQuat */
FORCEINLINE FQuat ToFQuat(const ovrpQuatf& InQuat)
{
return FQuat(-InQuat.z, InQuat.x, InQuat.y, -InQuat.w);
}
/** Converts FQuat to ovrpQuatf */
FORCEINLINE ovrpQuatf ToOvrpQuatf(const FQuat& InQuat)
{
return ovrpQuatf{ static_cast<float>(InQuat.Y), static_cast<float>(InQuat.Z), static_cast<float>(-InQuat.X), static_cast<float>(-InQuat.W) };
}
/** Converts vector from Oculus to Unreal */
FORCEINLINE FVector ToFVector(const ovrpVector3f& InVec)
{
return FVector(-InVec.z, InVec.x, InVec.y);
}
/** Converts vector from Unreal to Oculus. */
FORCEINLINE ovrpVector3f ToOvrpVector3f(const FVector& InVec)
{
return ovrpVector3f{ static_cast<float>(InVec.Y), static_cast<float>(InVec.Z), static_cast<float>(-InVec.X) };
}
FORCEINLINE FMatrix ToFMatrix(const ovrpMatrix4f& vtm)
{
// Rows and columns are swapped between ovrpMatrix4f and FMatrix
return FMatrix(
FPlane(vtm.M[0][0], vtm.M[1][0], vtm.M[2][0], vtm.M[3][0]),
FPlane(vtm.M[0][1], vtm.M[1][1], vtm.M[2][1], vtm.M[3][1]),
FPlane(vtm.M[0][2], vtm.M[1][2], vtm.M[2][2], vtm.M[3][2]),
FPlane(vtm.M[0][3], vtm.M[1][3], vtm.M[2][3], vtm.M[3][3]));
}
FORCEINLINE ovrpVector4f LinearColorToOvrpVector4f(const FLinearColor& InColor)
{
return ovrpVector4f{ InColor.R, InColor.G, InColor.B, InColor.A };
}
FORCEINLINE ovrpRecti ToOvrpRecti(const FIntRect& rect)
{
return ovrpRecti{ { rect.Min.X, rect.Min.Y }, { rect.Size().X, rect.Size().Y } };
}
FORCEINLINE FLinearColor ToFLinearColor(const ovrpColorf& color)
{
return FLinearColor(color.r, color.g, color.b, color.a);
}
FORCEINLINE ovrpColorf ToOvrpColorf(const FLinearColor LinearColor)
{
return ovrpColorf{ LinearColor.R, LinearColor.G, LinearColor.B, LinearColor.A };
}
FORCEINLINE ovrpMatrix4f ToOvrpMatrix(FMatrix Matrix)
{
ovrpMatrix4f Result;
Result.M[0][0] = Matrix.M[0][0];
Result.M[0][1] = Matrix.M[0][1];
Result.M[0][2] = Matrix.M[0][2];
Result.M[0][3] = Matrix.M[0][3];
Result.M[1][0] = Matrix.M[1][0];
Result.M[1][1] = Matrix.M[1][1];
Result.M[1][2] = Matrix.M[1][2];
Result.M[1][3] = Matrix.M[1][3];
Result.M[2][0] = Matrix.M[2][0];
Result.M[2][1] = Matrix.M[2][1];
Result.M[2][2] = Matrix.M[2][2];
Result.M[2][3] = Matrix.M[2][3];
Result.M[3][0] = Matrix.M[3][0];
Result.M[3][1] = Matrix.M[3][1];
Result.M[3][2] = Matrix.M[3][2];
Result.M[3][3] = Matrix.M[3][3];
return Result;
}
/** Helper that converts ovrTrackedDeviceType to EOculusXRTrackedDeviceType */
FORCEINLINE EOculusXRTrackedDeviceType ToEOculusXRTrackedDeviceType(ovrpNode Source)
{
EOculusXRTrackedDeviceType Destination = EOculusXRTrackedDeviceType::All; // Best attempt at initialization
switch (Source)
{
case ovrpNode_None:
Destination = EOculusXRTrackedDeviceType::None;
break;
case ovrpNode_Head:
Destination = EOculusXRTrackedDeviceType::HMD;
break;
case ovrpNode_HandLeft:
Destination = EOculusXRTrackedDeviceType::LTouch;
break;
case ovrpNode_HandRight:
Destination = EOculusXRTrackedDeviceType::RTouch;
break;
case ovrpNode_DeviceObjectZero:
Destination = EOculusXRTrackedDeviceType::DeviceObjectZero;
break;
default:
break;
}
return Destination;
}
/** Helper that converts EOculusXRTrackedDeviceType to ovrTrackedDeviceType */
FORCEINLINE ovrpNode ToOvrpNode(EOculusXRTrackedDeviceType Source)
{
ovrpNode Destination = ovrpNode_None; // Best attempt at initialization
switch (Source)
{
case EOculusXRTrackedDeviceType::None:
Destination = ovrpNode_None;
break;
case EOculusXRTrackedDeviceType::HMD:
Destination = ovrpNode_Head;
break;
case EOculusXRTrackedDeviceType::LTouch:
Destination = ovrpNode_HandLeft;
break;
case EOculusXRTrackedDeviceType::RTouch:
Destination = ovrpNode_HandRight;
break;
case EOculusXRTrackedDeviceType::DeviceObjectZero:
Destination = ovrpNode_DeviceObjectZero;
break;
default:
break;
}
return Destination;
}
FORCEINLINE int32 ToExternalDeviceId(const ovrpNode Source)
{
int32 ExternalDeviceId = INDEX_NONE;
switch (Source)
{
case ovrpNode_Head:
// required to be zero (see IXRTrackingSystem::HMDDeviceId)
ExternalDeviceId = 0;
break;
case ovrpNode_None:
case ovrpNode_Count:
case ovrpNode_EnumSize:
// ExternalDeviceId = INDEX_NONE;
break;
default:
// add one, in case the enum value is zero (conflicting with the HMD)
ExternalDeviceId = 1 + (int32)Source;
break;
}
return ExternalDeviceId;
}
FORCEINLINE ovrpNode ToOvrpNode(const int32 ExternalDeviceId)
{
ovrpNode Destination = ovrpNode_None;
switch (ExternalDeviceId)
{
case 0:
// zero implies HMD (see ToExternalDeviceId/IXRTrackingSystem::HMDDeviceId)
Destination = ovrpNode_Head;
break;
case -1:
// Destination = ovrpNode_None;
break;
default:
// we added one to avoid collision with the HMD's ID (see ToExternalDeviceId)
Destination = ovrpNode(ExternalDeviceId - 1);
break;
}
return Destination;
}
bool ConvertPose_Internal(const FPose& InPose, FPose& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale);
bool ConvertPose_Internal(const ovrpPosef& InPose, FPose& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale);
bool ConvertPose_Internal(const FPose& InPose, ovrpPosef& OutPose, const FQuat BaseOrientation, const FVector BaseOffset, float WorldToMetersScale);
FORCEINLINE ovrpInsightPassthroughColorMapType ToOVRPColorMapType(EOculusXRColorMapType InColorMapType)
{
switch (InColorMapType)
{
case ColorMapType_GrayscaleToColor:
return ovrpInsightPassthroughColorMapType_MonoToRgba;
case ColorMapType_Grayscale:
return ovrpInsightPassthroughColorMapType_MonoToMono;
case ColorMapType_ColorAdjustment:
return ovrpInsightPassthroughColorMapType_BrightnessContrastSaturation;
case ColorMapType_ColorLut:
return ovrpInsightPassthroughColorMapType_ColorLut;
case ColorMapType_ColorLut_Interpolated:
return ovrpInsightPassthroughColorMapType_InterpolatedColorLut;
default:
return ovrpInsightPassthroughColorMapType_None;
}
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
/** Check currently executing from Game thread */
OCULUSXRHMD_API bool InGameThread();
FORCEINLINE void CheckInGameThread()
{
#if DO_CHECK
check(InGameThread());
#endif
}
/** Check currently executing from Render thread */
OCULUSXRHMD_API bool InRenderThread();
FORCEINLINE void CheckInRenderThread()
{
#if DO_CHECK
check(InRenderThread());
#endif
}
/** Check currently executing from RHI thread */
OCULUSXRHMD_API bool InRHIThread();
FORCEINLINE void CheckInRHIThread()
{
#if DO_CHECK
check(InRHIThread());
#endif
}
FORCEINLINE bool GetUnitScaleFactorFromSettings(UWorld* World, float& outWorldToMeters)
{
if (IsValid(World))
{
const auto* WorldSettings = World->GetWorldSettings();
if (IsValid(WorldSettings))
{
outWorldToMeters = WorldSettings->WorldToMeters;
return true;
}
}
return false;
}
} // namespace OculusXRHMD

View File

@@ -0,0 +1,64 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#if OCULUS_HMD_SUPPORTED_PLATFORMS
//-------------------------------------------------------------------------------------------------
// D3D11
//-------------------------------------------------------------------------------------------------
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11
#include "ID3D11DynamicRHI.h"
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11
//-------------------------------------------------------------------------------------------------
// D3D12
//-------------------------------------------------------------------------------------------------
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
#define GetD3D11CubeFace GetD3D12CubeFace
#define VerifyD3D11Result VerifyD3D12Result
#define GetD3D11TextureFromRHITexture GetD3D12TextureFromRHITexture
#define FRingAllocation FRingAllocation_D3D12
#define GetRenderTargetFormat GetRenderTargetFormat_D3D12
#define ED3D11ShaderOffsetBuffer ED3D12ShaderOffsetBuffer
#define FindShaderResourceDXGIFormat FindShaderResourceDXGIFormat_D3D12
#define FindUnorderedAccessDXGIFormat FindUnorderedAccessDXGIFormat_D3D12
#define FindDepthStencilDXGIFormat FindDepthStencilDXGIFormat_D3D12
#define HasStencilBits HasStencilBits_D3D12
#define FVector4VertexDeclaration FVector4VertexDeclaration_D3D12
#define GLOBAL_CONSTANT_BUFFER_INDEX GLOBAL_CONSTANT_BUFFER_INDEX_D3D12
#define MAX_CONSTANT_BUFFER_SLOTS MAX_CONSTANT_BUFFER_SLOTS_D3D12
#define FD3DGPUProfiler FD3D12GPUProfiler
#define FRangeAllocator FRangeAllocator_D3D12
#include "ID3D12DynamicRHI.h"
#undef GetD3D11CubeFace
#undef VerifyD3D11Result
#undef GetD3D11TextureFromRHITexture
#undef FRingAllocation
#undef GetRenderTargetFormat
#undef ED3D11ShaderOffsetBuffer
#undef FindShaderResourceDXGIFormat
#undef FindUnorderedAccessDXGIFormat
#undef FindDepthStencilDXGIFormat
#undef HasStencilBits
#undef FVector4VertexDeclaration
#undef GLOBAL_CONSTANT_BUFFER_INDEX
#undef MAX_CONSTANT_BUFFER_SLOTS
#undef FD3DGPUProfiler
#undef FRangeAllocator
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
//-------------------------------------------------------------------------------------------------
// Vulkan
//-------------------------------------------------------------------------------------------------
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN
#include "IVulkanDynamicRHI.h"
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,378 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMDRuntimeSettings.h"
#include "Algo/ForEach.h"
//////////////////////////////////////////////////////////////////////////
// UOculusXRHMDRuntimeSettings
#include "OculusXRHMD_Settings.h"
#include "DeviceProfiles/DeviceProfile.h"
#include "DeviceProfiles/DeviceProfileManager.h"
UOculusXRHMDRuntimeSettings::UOculusXRHMDRuntimeSettings(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, bAutoEnabled(false)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
// FSettings is the sole source of truth for Oculus default settings
OculusXRHMD::FSettings DefaultSettings;
SystemSplashBackground = DefaultSettings.SystemSplashBackground;
bSupportsDash = DefaultSettings.Flags.bSupportsDash;
bCompositesDepth = DefaultSettings.Flags.bCompositeDepth;
bHQDistortion = DefaultSettings.Flags.bHQDistortion;
SuggestedCpuPerfLevel = DefaultSettings.SuggestedCpuPerfLevel;
SuggestedGpuPerfLevel = DefaultSettings.SuggestedGpuPerfLevel;
FoveatedRenderingMethod = DefaultSettings.FoveatedRenderingMethod;
FoveatedRenderingLevel = DefaultSettings.FoveatedRenderingLevel;
bDynamicFoveatedRendering = DefaultSettings.bDynamicFoveatedRendering;
bSupportEyeTrackedFoveatedRendering = DefaultSettings.bSupportEyeTrackedFoveatedRendering;
bFocusAware = DefaultSettings.Flags.bFocusAware;
bDynamicResolution = DefaultSettings.Flags.bPixelDensityAdaptive;
XrApi = DefaultSettings.XrApi;
ColorSpace = DefaultSettings.ColorSpace;
ControllerPoseAlignment = DefaultSettings.ControllerPoseAlignment;
bRequiresSystemKeyboard = DefaultSettings.Flags.bRequiresSystemKeyboard;
HandTrackingSupport = DefaultSettings.HandTrackingSupport;
HandTrackingFrequency = DefaultSettings.HandTrackingFrequency;
HandTrackingVersion = DefaultSettings.HandTrackingVersion;
bInsightPassthroughEnabled = DefaultSettings.Flags.bInsightPassthroughEnabled;
bBodyTrackingEnabled = DefaultSettings.Flags.bBodyTrackingEnabled;
bEyeTrackingEnabled = DefaultSettings.Flags.bEyeTrackingEnabled;
bFaceTrackingEnabled = DefaultSettings.Flags.bFaceTrackingEnabled;
bSupportExperimentalFeatures = DefaultSettings.bSupportExperimentalFeatures;
bAnchorSupportEnabled = DefaultSettings.Flags.bAnchorSupportEnabled;
bAnchorSharingEnabled = DefaultSettings.Flags.bAnchorSharingEnabled;
bSceneSupportEnabled = DefaultSettings.Flags.bSceneSupportEnabled;
bIterativeCookOnTheFly = DefaultSettings.Flags.bIterativeCookOnTheFly;
bSetActivePIEToPrimary = DefaultSettings.Flags.bSetActivePIEToPrimary;
bSetCVarPIEToPrimary = DefaultSettings.Flags.bSetCVarPIEToPrimary;
bUpdateHeadPoseForInactivePlayer = DefaultSettings.Flags.bUpdateHeadPoseForInactivePlayer;
MPPoseRestoreType = DefaultSettings.MPPoseRestoreType;
bBoundaryVisibilitySupportEnabled = DefaultSettings.Flags.bBoundaryVisibilitySupportEnabled;
bDefaultBoundaryVisibilitySuppressed = DefaultSettings.Flags.bDefaultBoundaryVisibilitySuppressed;
bColocationSessionsEnabled = DefaultSettings.Flags.bColocationSessionsEnabled;
ProcessorFavor = DefaultSettings.ProcessorFavor;
bTileTurnOffEnabled = DefaultSettings.Flags.bTileTurnOffEnabled;
BodyTrackingFidelity = DefaultSettings.BodyTrackingFidelity;
BodyTrackingJointSet = DefaultSettings.BodyTrackingJointSet;
FaceTrackingDataSource.Empty(static_cast<int8>(EFaceTrackingDataSourceConfig::MAX));
FaceTrackingDataSource.Append(DefaultSettings.FaceTrackingDataSource);
bFaceTrackingVisemesEnabled = DefaultSettings.bFaceTrackingVisemesEnabled;
// Default this to false, FSettings doesn't have a separate composite depth flag for mobile
bCompositeDepthMobile = false;
bThumbstickDpadEmulationEnabled = true;
bSupportSBC = DefaultSettings.Flags.bSupportSBC;
SBCPath = DefaultSettings.SBCPath;
#else
// Some set of reasonable defaults, since blueprints are still available on non-Oculus platforms.
SystemSplashBackground = ESystemSplashBackgroundType::Black;
bSupportsDash = false;
bCompositesDepth = false;
bHQDistortion = false;
SuggestedCpuPerfLevel = EOculusXRProcessorPerformanceLevel::SustainedLow;
SuggestedGpuPerfLevel = EOculusXRProcessorPerformanceLevel::SustainedHigh;
FoveatedRenderingMethod = EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering;
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::Off;
bDynamicFoveatedRendering = false;
bSupportEyeTrackedFoveatedRendering = false;
bDynamicResolution = false;
bCompositeDepthMobile = false;
bFocusAware = true;
XrApi = EOculusXRXrApi::OVRPluginOpenXR;
bLateLatching = false;
ColorSpace = EOculusXRColorSpace::P3;
ControllerPoseAlignment = EOculusXRControllerPoseAlignment::Default;
bRequiresSystemKeyboard = false;
HandTrackingSupport = EOculusXRHandTrackingSupport::ControllersOnly;
HandTrackingFrequency = EOculusXRHandTrackingFrequency::Low;
HandTrackingVersion = EOculusXRHandTrackingVersion::Default;
bInsightPassthroughEnabled = false;
bSupportExperimentalFeatures = false;
bBodyTrackingEnabled = false;
bEyeTrackingEnabled = false;
bFaceTrackingEnabled = false;
bSupportSBC = false;
bFaceTrackingVisemesEnabled = false;
bAnchorSupportEnabled = false;
bAnchorSharingEnabled = false;
bSceneSupportEnabled = false;
bIterativeCookOnTheFly = false;
bSetActivePIEToPrimary = false;
bSetCVarPIEToPrimary = false;
bUpdateHeadPoseForInactivePlayer = false;
MPPoseRestoreType = EOculusXRMPPoseRestoreType::Disabled;
bBoundaryVisibilitySupportEnabled = false;
bDefaultBoundaryVisibilitySuppressed = false;
bColocationSessionsEnabled = false;
ProcessorFavor = EProcessorFavor::FavorEqually;
bTileTurnOffEnabled = false;
bThumbstickDpadEmulationEnabled = true;
#endif
LoadFromIni();
}
#if WITH_EDITOR
bool UOculusXRHMDRuntimeSettings::CanEditChange(const FProperty* InProperty) const
{
bool bIsEditable = Super::CanEditChange(InProperty);
if (bIsEditable && InProperty)
{
const FName PropertyName = InProperty->GetFName();
if (PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, XrApi) && !FModuleManager::Get().IsModuleLoaded("OpenXRHMD"))
{
bIsEditable = false;
}
// Disable settings for marketplace release that are only compatible with the Oculus engine fork
#ifndef WITH_OCULUS_BRANCH
if (PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, FoveatedRenderingMethod) || PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, bSupportEyeTrackedFoveatedRendering) || PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, bDynamicResolution))
{
bIsEditable = false;
}
#endif // WITH_OCULUS_BRANCH
}
return bIsEditable;
}
void UOculusXRHMDRuntimeSettings::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.Property != nullptr)
{
// Automatically switch to Fixed Foveated Rendering when removing Eye Tracked Foveated rendering support
if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, bSupportEyeTrackedFoveatedRendering) && !bSupportEyeTrackedFoveatedRendering)
{
FoveatedRenderingMethod = EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering;
UpdateSinglePropertyInConfigFile(GetClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, FoveatedRenderingMethod)), GetDefaultConfigFilename());
}
// Automatically enable support for eye tracked foveated rendering when selecting the Eye Tracked Foveated Rendering method
if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, FoveatedRenderingMethod) && FoveatedRenderingMethod == EOculusXRFoveatedRenderingMethod::EyeTrackedFoveatedRendering)
{
bSupportEyeTrackedFoveatedRendering = true;
UpdateSinglePropertyInConfigFile(GetClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, bSupportEyeTrackedFoveatedRendering)), GetDefaultConfigFilename());
}
if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, SupportedDevices))
{
if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayAdd)
{
// Get a list of all available devices
TArray<EOculusXRSupportedDevices> deviceList;
#define OCULUS_DEVICE_LOOP(device) deviceList.Add(device);
FOREACH_ENUM_EOCULUSXRSUPPORTEDDEVICES(OCULUS_DEVICE_LOOP);
#undef OCULUS_DEVICE_LOOP
// Add last device that isn't already in the list
for (int i = deviceList.Num() - 1; i >= 0; --i)
{
if (!SupportedDevices.Contains(deviceList[i]))
{
SupportedDevices.Last() = deviceList[i];
break;
}
// Just add another copy of the first device if nothing was available
SupportedDevices.Last() = deviceList[deviceList.Num() - 1];
}
}
}
// Force CTXPT when Passthough is enabled
if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, bInsightPassthroughEnabled) && bInsightPassthroughEnabled)
{
SystemSplashBackground = ESystemSplashBackgroundType::Contextual;
UpdateSinglePropertyInConfigFile(GetClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, SystemSplashBackground)), GetDefaultConfigFilename());
}
}
}
#endif // WITH_EDITOR
void UOculusXRHMDRuntimeSettings::PostInitProperties()
{
Super::PostInitProperties();
RenameProperties();
MigratePixelDensityRange();
const TCHAR* OculusSettings = TEXT("/Script/OculusXRHMD.OculusXRHMDRuntimeSettings");
if (!FModuleManager::Get().IsModuleLoaded("OpenXRHMD"))
{
XrApi = EOculusXRXrApi::OVRPluginOpenXR;
UpdateSinglePropertyInConfigFile(GetClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, XrApi)), GetDefaultConfigFilename());
}
// Force CTXPT for MR apps
if (bInsightPassthroughEnabled && SystemSplashBackground != ESystemSplashBackgroundType::Contextual)
{
SystemSplashBackground = ESystemSplashBackgroundType::Contextual;
UpdateSinglePropertyInConfigFile(GetClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, SystemSplashBackground)), GetDefaultConfigFilename());
}
UpdateSinglePropertyInConfigFile(GetClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UOculusXRHMDRuntimeSettings, SBCPath)), GetDefaultConfigFilename());
}
void UOculusXRHMDRuntimeSettings::LoadFromIni()
{
const TCHAR* OculusSettings = TEXT("Oculus.Settings");
bool v;
float f;
if (GConfig->GetFloat(OculusSettings, TEXT("PixelDensityMax"), f, GEngineIni))
{
UE_LOG(LogTemp, Error, TEXT("DONOT manually add setting to Oculus.Settings. PixelDensityMax is ignored."));
PixelDensityMax = 0.f;
}
if (GConfig->GetFloat(OculusSettings, TEXT("PixelDensityMin"), f, GEngineIni))
{
UE_LOG(LogTemp, Error, TEXT("DONOT manually add setting to Oculus.Settings. PixelDensityMin is ignored."));
PixelDensityMin = 0.f;
}
if (GConfig->GetBool(OculusSettings, TEXT("bHQDistortion"), v, GEngineIni))
{
bHQDistortion = v;
}
if (GConfig->GetBool(OculusSettings, TEXT("bCompositeDepth"), v, GEngineIni))
{
bCompositesDepth = v;
}
}
/** This essentially acts like redirects for plugin settings saved in the engine config.
Anything added here should check for the current setting in the config so that if the dev changes the setting manually, we don't overwrite it with the old setting.
Note: Do not use UpdateSinglePropertyInConfigFile() here, since that uses a temp config to save the single property,
it'll get overwritten when GConfig->RemoveKey() marks the main config as dirty and it gets saved again **/
void UOculusXRHMDRuntimeSettings::RenameProperties()
{
const TCHAR* OculusSettings = TEXT("/Script/OculusXRHMD.OculusXRHMDRuntimeSettings");
bool v = false;
FString str;
// FFRLevel was renamed to FoveatedRenderingLevel
if (!GConfig->GetString(OculusSettings, GET_MEMBER_NAME_STRING_CHECKED(UOculusXRHMDRuntimeSettings, FoveatedRenderingLevel), str, GetDefaultConfigFilename()) && GConfig->GetString(OculusSettings, TEXT("FFRLevel"), str, GetDefaultConfigFilename()))
{
if (str.Equals(TEXT("FFR_Off")))
{
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::Off;
}
else if (str.Equals(TEXT("FFR_Low")))
{
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::Low;
}
else if (str.Equals(TEXT("FFR_Medium")))
{
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::Medium;
}
else if (str.Equals(TEXT("FFR_High")))
{
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::High;
}
else if (str.Equals(TEXT("FFR_HighTop")))
{
FoveatedRenderingLevel = EOculusXRFoveatedRenderingLevel::HighTop;
}
// Use GetNameStringByValue() here because GetValueAsString() includes the type name as well
GConfig->SetString(OculusSettings, GET_MEMBER_NAME_STRING_CHECKED(UOculusXRHMDRuntimeSettings, FoveatedRenderingLevel), *StaticEnum<EOculusXRFoveatedRenderingLevel>()->GetNameStringByValue((int64)FoveatedRenderingLevel), GetDefaultConfigFilename());
GConfig->RemoveKey(OculusSettings, TEXT("FFRLevel"), GetDefaultConfigFilename());
}
// FFRDynamic was renamed to bDynamicFoveatedRendering
if (!GConfig->GetString(OculusSettings, GET_MEMBER_NAME_STRING_CHECKED(UOculusXRHMDRuntimeSettings, bDynamicFoveatedRendering), str, GetDefaultConfigFilename()) && GConfig->GetBool(OculusSettings, TEXT("FFRDynamic"), v, GetDefaultConfigFilename()))
{
bDynamicFoveatedRendering = v;
GConfig->SetBool(OculusSettings, GET_MEMBER_NAME_STRING_CHECKED(UOculusXRHMDRuntimeSettings, bDynamicFoveatedRendering), bDynamicFoveatedRendering, GetDefaultConfigFilename());
GConfig->RemoveKey(OculusSettings, TEXT("FFRDynamic"), GetDefaultConfigFilename());
}
const FString Quest = TEXT("Quest");
#ifndef WITH_OCULUS_BRANCH
const TCHAR* AndroidSettings = TEXT("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings");
TArray<FString> PackageList;
const TCHAR* PackageForMobileKey = TEXT("+PackageForOculusMobile");
if (GConfig->GetArray(AndroidSettings, PackageForMobileKey, PackageList, GetDefaultConfigFilename()))
{
const FString Quest2 = TEXT("Quest2");
if (PackageList.Contains(Quest))
{
PackageList.Remove(Quest);
if (!PackageList.Contains(Quest2))
{
PackageList.Add(Quest2);
}
GConfig->SetArray(AndroidSettings, PackageForMobileKey, PackageList, GetDefaultConfigFilename());
}
}
#endif // WITH_OCULUS_BRANCH
TArray<FString> DeviceList;
const FString SupportedDevicesKey = FString("+").Append(GET_MEMBER_NAME_STRING_CHECKED(UOculusXRHMDRuntimeSettings, SupportedDevices));
if (GConfig->GetArray(OculusSettings, *SupportedDevicesKey, DeviceList, GetDefaultConfigFilename()))
{
const EOculusXRSupportedDevices LastSupportedDevice = EOculusXRSupportedDevices::Quest2;
const FString LastSupportedDeviceString = StaticEnum<EOculusXRSupportedDevices>()->GetNameStringByValue((int64)LastSupportedDevice);
if (DeviceList.Contains(Quest))
{
DeviceList.Remove(Quest);
if (!DeviceList.Contains(LastSupportedDeviceString))
{
DeviceList.Add(LastSupportedDeviceString);
}
GConfig->SetArray(OculusSettings, *SupportedDevicesKey, DeviceList, GetDefaultConfigFilename());
// Reflect the config changes in the Project Settings UI
SupportedDevices.Remove((EOculusXRSupportedDevices)0); // Enums that don't exist just have a value of 0
if (!SupportedDevices.Contains(LastSupportedDevice))
{
SupportedDevices.Add(LastSupportedDevice);
}
}
}
}
void UOculusXRHMDRuntimeSettings::MigratePixelDensityRange()
{
#if WITH_EDITOR
if (!FMath::IsNearlyZero(PixelDensityMin))
{
Algo::ForEach(UDeviceProfileManager::Get().Profiles, [&](UDeviceProfile* Profile) {
float ProfilePixelDensityMin = 0.f;
if (Profile->GetConsolidatedCVarValue(VAR_PixelDensityMin, ProfilePixelDensityMin))
{
Profile->ModifyCVarValue(VAR_PixelDensityMin, FString::SanitizeFloat(PixelDensityMin), true);
UDeviceProfileManager::Get().SaveProfiles(true);
}
});
PixelDensityMin = 0.f;
TryUpdateDefaultConfigFile();
UE_LOG(LogTemp, Log, TEXT("PixelDensityMin %f is migrated to per device range."), PixelDensityMin);
}
if (!FMath::IsNearlyZero(PixelDensityMax))
{
Algo::ForEach(UDeviceProfileManager::Get().Profiles, [&](UDeviceProfile* Profile) {
float ProfilePixelDensityMax = 0.f;
if (Profile->GetConsolidatedCVarValue(VAR_PixelDensityMax, ProfilePixelDensityMax))
{
Profile->ModifyCVarValue(VAR_PixelDensityMax, FString::SanitizeFloat(PixelDensityMax), true);
UDeviceProfileManager::Get().SaveProfiles(true);
}
});
PixelDensityMax = 0.f;
TryUpdateDefaultConfigFile();
UE_LOG(LogTemp, Log, TEXT("PixelDensityMax %f is migrated to per device range."), PixelDensityMax);
}
#endif
}

View File

@@ -0,0 +1,84 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_ConsoleCommands.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD.h"
#include "OculusXRSceneCaptureCubemap.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FConsoleCommands
//-------------------------------------------------------------------------------------------------
/// @cond DOXYGEN_WARNINGS
FConsoleCommands::FConsoleCommands(class FOculusXRHMD* InHMDPtr)
: UpdateOnRenderThreadCommand(TEXT("vr.oculus.bUpdateOnRenderThread"),
*NSLOCTEXT("OculusRift", "CCommandText_UpdateRT", "Oculus Rift specific extension.\nEnables or disables updating on the render thread.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::UpdateOnRenderThreadCommandHandler))
, HQBufferCommand(TEXT("vr.oculus.bHQBuffer"),
*NSLOCTEXT("OculusRift", "CCommandText_HQBuffer", "Oculus Rift specific extension.\nEnable or disable using floating point texture format for the eye layer.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::HQBufferCommandHandler))
, HQDistortionCommand(TEXT("vr.oculus.bHQDistortion"),
*NSLOCTEXT("OculusRift", "CCommandText_HQDistortion", "Oculus Rift specific extension.\nEnable or disable using multiple mipmap levels for the eye layer.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::HQDistortionCommandHandler))
, ShowGlobalMenuCommand(TEXT("vr.oculus.ShowGlobalMenu"),
*NSLOCTEXT("OculusRift", "CCommandText_GlobalMenu", "Oculus Rift specific extension.\nOpens the global menu.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::ShowGlobalMenuCommandHandler))
, ShowQuitMenuCommand(TEXT("vr.oculus.ShowQuitMenu"),
*NSLOCTEXT("OculusRift", "CCommandText_QuitMenu", "Oculus Rift specific extension.\nOpens the quit menu.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::ShowQuitMenuCommandHandler))
#if !UE_BUILD_SHIPPING
, StatsCommand(TEXT("vr.oculus.Debug.bShowStats"),
*NSLOCTEXT("OculusRift", "CCommandText_Stats", "Oculus Rift specific extension.\nEnable or disable rendering of stats.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::StatsCommandHandler))
, CubemapCommand(TEXT("vr.oculus.Debug.CaptureCubemap"),
*NSLOCTEXT("OculusRift", "CCommandText_Cubemap", "Oculus Rift specific extension.\nCaptures a cubemap for Oculus Home.\nOptional arguments (default is zero for all numeric arguments):\n xoff=<float> -- X axis offset from the origin\n yoff=<float> -- Y axis offset\n zoff=<float> -- Z axis offset\n yaw=<float> -- the direction to look into (roll and pitch is fixed to zero)\n mobile -- Generate a Mobile format cubemap\n (height of the captured cubemap will be 1024 instead of 2048 pixels)\n").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic(&UOculusXRSceneCaptureCubemap::CaptureCubemapCommandHandler))
, ShowSettingsCommand(TEXT("vr.oculus.Debug.Show"),
*NSLOCTEXT("OculusRift", "CCommandText_Show", "Oculus Rift specific extension.\nShows the current value of various stereo rendering params.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::ShowSettingsCommandHandler))
, IPDCommand(TEXT("vr.oculus.Debug.IPD"),
*NSLOCTEXT("OculusRift", "CCommandText_IPD", "Oculus Rift specific extension.\nShows or changes the current interpupillary distance in meters.").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateRaw(InHMDPtr, &FOculusXRHMD::IPDCommandHandler))
#endif // !UE_BUILD_SHIPPING
{
}
bool FConsoleCommands::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)
{
const TCHAR* OrigCmd = Cmd;
FString AliasedCommand;
if (FParse::Command(&Cmd, TEXT("OVRGLOBALMENU")))
{
AliasedCommand = TEXT("vr.oculus.ShowGlobalMenu");
}
else if (FParse::Command(&Cmd, TEXT("OVRQUITMENU")))
{
AliasedCommand = TEXT("vr.oculus.ShowQuitMenu");
}
#if !UE_BUILD_SHIPPING
else if (FParse::Command(&Cmd, TEXT("vr.oculus.Debug.EnforceHeadTracking")))
{
AliasedCommand = TEXT("vr.HeadTracking.bEnforced");
}
#endif // !UE_BUILD_SHIPPING
if (!AliasedCommand.IsEmpty())
{
Ar.Logf(ELogVerbosity::Warning, TEXT("%s is deprecated. Use %s instead"), OrigCmd, *AliasedCommand);
return IConsoleManager::Get().ProcessUserConsoleInput(*AliasedCommand, Ar, InWorld);
}
return false;
}
/// @endcond
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,43 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "HAL/IConsoleManager.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FConsoleCommands
//-------------------------------------------------------------------------------------------------
class FConsoleCommands : private FSelfRegisteringExec
{
public:
FConsoleCommands(class FOculusXRHMD* InHMDPtr);
// FSelfRegisteringExec interface
virtual bool Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar) override;
private:
FAutoConsoleCommand UpdateOnRenderThreadCommand;
FAutoConsoleCommand HQBufferCommand;
FAutoConsoleCommand HQDistortionCommand;
FAutoConsoleCommand ShowGlobalMenuCommand;
FAutoConsoleCommand ShowQuitMenuCommand;
#if !UE_BUILD_SHIPPING
// Debug console commands
FAutoConsoleCommand StatsCommand;
FAutoConsoleCommand CubemapCommand;
FAutoConsoleCommand ShowSettingsCommand;
FAutoConsoleCommand IPDCommand;
#endif // !UE_BUILD_SHIPPING
};
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,707 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_CustomPresent.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD.h"
#include "ScreenRendering.h"
#include "PipelineStateCache.h"
#include "ClearQuad.h"
#include "OculusShaders.h"
#include "CommonRenderResources.h"
#include "RHIStaticStates.h"
#if PLATFORM_ANDROID
#include "Android/AndroidJNI.h"
#include "Android/AndroidApplication.h"
#include "Android/AndroidPlatformMisc.h"
#endif
#define VULKAN_CUBEMAP_POSITIVE_Y 2
#define VULKAN_CUBEMAP_NEGATIVE_Y 3
namespace OculusXRHMD
{
/**
* A pixel shader for rendering a textured screen element with mip maps and array slice.
*/
class FScreenPSMipLevelArray : public FGlobalShader
{
DECLARE_SHADER_TYPE(FScreenPSMipLevelArray, Global);
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return true; }
FScreenPSMipLevelArray(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
InTexture.Bind(Initializer.ParameterMap, TEXT("InTexture"), SPF_Mandatory);
InTextureSampler.Bind(Initializer.ParameterMap, TEXT("InTextureSampler"));
InMipLevelParameter.Bind(Initializer.ParameterMap, TEXT("MipLevel"));
InArraySliceParameter.Bind(Initializer.ParameterMap, TEXT("ArraySlice"));
}
FScreenPSMipLevelArray() {}
void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, const FTexture* Texture, int ArraySlice, int MipLevel)
{
SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, Texture);
SetShaderValue(BatchedParameters, InMipLevelParameter, MipLevel);
SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice);
}
void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, FRHISamplerState* SamplerStateRHI, FRHITexture* TextureRHI, int ArraySlice, int MipLevel)
{
SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, SamplerStateRHI, TextureRHI);
SetShaderValue(BatchedParameters, InMipLevelParameter, MipLevel);
SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice);
}
private:
LAYOUT_FIELD(FShaderResourceParameter, InTexture);
LAYOUT_FIELD(FShaderResourceParameter, InTextureSampler);
LAYOUT_FIELD(FShaderParameter, InMipLevelParameter);
LAYOUT_FIELD(FShaderParameter, InArraySliceParameter);
};
IMPLEMENT_SHADER_TYPE(, FScreenPSMipLevelArray, TEXT("/Plugin/OculusXR/Private/ScreenPixelShaderArraySlice.usf"), TEXT("MainMipLevel"), SF_Pixel);
/**
* A pixel shader for rendering a textured screen element with mip maps and array slice.
*/
class FScreenPSsRGBSourceMipLevelArray : public FGlobalShader
{
DECLARE_SHADER_TYPE(FScreenPSsRGBSourceMipLevelArray, Global);
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return true; }
FScreenPSsRGBSourceMipLevelArray(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
InTexture.Bind(Initializer.ParameterMap, TEXT("InTexture"), SPF_Mandatory);
InTextureSampler.Bind(Initializer.ParameterMap, TEXT("InTextureSampler"));
InMipLevelParameter.Bind(Initializer.ParameterMap, TEXT("MipLevel"));
InArraySliceParameter.Bind(Initializer.ParameterMap, TEXT("ArraySlice"));
}
FScreenPSsRGBSourceMipLevelArray() {}
void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, const FTexture* Texture, int ArraySlice, int MipLevel)
{
SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, Texture);
SetShaderValue(BatchedParameters, InMipLevelParameter, MipLevel);
SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice);
}
void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, FRHISamplerState* SamplerStateRHI, FRHITexture* TextureRHI, int ArraySlice, int MipLevel)
{
SetTextureParameter(BatchedParameters, InTexture, InTextureSampler, SamplerStateRHI, TextureRHI);
SetShaderValue(BatchedParameters, InMipLevelParameter, MipLevel);
SetShaderValue(BatchedParameters, InArraySliceParameter, ArraySlice);
}
private:
LAYOUT_FIELD(FShaderResourceParameter, InTexture);
LAYOUT_FIELD(FShaderResourceParameter, InTextureSampler);
LAYOUT_FIELD(FShaderParameter, InMipLevelParameter);
LAYOUT_FIELD(FShaderParameter, InArraySliceParameter);
};
IMPLEMENT_SHADER_TYPE(, FScreenPSsRGBSourceMipLevelArray, TEXT("/Plugin/OculusXR/Private/ScreenPixelShaderArraySlice.usf"), TEXT("MainsRGBSourceMipLevel"), SF_Pixel);
//-------------------------------------------------------------------------------------------------
// FCustomPresent
//-------------------------------------------------------------------------------------------------
FCustomPresent::FCustomPresent(class FOculusXRHMD* InOculusXRHMD, ovrpRenderAPIType InRenderAPI, EPixelFormat InDefaultPixelFormat, bool bInSupportsSRGB)
: OculusXRHMD(InOculusXRHMD)
, RenderAPI(InRenderAPI)
, DefaultPixelFormat(InDefaultPixelFormat)
, bSupportsSRGB(bInSupportsSRGB)
, bSupportsSubsampled(false)
, bIsStandaloneStereoDevice(false)
{
CheckInGameThread();
DefaultOvrpTextureFormat = GetOvrpTextureFormat(GetDefaultPixelFormat());
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_None;
#if PLATFORM_ANDROID
bIsStandaloneStereoDevice = FAndroidMisc::GetDeviceMake() == FString("Oculus");
#endif
// grab a pointer to the renderer module for displaying our mirror window
static const FName RendererModuleName("Renderer");
RendererModule = FModuleManager::GetModulePtr<IRendererModule>(RendererModuleName);
}
void FCustomPresent::ReleaseResources_RHIThread()
{
CheckInRHIThread();
if (MirrorTextureRHI.IsValid())
{
FOculusXRHMDModule::GetPluginWrapper().DestroyMirrorTexture2();
MirrorTextureRHI = nullptr;
}
}
void FCustomPresent::Shutdown()
{
CheckInGameThread();
// OculusXRHMD is going away, but this object can live on until viewport is destroyed
ExecuteOnRenderThread([this]() {
ExecuteOnRHIThread([this]() {
OculusXRHMD = nullptr;
});
});
}
bool FCustomPresent::NeedsNativePresent()
{
return !bIsStandaloneStereoDevice;
}
bool FCustomPresent::Present(int32& SyncInterval)
{
CheckInRHIThread();
if (OculusXRHMD)
{
FGameFrame* Frame_RHIThread = OculusXRHMD->GetFrame_RHIThread();
if (Frame_RHIThread)
{
FinishRendering_RHIThread();
}
}
SyncInterval = 0; // VSync off
return NeedsNativePresent();
}
void FCustomPresent::UpdateMirrorTexture_RenderThread()
{
SCOPE_CYCLE_COUNTER(STAT_BeginRendering);
CheckInRenderThread();
const ESpectatorScreenMode MirrorWindowMode = OculusXRHMD->GetSpectatorScreenMode_RenderThread();
const FIntPoint MirrorWindowSize = OculusXRHMD->GetFrame_RenderThread()->WindowSize;
if (FOculusXRHMDModule::GetPluginWrapper().GetInitialized())
{
// Need to destroy mirror texture?
if (MirrorTextureRHI.IsValid())
{
const auto MirrorTextureSize = FIntPoint(MirrorTextureRHI->GetDesc().Extent.X, MirrorTextureRHI->GetDesc().Extent.Y);
if (MirrorWindowMode != ESpectatorScreenMode::Distorted || MirrorWindowSize != MirrorTextureSize)
{
ExecuteOnRHIThread([]() {
FOculusXRHMDModule::GetPluginWrapper().DestroyMirrorTexture2();
});
MirrorTextureRHI = nullptr;
}
}
// Need to create mirror texture?
if (!MirrorTextureRHI.IsValid() && MirrorWindowMode == ESpectatorScreenMode::Distorted && MirrorWindowSize.X != 0 && MirrorWindowSize.Y != 0)
{
const int Width = MirrorWindowSize.X;
const int Height = MirrorWindowSize.Y;
ovrpTextureHandle TextureHandle;
ExecuteOnRHIThread([&]() {
FOculusXRHMDModule::GetPluginWrapper().SetupMirrorTexture2(GetOvrpDevice(), Height, Width, GetDefaultOvrpTextureFormat(), &TextureHandle);
});
UE_LOG(LogHMD, Log, TEXT("Allocated a new mirror texture (size %d x %d)"), Width, Height);
ETextureCreateFlags TexCreateFlags = TexCreate_ShaderResource | TexCreate_RenderTargetable;
MirrorTextureRHI = CreateTexture_RenderThread(Width, Height, GetDefaultPixelFormat(), FClearValueBinding::None, 1, 1, 1, RRT_Texture2D, TextureHandle, TexCreateFlags)->GetTexture2D();
}
}
}
void FCustomPresent::FinishRendering_RHIThread()
{
SCOPE_CYCLE_COUNTER(STAT_FinishRendering);
CheckInRHIThread();
#if STATS
if (OculusXRHMD->GetFrame_RHIThread()->ShowFlags.Rendering)
{
ovrpAppLatencyTimings AppLatencyTimings;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetAppLatencyTimings2(&AppLatencyTimings)))
{
SET_FLOAT_STAT(STAT_LatencyRender, AppLatencyTimings.LatencyRender * 1000.0f);
SET_FLOAT_STAT(STAT_LatencyTimewarp, AppLatencyTimings.LatencyTimewarp * 1000.0f);
SET_FLOAT_STAT(STAT_LatencyPostPresent, AppLatencyTimings.LatencyPostPresent * 1000.0f);
SET_FLOAT_STAT(STAT_ErrorRender, AppLatencyTimings.ErrorRender * 1000.0f);
SET_FLOAT_STAT(STAT_ErrorTimewarp, AppLatencyTimings.ErrorTimewarp * 1000.0f);
}
}
#endif
OculusXRHMD->FinishRHIFrame_RHIThread();
#if PLATFORM_ANDROID
float GPUFrameTime = 0.0f;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetGPUFrameTime(&GPUFrameTime)))
{
SubmitGPUFrameTime(GPUFrameTime);
}
#endif
}
EPixelFormat FCustomPresent::GetPixelFormat(EPixelFormat Format) const
{
switch (Format)
{
// case PF_B8G8R8A8:
case PF_FloatRGBA:
case PF_FloatR11G11B10:
// case PF_R8G8B8A8:
case PF_G16:
case PF_R16F:
case PF_R32_FLOAT:
case PF_ShadowDepth:
case PF_D24:
return Format;
}
return GetDefaultPixelFormat();
}
EPixelFormat FCustomPresent::GetPixelFormat(ovrpTextureFormat Format) const
{
switch (Format)
{
// case ovrpTextureFormat_R8G8B8A8_sRGB:
// case ovrpTextureFormat_R8G8B8A8:
// return PF_R8G8B8A8;
case ovrpTextureFormat_R16G16B16A16_FP:
return PF_FloatRGBA;
case ovrpTextureFormat_R11G11B10_FP:
return PF_FloatR11G11B10;
// case ovrpTextureFormat_B8G8R8A8_sRGB:
// case ovrpTextureFormat_B8G8R8A8:
// return PF_B8G8R8A8;
case ovrpTextureFormat_R16:
return PF_G16; // G stands for grey here, not green, and is actually R16 in RHI
case ovrpTextureFormat_R16_FP:
return PF_R16F;
case ovrpTextureFormat_R32_FP:
return PF_R32_FLOAT;
case ovrpTextureFormat_D16:
return PF_ShadowDepth; // ShadowDepth maps to D16 in Vulkan
case ovrpTextureFormat_D24_S8:
return PF_D24;
}
return GetDefaultPixelFormat();
}
ovrpTextureFormat FCustomPresent::GetOvrpTextureFormat(EPixelFormat Format, bool usesRGB) const
{
switch (GetPixelFormat(Format))
{
case PF_B8G8R8A8:
return bSupportsSRGB && usesRGB ? ovrpTextureFormat_B8G8R8A8_sRGB : ovrpTextureFormat_B8G8R8A8;
case PF_FloatRGBA:
return ovrpTextureFormat_R16G16B16A16_FP;
case PF_FloatR11G11B10:
return ovrpTextureFormat_R11G11B10_FP;
case PF_R8G8B8A8:
return bSupportsSRGB && usesRGB ? ovrpTextureFormat_R8G8B8A8_sRGB : ovrpTextureFormat_R8G8B8A8;
case PF_G16:
return ovrpTextureFormat_R16;
case PF_R16F:
return ovrpTextureFormat_R16_FP;
case PF_R32_FLOAT:
return ovrpTextureFormat_R32_FP;
case PF_ShadowDepth:
return ovrpTextureFormat_D16;
case PF_D24:
return ovrpTextureFormat_D24_S8;
}
return ovrpTextureFormat_None;
}
bool FCustomPresent::IsSRGB(ovrpTextureFormat InFormat)
{
switch (InFormat)
{
case ovrpTextureFormat_B8G8R8A8_sRGB:
case ovrpTextureFormat_R8G8B8A8_sRGB:
return true;
}
return false;
}
int FCustomPresent::GetSystemRecommendedMSAALevel() const
{
int SystemRecommendedMSAALevel = 1;
FOculusXRHMDModule::GetPluginWrapper().GetSystemRecommendedMSAALevel2(&SystemRecommendedMSAALevel);
return SystemRecommendedMSAALevel;
}
FXRSwapChainPtr FCustomPresent::CreateSwapChain_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, const TArray<ovrpTextureHandle>& InTextures, ETextureCreateFlags InTexCreateFlags, const TCHAR* DebugName)
{
TArray<FTextureRHIRef> RHITextureSwapChain = CreateSwapChainTextures_RenderThread(InSizeX, InSizeY, InFormat, InBinding, InNumMips, InNumSamples, InNumSamplesTileMem, InResourceType, InTextures, InTexCreateFlags, DebugName);
FTextureRHIRef RHITexture = GDynamicRHI->RHICreateAliasedTexture(RHITextureSwapChain[0]);
return CreateXRSwapChain(MoveTemp(RHITextureSwapChain), RHITexture);
}
TArray<FTextureRHIRef> FCustomPresent::CreateSwapChainTextures_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, const TArray<ovrpTextureHandle>& InTextures, ETextureCreateFlags InTexCreateFlags, const TCHAR* DebugName)
{
CheckInRenderThread();
TArray<FTextureRHIRef> RHITextureSwapChain;
{
for (int32 TextureIndex = 0; TextureIndex < InTextures.Num(); ++TextureIndex)
{
FTextureRHIRef TexRef = CreateTexture_RenderThread(InSizeX, InSizeY, InFormat, InBinding, InNumMips, InNumSamples, InNumSamplesTileMem, InResourceType, InTextures[TextureIndex], InTexCreateFlags);
FString TexName = FString::Printf(TEXT("%s (%d/%d)"), DebugName, TextureIndex, InTextures.Num());
TexRef->SetName(*TexName);
RHIBindDebugLabelName(TexRef, *TexName);
RHITextureSwapChain.Add(TexRef);
}
}
return RHITextureSwapChain;
}
void FCustomPresent::CopyTexture_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture* DstTexture, FRHITexture* SrcTexture, FIntRect DstRect, FIntRect SrcRect, bool bAlphaPremultiply, bool bNoAlphaWrite, bool bInvertY, bool sRGBSource, bool bInvertAlpha)
{
FCustomPresent::CopyTexture_RenderThread(RHICmdList, RendererModule, DstTexture, SrcTexture, OculusXRHMD->GetSettings_RenderThread()->CurrentFeatureLevel, RenderAPI == ovrpRenderAPI_Vulkan,
DstRect, SrcRect, bAlphaPremultiply, bNoAlphaWrite, bInvertY, sRGBSource, bInvertAlpha);
}
void FCustomPresent::CopyTexture_RenderThread(FRHICommandListImmediate& RHICmdList, IRendererModule* RendererModule, FRHITexture* DstTexture, FRHITexture* SrcTexture, FStaticFeatureLevel FeatureLevel, bool bUsingVulkan,
FIntRect DstRect, FIntRect SrcRect, bool bAlphaPremultiply, bool bNoAlphaWrite, bool bInvertY, bool sRGBSource, bool bInvertAlpha)
{
CheckInRenderThread();
FIntPoint DstSize;
FIntPoint SrcSize;
if (DstTexture->GetDesc().IsTexture2D() && SrcTexture->GetDesc().IsTexture2D())
{
DstSize = FIntPoint(DstTexture->GetSizeX(), DstTexture->GetSizeY());
SrcSize = FIntPoint(SrcTexture->GetSizeX(), SrcTexture->GetSizeY());
}
else if (DstTexture->GetDesc().IsTextureCube() && SrcTexture->GetDesc().IsTextureCube())
{
DstSize = FIntPoint(DstTexture->GetSize(), DstTexture->GetSize());
SrcSize = FIntPoint(SrcTexture->GetSize(), SrcTexture->GetSize());
}
else
{
return;
}
if (DstRect.IsEmpty())
{
DstRect = FIntRect(FIntPoint::ZeroValue, DstSize);
}
if (SrcRect.IsEmpty())
{
SrcRect = FIntRect(FIntPoint::ZeroValue, SrcSize);
}
const uint32 ViewportWidth = DstRect.Width();
const uint32 ViewportHeight = DstRect.Height();
const FIntPoint TargetSize(ViewportWidth, ViewportHeight);
float U = SrcRect.Min.X / (float)SrcSize.X;
float V = SrcRect.Min.Y / (float)SrcSize.Y;
float USize = SrcRect.Width() / (float)SrcSize.X;
float VSize = SrcRect.Height() / (float)SrcSize.Y;
#if PLATFORM_ANDROID // on android, top-left isn't 0/0 but 1/0.
if (bInvertY)
{
V = 1.0f - V;
VSize = -VSize;
}
#endif
FRHITexture* SrcTextureRHI = SrcTexture;
RHICmdList.Transition(FRHITransitionInfo(SrcTextureRHI, ERHIAccess::Unknown, ERHIAccess::SRVGraphics));
FGraphicsPipelineStateInitializer GraphicsPSOInit;
if (bInvertAlpha)
{
// write RGBA, RGB = src.rgb * 1 + dst.rgb * 0, A = src.a * 0 + dst.a * (1 - src.a)
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_Zero, BO_Add, BF_Zero, BF_InverseSourceAlpha>::GetRHI();
}
else if (bAlphaPremultiply)
{
if (bNoAlphaWrite)
{
// for quads, write RGB, RGB = src.rgb * 1 + dst.rgb * 0
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_Zero, BO_Add, BF_One, BF_Zero>::GetRHI();
}
else
{
// for quads, write RGBA, RGB = src.rgb * src.a + dst.rgb * 0, A = src.a + dst.a * 0
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_SourceAlpha, BF_Zero, BO_Add, BF_One, BF_Zero>::GetRHI();
}
}
else
{
if (bNoAlphaWrite)
{
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB>::GetRHI();
}
else
{
// for mirror window, write RGBA, RGB = src.rgb * src.a + dst.rgb * (1 - src.a), A = src.a * 1 + dst.a * (1 - src a)
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_SourceAlpha, BF_InverseSourceAlpha, BO_Add, BF_One, BF_InverseSourceAlpha>::GetRHI();
}
}
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
auto ShaderMap = GetGlobalShaderMap(FeatureLevel);
TShaderMapRef<FScreenVS> VertexShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
if (DstTexture->GetDesc().IsTexture2D())
{
sRGBSource &= EnumHasAnyFlags(SrcTexture->GetFlags(), TexCreate_SRGB);
// Need to copy over mip maps on Android since they are not generated like they are on PC
#if PLATFORM_ANDROID
uint32 NumMips = SrcTexture->GetNumMips();
#else
uint32 NumMips = 1;
#endif
const bool bUseTexArrayShader = SrcTexture->GetDesc().IsTextureArray() && DstTexture->GetDesc().IsTextureArray();
const int32 SliceCount = bUseTexArrayShader ? FMath::Min(SrcTexture->GetDesc().ArraySize, DstTexture->GetDesc().ArraySize) : 1;
for (uint32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
{
FRHIRenderPassInfo RPInfo(DstTexture, ERenderTargetActions::Load_Store);
RPInfo.ColorRenderTargets[0].MipIndex = MipIndex;
for (int32 SliceIndex = 0; SliceIndex < SliceCount; ++SliceIndex)
{
RPInfo.ColorRenderTargets[0].ArraySlice = SliceIndex;
RHICmdList.BeginRenderPass(RPInfo, TEXT("CopyTexture"));
{
const uint32 MipViewportWidth = ViewportWidth >> MipIndex;
const uint32 MipViewportHeight = ViewportHeight >> MipIndex;
const FIntPoint MipTargetSize(MipViewportWidth, MipViewportHeight);
if (bNoAlphaWrite || bInvertAlpha)
{
RHICmdList.SetViewport(DstRect.Min.X, DstRect.Min.Y, 0.0f, DstRect.Max.X, DstRect.Max.Y, 1.0f);
DrawClearQuad(RHICmdList, bAlphaPremultiply ? FLinearColor::Black : FLinearColor::White);
}
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
FRHISamplerState* SamplerState = DstRect.Size() == SrcRect.Size() ? TStaticSamplerState<SF_Point>::GetRHI() : TStaticSamplerState<SF_Bilinear>::GetRHI();
if (!sRGBSource)
{
if (bUseTexArrayShader)
{
TShaderMapRef<FScreenPSMipLevelArray> PixelShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, SliceIndex, MipIndex);
RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters);
}
else
{
TShaderMapRef<FScreenPSMipLevel> PixelShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
#if UE_VERSION_OLDER_THAN(5, 3, 0)
PixelShader->SetParameters(RHICmdList, SamplerState, SrcTextureRHI, MipIndex);
#else
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, MipIndex);
RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters);
#endif
}
}
else
{
if (bUseTexArrayShader)
{
TShaderMapRef<FScreenPSsRGBSourceMipLevelArray> PixelShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, SliceIndex, MipIndex);
RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters);
}
else
{
TShaderMapRef<FScreenPSsRGBSourceMipLevel> PixelShader(ShaderMap);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
#if UE_VERSION_OLDER_THAN(5, 3, 0)
PixelShader->SetParameters(RHICmdList, SamplerState, SrcTextureRHI, MipIndex);
#else
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, MipIndex);
RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters);
#endif
}
}
RHICmdList.SetViewport(DstRect.Min.X, DstRect.Min.Y, 0.0f, DstRect.Min.X + MipViewportWidth, DstRect.Min.Y + MipViewportHeight, 1.0f);
RendererModule->DrawRectangle(
RHICmdList,
0, 0, MipViewportWidth, MipViewportHeight,
U, V, USize, VSize,
MipTargetSize,
FIntPoint(1, 1),
VertexShader,
EDRF_Default);
}
RHICmdList.EndRenderPass();
}
}
}
else
{
for (int FaceIndex = 0; FaceIndex < 6; FaceIndex++)
{
FRHIRenderPassInfo RPInfo(DstTexture, ERenderTargetActions::Load_Store);
#if defined(WITH_OCULUS_BRANCH)
static const FMatrix44f FaceToSampleMatrix[6] = {
FMatrix44f(
FVector3f(0, 0, 1),
FVector3f(0, 1, 0),
FVector3f(1, 0, 0),
FVector3f(0, 0, 0)),
FMatrix44f(
FVector3f(0, 0, -1),
FVector3f(0, 1, 0),
FVector3f(-1, 0, 0),
FVector3f(0, 0, 0)),
FMatrix44f(
FVector3f(1, 0, 0),
FVector3f(0, 0, 1),
FVector3f(0, 1, 0),
FVector3f(0, 0, 0)),
FMatrix44f(
FVector3f(1, 0, 0),
FVector3f(0, 0, -1),
FVector3f(0, -1, 0),
FVector3f(0, 0, 0)),
FMatrix44f(
FVector3f(1, 0, 0),
FVector3f(0, 1, 0),
FVector3f(0, 0, -1),
FVector3f(0, 0, 0)),
FMatrix44f(
FVector3f(-1, 0, 0),
FVector3f(0, 1, 0),
FVector3f(0, 0, 1),
FVector3f(0, 0, 0)),
};
#endif
// On Vulkan the positive and negative Y faces of the cubemap need to be flipped
if (bUsingVulkan)
{
int NewFaceIndex = 0;
if (FaceIndex == VULKAN_CUBEMAP_POSITIVE_Y)
NewFaceIndex = VULKAN_CUBEMAP_NEGATIVE_Y;
else if (FaceIndex == VULKAN_CUBEMAP_NEGATIVE_Y)
NewFaceIndex = VULKAN_CUBEMAP_POSITIVE_Y;
else
NewFaceIndex = FaceIndex;
RPInfo.ColorRenderTargets[0].ArraySlice = NewFaceIndex;
}
else
{
RPInfo.ColorRenderTargets[0].ArraySlice = FaceIndex;
}
RHICmdList.BeginRenderPass(RPInfo, TEXT("CopyTextureFace"));
{
if (bNoAlphaWrite)
{
DrawClearQuad(RHICmdList, bAlphaPremultiply ? FLinearColor::Black : FLinearColor::White);
}
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
#if defined(WITH_OCULUS_BRANCH)
TShaderMapRef<FOculusOpenXRCubemapPS> PixelShader(ShaderMap);
#else
TShaderMapRef<FOculusCubemapPS> PixelShader(ShaderMap);
#endif
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
FRHISamplerState* SamplerState = DstRect.Size() == SrcRect.Size() ? TStaticSamplerState<SF_Point>::GetRHI() : TStaticSamplerState<SF_Bilinear>::GetRHI();
#if UE_VERSION_OLDER_THAN(5, 3, 0)
PixelShader->SetParameters(RHICmdList, SamplerState, SrcTextureRHI, FaceIndex);
#else
FRHIBatchedShaderParameters& BatchedParameters = RHICmdList.GetScratchShaderParameters();
#if defined(WITH_OCULUS_BRANCH)
check(FaceIndex < UE_ARRAY_COUNT(FaceToSampleMatrix));
PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, FaceToSampleMatrix[FaceIndex]);
#else
PixelShader->SetParameters(BatchedParameters, SamplerState, SrcTextureRHI, FaceIndex);
#endif
RHICmdList.SetBatchedShaderParameters(RHICmdList.GetBoundPixelShader(), BatchedParameters);
#endif
RHICmdList.SetViewport(DstRect.Min.X, DstRect.Min.Y, 0.0f, DstRect.Max.X, DstRect.Max.Y, 1.0f);
RendererModule->DrawRectangle(
RHICmdList,
0, 0, ViewportWidth, ViewportHeight,
#if PLATFORM_ANDROID
U, V, USize, VSize,
#else
U, 1.0 - V, USize, -VSize,
#endif
TargetSize,
FIntPoint(1, 1),
VertexShader,
EDRF_Default);
}
RHICmdList.EndRenderPass();
}
}
}
void FCustomPresent::SubmitGPUCommands_RenderThread(FRHICommandListImmediate& RHICmdList)
{
CheckInRenderThread();
RHICmdList.ImmediateFlush(EImmediateFlushType::DispatchToRHIThread);
}
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,120 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_Settings.h"
#include "OculusXRHMD_GameFrame.h"
#include "XRSwapChain.h"
#include "RHI.h"
#include "RendererInterface.h"
#include "IStereoLayers.h"
#include "XRRenderBridge.h"
#if PLATFORM_WINDOWS
#include "Windows/WindowsHWrapper.h"
#endif
DECLARE_STATS_GROUP(TEXT("OculusXRHMD"), STATGROUP_OculusXRHMD, STATCAT_Advanced);
DECLARE_CYCLE_STAT(TEXT("BeginRendering"), STAT_BeginRendering, STATGROUP_OculusXRHMD);
DECLARE_CYCLE_STAT(TEXT("FinishRendering"), STAT_FinishRendering, STATGROUP_OculusXRHMD);
DECLARE_FLOAT_COUNTER_STAT(TEXT("LatencyRender"), STAT_LatencyRender, STATGROUP_OculusXRHMD);
DECLARE_FLOAT_COUNTER_STAT(TEXT("LatencyTimewarp"), STAT_LatencyTimewarp, STATGROUP_OculusXRHMD);
DECLARE_FLOAT_COUNTER_STAT(TEXT("LatencyPostPresent"), STAT_LatencyPostPresent, STATGROUP_OculusXRHMD);
DECLARE_FLOAT_COUNTER_STAT(TEXT("ErrorRender"), STAT_ErrorRender, STATGROUP_OculusXRHMD);
DECLARE_FLOAT_COUNTER_STAT(TEXT("ErrorTimewarp"), STAT_ErrorTimewarp, STATGROUP_OculusXRHMD);
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FCustomPresent
//-------------------------------------------------------------------------------------------------
class FCustomPresent : public FXRRenderBridge
{
public:
FCustomPresent(class FOculusXRHMD* InOculusXRHMD, ovrpRenderAPIType InRenderAPI, EPixelFormat InDefaultPixelFormat, bool InSupportsSRGB);
// FXRRenderBridge/FRHICustomPresent
virtual bool NeedsNativePresent() override;
virtual bool Present(int32& SyncInterval) override;
virtual void FinishRendering_RHIThread();
ovrpRenderAPIType GetRenderAPI() const { return RenderAPI; }
virtual bool IsUsingCorrectDisplayAdapter() const { return true; }
void UpdateMirrorTexture_RenderThread();
void ReleaseResources_RHIThread();
void Shutdown();
FTextureRHIRef GetMirrorTexture() { return MirrorTextureRHI; }
virtual void* GetOvrpInstance() const { return nullptr; }
virtual void* GetOvrpPhysicalDevice() const { return nullptr; }
virtual void* GetOvrpDevice() const { return nullptr; }
virtual void* GetOvrpCommandQueue() const { return nullptr; }
EPixelFormat GetPixelFormat(EPixelFormat InFormat) const;
EPixelFormat GetPixelFormat(ovrpTextureFormat InFormat) const;
EPixelFormat GetDefaultPixelFormat() const { return DefaultPixelFormat; }
ovrpTextureFormat GetOvrpTextureFormat(EPixelFormat InFormat, bool usesRGB = true) const;
ovrpTextureFormat GetDefaultOvrpTextureFormat() const { return DefaultOvrpTextureFormat; }
ovrpTextureFormat GetDefaultDepthOvrpTextureFormat() const { return DefaultDepthOvrpTextureFormat; }
static bool IsSRGB(ovrpTextureFormat InFormat);
virtual int GetSystemRecommendedMSAALevel() const;
virtual int GetLayerFlags() const { return 0; }
virtual FTextureRHIRef CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags TexCreateFlags) = 0;
FXRSwapChainPtr CreateSwapChain_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, const TArray<ovrpTextureHandle>& InTextures, ETextureCreateFlags InTexCreateFlags, const TCHAR* DebugName);
TArray<FTextureRHIRef> CreateSwapChainTextures_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, const TArray<ovrpTextureHandle>& InTextures, ETextureCreateFlags InTexCreateFlags, const TCHAR* DebugName);
void CopyTexture_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture* DstTexture, FRHITexture* SrcTexture, FIntRect DstRect = FIntRect(), FIntRect SrcRect = FIntRect(), bool bAlphaPremultiply = false, bool bNoAlphaWrite = false, bool bInvertY = true, bool sRGBSource = false, bool bInvertAlpha = false);
OCULUSXRHMD_API static void CopyTexture_RenderThread(FRHICommandListImmediate& RHICmdList, IRendererModule* RendererModule, FRHITexture* DstTexture, FRHITexture* SrcTexture, FStaticFeatureLevel FeatureLevel, bool bUsingVulkan, FIntRect DstRect = FIntRect(), FIntRect SrcRect = FIntRect(), bool bAlphaPremultiply = false, bool bNoAlphaWrite = false, bool bInvertY = true, bool sRGBSource = false, bool bInvertAlpha = false);
void SubmitGPUCommands_RenderThread(FRHICommandListImmediate& RHICmdList);
virtual void SubmitGPUFrameTime(float GPUFrameTime) {}
// This is a hack to turn force FSR off when we allocate our FDM to avoid a crash on Quest 3
// TODO: Remove this for UE 5.3 after there's an engine-side fix
virtual void UseFragmentDensityMapOverShadingRate_RHIThread() {};
#ifdef WITH_OCULUS_BRANCH
virtual void UpdateFoveationOffsets_RHIThread(bool bUseOffsets, FIntPoint Offsets[2]) {};
#endif // WITH_OCULUS_BRANCH
bool SupportsSRGB()
{
return bSupportsSRGB;
}
bool SupportsSubsampled() { return bSupportsSubsampled; }
protected:
FOculusXRHMD* OculusXRHMD;
ovrpRenderAPIType RenderAPI;
EPixelFormat DefaultPixelFormat;
bool bSupportsSRGB;
bool bSupportsSubsampled;
ovrpTextureFormat DefaultOvrpTextureFormat;
ovrpTextureFormat DefaultDepthOvrpTextureFormat;
IRendererModule* RendererModule;
FTextureRHIRef MirrorTextureRHI;
bool bIsStandaloneStereoDevice;
};
//-------------------------------------------------------------------------------------------------
// APIs
//-------------------------------------------------------------------------------------------------
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11
FCustomPresent* CreateCustomPresent_D3D11(FOculusXRHMD* InOculusXRHMD);
#endif
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
FCustomPresent* CreateCustomPresent_D3D12(FOculusXRHMD* InOculusXRHMD);
#endif
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN
FCustomPresent* CreateCustomPresent_Vulkan(FOculusXRHMD* InOculusXRHMD);
#endif
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,118 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_CustomPresent.h"
#include "OculusXRHMDPrivateRHI.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11
#include "OculusXRHMD.h"
#ifndef WINDOWS_PLATFORM_TYPES_GUARD
#include "Windows/AllowWindowsPlatformTypes.h"
#endif
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FD3D11CustomPresent
//-------------------------------------------------------------------------------------------------
class FD3D11CustomPresent : public FCustomPresent
{
public:
FD3D11CustomPresent(FOculusXRHMD* InOculusXRHMD);
// Implementation of FCustomPresent, called by Plugin itself
virtual bool IsUsingCorrectDisplayAdapter() const override;
virtual void* GetOvrpDevice() const override;
virtual FTextureRHIRef CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags) override;
};
FD3D11CustomPresent::FD3D11CustomPresent(FOculusXRHMD* InOculusXRHMD)
: FCustomPresent(InOculusXRHMD, ovrpRenderAPI_D3D11, PF_B8G8R8A8, true)
{
switch (GPixelFormats[PF_DepthStencil].PlatformFormat)
{
case DXGI_FORMAT_R24G8_TYPELESS:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D24_S8;
break;
case DXGI_FORMAT_R32G8X24_TYPELESS:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D32_FP_S8;
break;
default:
UE_LOG(LogHMD, Error, TEXT("Unrecognized depth buffer format"));
break;
}
}
bool FD3D11CustomPresent::IsUsingCorrectDisplayAdapter() const
{
const void* luid;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetDisplayAdapterId2(&luid)) && luid)
{
TRefCountPtr<ID3D11Device> D3D11Device;
ExecuteOnRenderThread([&D3D11Device]() {
D3D11Device = (ID3D11Device*)RHIGetNativeDevice();
});
if (D3D11Device)
{
TRefCountPtr<IDXGIDevice> DXGIDevice;
TRefCountPtr<IDXGIAdapter> DXGIAdapter;
DXGI_ADAPTER_DESC DXGIAdapterDesc;
if (SUCCEEDED(D3D11Device->QueryInterface(__uuidof(IDXGIDevice), (void**)DXGIDevice.GetInitReference())) && SUCCEEDED(DXGIDevice->GetAdapter(DXGIAdapter.GetInitReference())) && SUCCEEDED(DXGIAdapter->GetDesc(&DXGIAdapterDesc)))
{
return !FMemory::Memcmp(luid, &DXGIAdapterDesc.AdapterLuid, sizeof(LUID));
}
}
}
// Not enough information. Assume that we are using the correct adapter.
return true;
}
void* FD3D11CustomPresent::GetOvrpDevice() const
{
return GetID3D11DynamicRHI()->RHIGetDevice();
}
FTextureRHIRef FD3D11CustomPresent::CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags)
{
CheckInRenderThread();
switch (InResourceType)
{
case RRT_Texture2D:
return GetID3D11DynamicRHI()->RHICreateTexture2DFromResource(InFormat, InTexCreateFlags, InBinding, (ID3D11Texture2D*)InTexture).GetReference();
case RRT_Texture2DArray:
return GetID3D11DynamicRHI()->RHICreateTexture2DArrayFromResource(InFormat, InTexCreateFlags, InBinding, (ID3D11Texture2D*)InTexture).GetReference();
case RRT_TextureCube:
return GetID3D11DynamicRHI()->RHICreateTextureCubeFromResource(InFormat, InTexCreateFlags | TexCreate_TargetArraySlicesIndependently, InBinding, (ID3D11Texture2D*)InTexture).GetReference();
default:
return nullptr;
}
}
//-------------------------------------------------------------------------------------------------
// APIs
//-------------------------------------------------------------------------------------------------
FCustomPresent* CreateCustomPresent_D3D11(FOculusXRHMD* InOculusXRHMD)
{
return new FD3D11CustomPresent(InOculusXRHMD);
}
} // namespace OculusXRHMD
#if PLATFORM_WINDOWS
#undef WINDOWS_PLATFORM_TYPES_GUARD
#endif
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_D3D11

View File

@@ -0,0 +1,114 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_CustomPresent.h"
#include "OculusXRHMDPrivateRHI.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12
#include "OculusXRHMD.h"
#ifndef WINDOWS_PLATFORM_TYPES_GUARD
#include "Windows/AllowWindowsPlatformTypes.h"
#endif
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FCustomPresentD3D12
//-------------------------------------------------------------------------------------------------
class FD3D12CustomPresent : public FCustomPresent
{
public:
FD3D12CustomPresent(FOculusXRHMD* InOculusXRHMD);
// Implementation of FCustomPresent, called by Plugin itself
virtual bool IsUsingCorrectDisplayAdapter() const override;
virtual void* GetOvrpDevice() const override;
virtual FTextureRHIRef CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags) override;
};
FD3D12CustomPresent::FD3D12CustomPresent(FOculusXRHMD* InOculusXRHMD)
: FCustomPresent(InOculusXRHMD, ovrpRenderAPI_D3D12, PF_B8G8R8A8, true)
{
switch (GPixelFormats[PF_DepthStencil].PlatformFormat)
{
case DXGI_FORMAT_R24G8_TYPELESS:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D24_S8;
break;
case DXGI_FORMAT_R32G8X24_TYPELESS:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D32_FP_S8;
break;
default:
UE_LOG(LogHMD, Error, TEXT("Unrecognized depth buffer format"));
break;
}
}
bool FD3D12CustomPresent::IsUsingCorrectDisplayAdapter() const
{
const void* luid;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetDisplayAdapterId2(&luid)) && luid)
{
TRefCountPtr<ID3D12Device> D3DDevice;
ExecuteOnRenderThread([&D3DDevice]() {
D3DDevice = (ID3D12Device*)RHIGetNativeDevice();
});
if (D3DDevice)
{
LUID AdapterLuid = D3DDevice->GetAdapterLuid();
return !FMemory::Memcmp(luid, &AdapterLuid, sizeof(LUID));
}
}
// Not enough information. Assume that we are using the correct adapter.
return true;
}
void* FD3D12CustomPresent::GetOvrpDevice() const
{
return GetID3D12DynamicRHI()->RHIGetCommandQueue();
}
FTextureRHIRef FD3D12CustomPresent::CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags)
{
CheckInRenderThread();
ID3D12DynamicRHI* DynamicRHI = GetID3D12DynamicRHI();
switch (InResourceType)
{
case RRT_Texture2D:
return DynamicRHI->RHICreateTexture2DFromResource(InFormat, InTexCreateFlags, InBinding, (ID3D12Resource*)InTexture).GetReference();
case RRT_Texture2DArray:
return DynamicRHI->RHICreateTexture2DArrayFromResource(InFormat, InTexCreateFlags, InBinding, (ID3D12Resource*)InTexture).GetReference();
case RRT_TextureCube:
return DynamicRHI->RHICreateTextureCubeFromResource(InFormat, InTexCreateFlags | TexCreate_TargetArraySlicesIndependently, InBinding, (ID3D12Resource*)InTexture).GetReference();
default:
return nullptr;
}
}
//-------------------------------------------------------------------------------------------------
// APIs
//-------------------------------------------------------------------------------------------------
FCustomPresent* CreateCustomPresent_D3D12(FOculusXRHMD* InOculusXRHMD)
{
return new FD3D12CustomPresent(InOculusXRHMD);
}
} // namespace OculusXRHMD
#if PLATFORM_WINDOWS
#undef WINDOWS_PLATFORM_TYPES_GUARD
#endif
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_D3D12

View File

@@ -0,0 +1,178 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_CustomPresent.h"
#include "OculusXRHMDPrivateRHI.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN
#include "OculusXRHMD.h"
#include "IVulkanDynamicRHI.h"
#include "Async/Async.h"
#if PLATFORM_WINDOWS
#ifndef WINDOWS_PLATFORM_TYPES_GUARD
#include "Windows/AllowWindowsPlatformTypes.h"
#endif
#endif
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FCustomPresentVulkan
//-------------------------------------------------------------------------------------------------
class FVulkanCustomPresent : public FCustomPresent
{
public:
FVulkanCustomPresent(FOculusXRHMD* InOculusXRHMD);
// Implementation of FCustomPresent, called by Plugin itself
virtual bool IsUsingCorrectDisplayAdapter() const override;
virtual void* GetOvrpInstance() const override;
virtual void* GetOvrpPhysicalDevice() const override;
virtual void* GetOvrpDevice() const override;
virtual void* GetOvrpCommandQueue() const override;
virtual FTextureRHIRef CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags) override;
// This is a hack to turn force FSR off when we allocate our FDM to avoid a crash on Quest 3
// TODO: Remove this for UE 5.3 after there's an engine-side fix
virtual void UseFragmentDensityMapOverShadingRate_RHIThread() override;
#ifdef WITH_OCULUS_BRANCH
virtual void UpdateFoveationOffsets_RHIThread(bool bUseTileOffsets, FIntPoint TileOffsets[2]) override;
#endif // WITH_OCULUS_BRANCH
};
FVulkanCustomPresent::FVulkanCustomPresent(FOculusXRHMD* InOculusXRHMD)
: FCustomPresent(InOculusXRHMD, ovrpRenderAPI_Vulkan, PF_R8G8B8A8, true)
{
#if PLATFORM_ANDROID
AsyncTask(ENamedThreads::GameThread, []() {
#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
switch (GPixelFormats[PF_DepthStencil].PlatformFormat)
{
case VK_FORMAT_D24_UNORM_S8_UINT:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D24_S8;
break;
case VK_FORMAT_D32_SFLOAT_S8_UINT:
DefaultDepthOvrpTextureFormat = ovrpTextureFormat_D32_FP_S8;
break;
default:
UE_LOG(LogHMD, Error, TEXT("Unrecognized depth buffer format"));
break;
}
bSupportsSubsampled = GetIVulkanDynamicRHI()->RHISupportsEXTFragmentDensityMap2();
}
bool FVulkanCustomPresent::IsUsingCorrectDisplayAdapter() const
{
#if PLATFORM_WINDOWS
const void* AdapterId = nullptr;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetDisplayAdapterId2(&AdapterId)) && AdapterId)
{
return GetIVulkanDynamicRHI()->RHIDoesAdapterMatchDevice(AdapterId);
}
#endif
// Not enough information. Assume that we are using the correct adapter.
return true;
}
void* FVulkanCustomPresent::GetOvrpInstance() const
{
return GetIVulkanDynamicRHI()->RHIGetVkInstance();
}
void* FVulkanCustomPresent::GetOvrpPhysicalDevice() const
{
return GetIVulkanDynamicRHI()->RHIGetVkPhysicalDevice();
}
void* FVulkanCustomPresent::GetOvrpDevice() const
{
return GetIVulkanDynamicRHI()->RHIGetVkDevice();
}
void* FVulkanCustomPresent::GetOvrpCommandQueue() const
{
return GetIVulkanDynamicRHI()->RHIGetGraphicsVkQueue();
}
FTextureRHIRef FVulkanCustomPresent::CreateTexture_RenderThread(uint32 InSizeX, uint32 InSizeY, EPixelFormat InFormat, FClearValueBinding InBinding, uint32 InNumMips, uint32 InNumSamples, uint32 InNumSamplesTileMem, ERHIResourceType InResourceType, ovrpTextureHandle InTexture, ETextureCreateFlags InTexCreateFlags)
{
CheckInRenderThread();
IVulkanDynamicRHI* VulkanRHI = GetIVulkanDynamicRHI();
const VkImageSubresourceRange SubresourceRangeAll = { VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS };
if (EnumHasAnyFlags(InTexCreateFlags, TexCreate_RenderTargetable))
{
VulkanRHI->RHISetImageLayout((VkImage)InTexture, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, SubresourceRangeAll);
}
else if (EnumHasAnyFlags(InTexCreateFlags, TexCreate_Foveation))
{
VulkanRHI->RHISetImageLayout((VkImage)InTexture, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT, SubresourceRangeAll);
}
switch (InResourceType)
{
case RRT_Texture2D:
return VulkanRHI->RHICreateTexture2DFromResource(InFormat, InSizeX, InSizeY, InNumMips, InNumSamples, (VkImage)InTexture, InTexCreateFlags, InBinding).GetReference();
case RRT_Texture2DArray:
return VulkanRHI->RHICreateTexture2DArrayFromResource(InFormat, InSizeX, InSizeY, 2, InNumMips, InNumSamples, (VkImage)InTexture, InTexCreateFlags, InBinding).GetReference();
case RRT_TextureCube:
return VulkanRHI->RHICreateTextureCubeFromResource(InFormat, InSizeX, false, 1, InNumMips, (VkImage)InTexture, InTexCreateFlags, InBinding).GetReference();
default:
return nullptr;
}
}
// This is a hack to turn force FSR off when we allocate our FDM to avoid a crash on Quest 3
// TODO: Remove this for UE 5.3 after there's an engine-side fix
void FVulkanCustomPresent::UseFragmentDensityMapOverShadingRate_RHIThread()
{
CheckInRHIThread();
SCOPED_NAMED_EVENT(UseFragmentDensityMapOverShadingRate_RHIThread, FColor::Red);
GRHIVariableRateShadingImageDataType = VRSImage_Fractional;
GRHIVariableRateShadingImageFormat = PF_R8G8;
}
#ifdef WITH_OCULUS_BRANCH
void FVulkanCustomPresent::UpdateFoveationOffsets_RHIThread(bool bUseOffsets, FIntPoint Offsets[2])
{
CheckInRHIThread();
SCOPED_NAMED_EVENT(UpdateFoveationOffsets_RHIThread, FColor::Red);
GetIVulkanDynamicRHI()->RHISetQcomFragmentDensityMapOffsets(bUseOffsets, Offsets);
}
#endif // WITH_OCULUS_BRANCH
//-------------------------------------------------------------------------------------------------
// APIs
//-------------------------------------------------------------------------------------------------
FCustomPresent* CreateCustomPresent_Vulkan(FOculusXRHMD* InOculusXRHMD)
{
return new FVulkanCustomPresent(InOculusXRHMD);
}
} // namespace OculusXRHMD
#if PLATFORM_WINDOWS
#undef WINDOWS_PLATFORM_TYPES_GUARD
#endif
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN

View File

@@ -0,0 +1,80 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_DeferredDeletionQueue.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMDPrivate.h"
#include "XRThreadUtils.h"
#include "OculusXRHMDModule.h"
#include "Misc/EngineVersionComparison.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FDeferredDeletionQueue
//-------------------------------------------------------------------------------------------------
uint32 GOculusXRHMDLayerDeletionFrameNumber = 0;
const uint32 NUM_FRAMES_TO_WAIT_FOR_LAYER_DELETE = 3;
const uint32 NUM_FRAMES_TO_WAIT_FOR_OVRP_LAYER_DELETE = 7;
void FDeferredDeletionQueue::AddLayerToDeferredDeletionQueue(const FLayerPtr& ptr)
{
DeferredDeletionEntry Entry;
Entry.Layer = ptr;
Entry.FrameEnqueued = GOculusXRHMDLayerDeletionFrameNumber;
Entry.EntryType = DeferredDeletionEntry::DeferredDeletionEntryType::Layer;
DeferredDeletionArray.Add(Entry);
}
void FDeferredDeletionQueue::AddOVRPLayerToDeferredDeletionQueue(const uint32 layerID)
{
DeferredDeletionEntry Entry;
Entry.OvrpLayerId = layerID;
Entry.FrameEnqueued = GOculusXRHMDLayerDeletionFrameNumber;
Entry.EntryType = DeferredDeletionEntry::DeferredDeletionEntryType::OvrpLayer;
DeferredDeletionArray.Add(Entry);
}
void FDeferredDeletionQueue::HandleLayerDeferredDeletionQueue_RenderThread(bool bDeleteImmediately)
{
// Traverse list backwards so the swap switches to elements already tested
for (int32 Index = DeferredDeletionArray.Num() - 1; Index >= 0; --Index)
{
DeferredDeletionEntry* Entry = &DeferredDeletionArray[Index];
if (Entry->EntryType == DeferredDeletionEntry::DeferredDeletionEntryType::Layer)
{
if (bDeleteImmediately || GOculusXRHMDLayerDeletionFrameNumber > Entry->FrameEnqueued + NUM_FRAMES_TO_WAIT_FOR_LAYER_DELETE)
{
#if UE_VERSION_OLDER_THAN(5, 5, 0)
DeferredDeletionArray.RemoveAtSwap(Index, 1, false);
#else
DeferredDeletionArray.RemoveAtSwap(Index, 1, EAllowShrinking::No);
#endif
}
}
else if (Entry->EntryType == DeferredDeletionEntry::DeferredDeletionEntryType::OvrpLayer)
{
if (bDeleteImmediately || GOculusXRHMDLayerDeletionFrameNumber > Entry->FrameEnqueued + NUM_FRAMES_TO_WAIT_FOR_OVRP_LAYER_DELETE)
{
ExecuteOnRHIThread_DoNotWait([OvrpLayerId = Entry->OvrpLayerId]() {
UE_LOG(LogHMD, Warning, TEXT("Destroying layer %d"), OvrpLayerId);
FOculusXRHMDModule::GetPluginWrapper().DestroyLayer(OvrpLayerId);
});
#if UE_VERSION_OLDER_THAN(5, 5, 0)
DeferredDeletionArray.RemoveAtSwap(Index, 1, false);
#else
DeferredDeletionArray.RemoveAtSwap(Index, 1, EAllowShrinking::No);
#endif
}
}
}
// if the function is to be called multiple times, move this increment somewhere unique!
++GOculusXRHMDLayerDeletionFrameNumber;
}
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,45 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_Layer.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FDeferredDeletionQueue
//-------------------------------------------------------------------------------------------------
class FDeferredDeletionQueue
{
public:
void AddLayerToDeferredDeletionQueue(const FLayerPtr& ptr);
void AddOVRPLayerToDeferredDeletionQueue(const uint32 layerID);
void HandleLayerDeferredDeletionQueue_RenderThread(bool bDeleteImmediately = false);
private:
struct DeferredDeletionEntry
{
enum class DeferredDeletionEntryType
{
Layer,
OvrpLayer
};
FLayerPtr Layer;
uint32 OvrpLayerId;
uint32 FrameEnqueued;
DeferredDeletionEntryType EntryType;
};
TArray<DeferredDeletionEntry> DeferredDeletionArray;
};
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,97 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_DynamicResolutionState.h"
#include "LegacyScreenPercentageDriver.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "SceneView.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FDynamicResolutionState implementation
//-------------------------------------------------------------------------------------------------
FDynamicResolutionState::FDynamicResolutionState(const OculusXRHMD::FSettingsPtr InSettings)
: Settings(InSettings)
, ResolutionFraction(-1.0f)
, ResolutionFractionUpperBound(-1.0f)
{
check(Settings.IsValid());
}
void FDynamicResolutionState::ResetHistory() {
// Empty - Oculus drives resolution fraction externally
};
bool FDynamicResolutionState::IsSupported() const
{
return true;
}
void FDynamicResolutionState::SetupMainViewFamily(class FSceneViewFamily& ViewFamily)
{
check(IsInGameThread());
check(ViewFamily.EngineShowFlags.ScreenPercentage == true);
if (IsEnabled())
{
// Compute desired resolution fraction range
float MinResolutionFraction = Settings->GetPixelDensityMin();
float MaxResolutionFraction = Settings->GetPixelDensityMax();
// Clamp resolution fraction to what the renderer can do.
MinResolutionFraction = FMath::Max(MinResolutionFraction, ISceneViewFamilyScreenPercentage::kMinResolutionFraction);
MaxResolutionFraction = FMath::Min(MaxResolutionFraction, ISceneViewFamilyScreenPercentage::kMaxResolutionFraction);
ResolutionFraction = FMath::Clamp(Settings->PixelDensity, MinResolutionFraction, MaxResolutionFraction);
ResolutionFractionUpperBound = MaxResolutionFraction;
ViewFamily.SetScreenPercentageInterface(new FLegacyScreenPercentageDriver(ViewFamily, ResolutionFraction, ResolutionFractionUpperBound));
}
}
#if !UE_VERSION_OLDER_THAN(5, 4, 0)
void FDynamicResolutionState::SetTemporalUpscaler(const UE::Renderer::Private::ITemporalUpscaler* InTemporalUpscaler)
{
// Not supported
return;
}
#endif // !UE_VERSION_OLDER_THAN(5, 4, 0)
DynamicRenderScaling::TMap<float> FDynamicResolutionState::GetResolutionFractionsApproximation() const
{
DynamicRenderScaling::TMap<float> ResolutionFractions;
ResolutionFractions.SetAll(1.0f);
ResolutionFractions[GDynamicPrimaryResolutionFraction] = ResolutionFraction;
return ResolutionFractions;
}
DynamicRenderScaling::TMap<float> FDynamicResolutionState::GetResolutionFractionsUpperBound() const
{
DynamicRenderScaling::TMap<float> ResolutionFractions;
ResolutionFractions.SetAll(1.0f);
ResolutionFractions[GDynamicPrimaryResolutionFraction] = ResolutionFractionUpperBound;
return ResolutionFractionUpperBound;
}
void FDynamicResolutionState::SetEnabled(bool bEnable)
{
check(IsInGameThread());
Settings->Flags.bPixelDensityAdaptive = bEnable;
}
bool FDynamicResolutionState::IsEnabled() const
{
check(IsInGameThread());
return Settings->Flags.bPixelDensityAdaptive;
}
void FDynamicResolutionState::ProcessEvent(EDynamicResolutionStateEvent Event) {
// Empty - Oculus drives resolution fraction externally
};
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,46 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_Settings.h"
#include "DynamicResolutionState.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FDynamicResolutionState
//-------------------------------------------------------------------------------------------------
class FDynamicResolutionState : public IDynamicResolutionState
{
public:
FDynamicResolutionState(const OculusXRHMD::FSettingsPtr InSettings);
// ISceneViewFamilyScreenPercentage
virtual void ResetHistory() override;
virtual bool IsSupported() const override;
virtual void SetupMainViewFamily(class FSceneViewFamily& ViewFamily) override;
#if !UE_VERSION_OLDER_THAN(5, 4, 0)
virtual void SetTemporalUpscaler(const UE::Renderer::Private::ITemporalUpscaler* InTemporalUpscaler) override;
#endif // !UE_VERSION_OLDER_THAN(5, 4, 0)
protected:
virtual DynamicRenderScaling::TMap<float> GetResolutionFractionsApproximation() const override;
virtual DynamicRenderScaling::TMap<float> GetResolutionFractionsUpperBound() const override;
virtual void SetEnabled(bool bEnable) override;
virtual bool IsEnabled() const override;
virtual void ProcessEvent(EDynamicResolutionStateEvent Event) override;
private:
const OculusXRHMD::FSettingsPtr Settings;
float ResolutionFraction;
float ResolutionFractionUpperBound;
};
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,74 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_FoveatedRendering.h"
#include "HeadMountedDisplayTypes.h" // For the LogHMD log category
#include "RenderGraphBuilder.h"
#include "SceneView.h"
#include "StereoRendering.h"
FOculusXRFoveatedRenderingImageGenerator::FOculusXRFoveatedRenderingImageGenerator(const FXRSwapChainPtr& Swapchain)
: FoveationSwapchain(Swapchain)
{
GVRSImageManager.RegisterExternalImageGenerator(this);
}
FOculusXRFoveatedRenderingImageGenerator::~FOculusXRFoveatedRenderingImageGenerator()
{
GVRSImageManager.UnregisterExternalImageGenerator(this);
}
#if !UE_VERSION_OLDER_THAN(5, 4, 0)
FRDGTextureRef FOculusXRFoveatedRenderingImageGenerator::GetImage(
FRDGBuilder& GraphBuilder,
const FViewInfo& ViewInfo,
FVariableRateShadingImageManager::EVRSImageType ImageType, bool bGetSoftwareImage)
#else
FRDGTextureRef FOculusXRFoveatedRenderingImageGenerator::GetImage(
FRDGBuilder& GraphBuilder,
const FViewInfo& ViewInfo,
FVariableRateShadingImageManager::EVRSImageType ImageType)
#endif
{
if (!FoveationSwapchain.IsValid())
{
return nullptr;
}
FTextureRHIRef SwapchainTexture = FoveationSwapchain->GetTexture2DArray() ? FoveationSwapchain->GetTexture2DArray() : FoveationSwapchain->GetTexture2D();
FIntPoint TexSize = SwapchainTexture->GetSizeXY();
// Only set texture and return true if we have a valid texture of compatible size
if (SwapchainTexture->IsValid() && TexSize.X > 0 && TexSize.Y > 0)
{
TRefCountPtr<IPooledRenderTarget> PooledRenderTarget = CreateRenderTarget(SwapchainTexture, *SwapchainTexture->GetName().ToString());
return GraphBuilder.RegisterExternalTexture(PooledRenderTarget, *SwapchainTexture->GetName().ToString(), ERDGTextureFlags::SkipTracking);
}
return nullptr;
}
#if !UE_VERSION_OLDER_THAN(5, 4, 0)
bool FOculusXRFoveatedRenderingImageGenerator::IsSupportedByView(const FSceneView& View) const
#else
bool FOculusXRFoveatedRenderingImageGenerator::IsEnabledForView(const FSceneView& View) const
#endif
{
return View.StereoPass != EStereoscopicPass::eSSP_FULL;
}
#if !UE_VERSION_OLDER_THAN(5, 4, 0)
FRDGTextureRef FOculusXRFoveatedRenderingImageGenerator::GetDebugImage(
FRDGBuilder& GraphBuilder,
const FViewInfo& ViewInfo,
FVariableRateShadingImageManager::EVRSImageType ImageType,
bool bGetSoftwareImage)
#else
FRDGTextureRef FOculusXRFoveatedRenderingImageGenerator::GetDebugImage(
FRDGBuilder& GraphBuilder,
const FViewInfo& ViewInfo,
FVariableRateShadingImageManager::EVRSImageType ImageType)
#endif
{
return nullptr;
}

View File

@@ -0,0 +1,84 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Misc/EngineVersionComparison.h"
#include "VariableRateShadingImageManager.h"
#include "XRSwapchain.h"
class FOculusXRFoveatedRenderingImageGenerator : public IVariableRateShadingImageGenerator
{
public:
FOculusXRFoveatedRenderingImageGenerator(const FXRSwapChainPtr& Swapchain);
virtual ~FOculusXRFoveatedRenderingImageGenerator() override;
// IVariableRateShadingImageGenerator interface
#if !UE_VERSION_OLDER_THAN(5, 4, 0)
virtual FRDGTextureRef GetImage(
FRDGBuilder& GraphBuilder,
const FViewInfo& ViewInfo,
FVariableRateShadingImageManager::EVRSImageType ImageType,
bool bGetSoftwareImage = false) override;
#else
virtual FRDGTextureRef GetImage(
FRDGBuilder& GraphBuilder,
const FViewInfo& ViewInfo,
FVariableRateShadingImageManager::EVRSImageType ImageType) override;
#endif
#if !UE_VERSION_OLDER_THAN(5, 4, 0)
virtual void PrepareImages(
FRDGBuilder& GraphBuilder,
const FSceneViewFamily& ViewFamily,
const FMinimalSceneTextures& SceneTextures,
bool bPrepareHardwareImages,
bool bPrepareSoftwareImages) override
{
return;
}
#else
virtual void PrepareImages(
FRDGBuilder& GraphBuilder,
const FSceneViewFamily& ViewFamily,
const FMinimalSceneTextures& SceneTextures) override
{
return;
}
#endif
#if !UE_VERSION_OLDER_THAN(5, 4, 0)
virtual bool IsEnabled() const override
{
return true;
};
#endif
#if !UE_VERSION_OLDER_THAN(5, 4, 0)
virtual bool IsSupportedByView(const FSceneView& View) const override;
#else
virtual bool IsEnabledForView(const FSceneView& View) const override;
#endif
#if !UE_VERSION_OLDER_THAN(5, 4, 0)
virtual FRDGTextureRef GetDebugImage(
FRDGBuilder& GraphBuilder,
const FViewInfo& ViewInfo,
FVariableRateShadingImageManager::EVRSImageType ImageType,
bool bGetSoftwareImage = false) override;
#else
virtual FRDGTextureRef GetDebugImage(
FRDGBuilder& GraphBuilder,
const FViewInfo& ViewInfo,
FVariableRateShadingImageManager::EVRSImageType ImageType) override;
#endif
virtual FVariableRateShadingImageManager::EVRSSourceType GetType() const override
{
return FVariableRateShadingImageManager::EVRSSourceType::FixedFoveation;
}
private:
const FXRSwapChainPtr& FoveationSwapchain;
};

View File

@@ -0,0 +1,33 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_GameFrame.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "GameFramework/WorldSettings.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FGameFrame
//-------------------------------------------------------------------------------------------------
FGameFrame::FGameFrame()
: FrameNumber(0), WorldToMetersScale(100.f), ShowFlags(ESFIM_All0), PlayerOrientation(FQuat::Identity), PlayerLocation(FVector::ZeroVector), FoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering), FoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel::Off), bDynamicFoveatedRendering(false)
{
Flags.Raw = 0;
Fov[0] = Fov[1] = SymmetricFov[0] = SymmetricFov[1] = ovrpFovf{ 0, 0, 0, 0 };
}
TSharedPtr<FGameFrame, ESPMode::ThreadSafe> FGameFrame::Clone() const
{
TSharedPtr<FGameFrame, ESPMode::ThreadSafe> NewFrame = MakeShareable(new FGameFrame(*this));
return NewFrame;
}
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,65 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_Settings.h"
#include "ShowFlags.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FGameFrame
//-------------------------------------------------------------------------------------------------
class FGameFrame : public TSharedFromThis<FGameFrame, ESPMode::ThreadSafe>
{
public:
uint32 FrameNumber; // current frame number. (StartGameFrame_GameThread)
float WorldToMetersScale; // World units (UU) to Meters scale. (OnStartGameFrame)
FIntPoint WindowSize; // actual window size (StartGameFrame_GameThread)
FEngineShowFlags ShowFlags; // (PreRenderViewFamily_RenderThread)
FQuat HeadOrientation; // (CalculateStereoViewOffset)
FQuat PlayerOrientation; // (CalculateStereoViewOffset)
FVector PlayerLocation; // (CalculateStereoViewOffset)
float NearClippingPlane; // (GetStereoProjectionMatrix)
FTransform TrackingToWorld; // (OnEndGameFrame)
FTransform LastTrackingToWorld; // (OnEndGameFrame)
EOculusXRFoveatedRenderingMethod FoveatedRenderingMethod; // OnStartGameFrame
EOculusXRFoveatedRenderingLevel FoveatedRenderingLevel; // OnStartGameFrame
bool bDynamicFoveatedRendering; // OnStartGameFrame
ovrpFovf Fov[ovrpEye_Count]; // UpdateStereoRenderingParams
ovrpFovf SymmetricFov[ovrpEye_Count]; // UpdateStereoRenderingParams, symmetric FOV if frame is using symmetricFOV.
union
{
struct
{
/** True, if splash is shown */
uint64 bSplashIsShown : 1;
/** True, if spectator screen is active */
uint64 bSpectatorScreenActive : 1;
/** True if the frame's positions have been updated on the render thread */
uint64 bRTLateUpdateDone : 1;
};
uint64 Raw;
} Flags;
public:
FGameFrame();
TSharedPtr<FGameFrame, ESPMode::ThreadSafe> Clone() const;
};
typedef TSharedPtr<FGameFrame, ESPMode::ThreadSafe> FGameFramePtr;
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,234 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#include "ProceduralMeshComponent.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_CustomPresent.h"
#include "XRSwapChain.h"
#include "OculusXRPassthroughLayerShapes.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FOvrpLayer
//-------------------------------------------------------------------------------------------------
class FDeferredDeletionQueue;
class FOvrpLayer : public TSharedFromThis<FOvrpLayer, ESPMode::ThreadSafe>
{
public:
FOvrpLayer(uint32 InOvrpLayerId, FDeferredDeletionQueue* InDeferredDeletion);
~FOvrpLayer();
protected:
uint32 OvrpLayerId;
private:
FDeferredDeletionQueue* DeferredDeletion; // necessary for deferred deletion queue of the actual OvrpLayer
};
typedef TSharedPtr<FOvrpLayer, ESPMode::ThreadSafe> FOvrpLayerPtr;
//-------------------------------------------------------------------------------------------------
// FLayer
//-------------------------------------------------------------------------------------------------
class FLayer : public TSharedFromThis<FLayer, ESPMode::ThreadSafe>
{
public:
FLayer(uint32 InId);
FLayer(const FLayer& InLayer);
~FLayer();
uint32 GetId() const { return Id; }
int GetOvrpId() const { return OvrpLayerId; }
void SetDesc(const IStereoLayers::FLayerDesc& InDesc);
void SetDesc(const FSettings* Settings, const IStereoLayers::FLayerDesc& InDesc);
const IStereoLayers::FLayerDesc& GetDesc() const { return Desc; }
void SetEyeLayerDesc(const ovrpLayerDesc_EyeFov& InEyeLayerDesc, bool bInSubmitSpaceWarp);
const FXRSwapChainPtr& GetSwapChain() const { return SwapChain; }
const FXRSwapChainPtr& GetRightSwapChain() const { return RightSwapChain; }
const FXRSwapChainPtr& GetDepthSwapChain() const { return DepthSwapChain; }
const FXRSwapChainPtr& GetFoveationSwapChain() const { return FoveationSwapChain; }
const FXRSwapChainPtr& GetMotionVectorSwapChain() const { return MotionVectorSwapChain; }
const FXRSwapChainPtr& GetMotionVectorDepthSwapChain() const { return MotionVectorDepthSwapChain; }
void MarkTextureForUpdate() { bUpdateTexture = true; }
bool NeedsPokeAHole();
void HandlePokeAHoleComponent();
void BuildPokeAHoleMesh(TArray<FVector>& Vertices, TArray<int32>& Triangles, TArray<FVector2D>& UV0);
bool NeedsPassthroughPokeAHole();
bool ShapeNeedsTextures(ovrpShape shape);
FTextureRHIRef GetTexture() { return Desc.Texture; }
TSharedPtr<FLayer, ESPMode::ThreadSafe> Clone() const;
bool CanReuseResources(const FLayer* InLayer) const;
bool Initialize_RenderThread(const FSettings* Settings, FCustomPresent* CustomPresent, FDeferredDeletionQueue* DeferredDeletion, FRHICommandListImmediate& RHICmdList, const FLayer* InLayer = nullptr);
void UpdateTexture_RenderThread(const FSettings* Settings, FCustomPresent* CustomPresent, FRHICommandListImmediate& RHICmdList);
void UpdatePassthrough_RenderThread(FCustomPresent* CustomPresent, FRHICommandListImmediate& RHICmdList, const FGameFrame* Frame);
const ovrpLayerSubmit* UpdateLayer_RHIThread(const FSettings* Settings, const FGameFrame* Frame, const int LayerIndex);
void IncrementSwapChainIndex_RHIThread(FCustomPresent* CustomPresent);
void ReleaseResources_RHIThread();
bool IsVisible() { return (Desc.Flags & IStereoLayers::LAYER_FLAG_HIDDEN) == 0; }
bool bNeedsTexSrgbCreate;
void AddPassthroughMesh_RenderThread(const TArray<FVector>& Vertices, const TArray<int32>& Triangles, FMatrix Transformation, uint64_t& OutMeshHandle, uint64_t& OutInstanceHandle);
void UpdatePassthroughMeshTransform_RenderThread(uint64_t InstanceHandle, FMatrix Transformation);
void RemovePassthroughMesh_RenderThread(uint64_t MeshHandle, uint64_t InstanceHandle);
void DestroyLayer();
protected:
struct FPassthroughMesh
{
FPassthroughMesh(uint64_t MeshHandle, uint64_t InstanceHandle)
: MeshHandle(MeshHandle)
, InstanceHandle(InstanceHandle)
{
}
uint64_t MeshHandle;
uint64_t InstanceHandle;
};
typedef TSharedPtr<TMap<FString, FPassthroughMesh>, ESPMode::ThreadSafe> FUserDefinedGeometryMapPtr;
void UpdatePassthroughStyle_RenderThread(const FEdgeStyleParameters& EdgeStyleParameters);
struct FPassthroughPokeActor
{
FPassthroughPokeActor(){};
FPassthroughPokeActor(UProceduralMeshComponent* PokeAHoleComponentPtr, AActor* PokeAHoleActor)
: PokeAHoleComponentPtr(PokeAHoleComponentPtr)
, PokeAHoleActor(PokeAHoleActor){};
UProceduralMeshComponent* PokeAHoleComponentPtr;
AActor* PokeAHoleActor;
};
typedef TSharedPtr<TMap<FString, FPassthroughPokeActor>, ESPMode::ThreadSafe> FPassthroughPokeActorMapPtr;
bool BuildPassthroughPokeActor(FOculusPassthroughMeshRef PassthroughMesh, FPassthroughPokeActor& OutPassthroughPokeActor);
void UpdatePassthroughPokeActors_GameThread();
uint32 Id;
IStereoLayers::FLayerDesc Desc;
int OvrpLayerId;
ovrpLayerDescUnion OvrpLayerDesc;
ovrpLayerSubmitUnion OvrpLayerSubmit;
FOvrpLayerPtr OvrpLayer;
FXRSwapChainPtr SwapChain;
FXRSwapChainPtr DepthSwapChain;
FXRSwapChainPtr FoveationSwapChain;
FXRSwapChainPtr RightSwapChain;
FXRSwapChainPtr RightDepthSwapChain;
FXRSwapChainPtr MotionVectorSwapChain;
FXRSwapChainPtr MotionVectorDepthSwapChain;
FTextureRHIRef InvAlphaTexture;
bool bUpdateTexture;
bool bInvertY;
bool bHasDepth;
bool bSupportDepthComposite;
bool bSubmitSpaceWarp;
UProceduralMeshComponent* PokeAHoleComponentPtr;
AActor* PokeAHoleActor;
FUserDefinedGeometryMapPtr UserDefinedGeometryMap;
FPassthroughPokeActorMapPtr PassthroughPokeActorMap;
};
typedef TSharedPtr<FLayer, ESPMode::ThreadSafe> FLayerPtr;
//-------------------------------------------------------------------------------------------------
// FLayerPtr_CompareId
//-------------------------------------------------------------------------------------------------
struct FLayerPtr_CompareId
{
FORCEINLINE bool operator()(const FLayerPtr& A, const FLayerPtr& B) const
{
return A->GetId() < B->GetId();
}
};
//-------------------------------------------------------------------------------------------------
// FLayerPtr_ComparePriority
//-------------------------------------------------------------------------------------------------
struct FLayerPtr_ComparePriority
{
FORCEINLINE bool operator()(const FLayerPtr& A, const FLayerPtr& B) const
{
if (A->GetDesc().Priority < B->GetDesc().Priority)
return true;
if (A->GetDesc().Priority > B->GetDesc().Priority)
return false;
return A->GetId() < B->GetId();
}
};
struct FLayerPtr_CompareTotal
{
FORCEINLINE int32 GetLayerTypePriority(const FLayerPtr& A) const
{
// Draw FReconstructedLayer, PoleAHole layers (Android only), EyeFov layer, followed by other layers
const bool IsEyeFov = (A->GetId() == 0);
const bool IsPokeAHole = A->NeedsPokeAHole() || A->NeedsPassthroughPokeAHole();
bool IsUnderlay = false;
if (A->GetDesc().HasShape<FReconstructedLayer>())
{
const FReconstructedLayer& ReconstructedLayerProps = A->GetDesc().GetShape<FReconstructedLayer>();
IsUnderlay = (ReconstructedLayerProps.PassthroughLayerOrder == PassthroughLayerOrder_Underlay);
}
else if (A->GetDesc().HasShape<FUserDefinedLayer>())
{
const FUserDefinedLayer& UserDefinedLayerProps = A->GetDesc().GetShape<FUserDefinedLayer>();
IsUnderlay = (UserDefinedLayerProps.PassthroughLayerOrder == PassthroughLayerOrder_Underlay);
}
const int32 Priority = IsUnderlay ? -2 : IsPokeAHole ? -1
: IsEyeFov ? 0
: 1;
return Priority;
}
FORCEINLINE bool operator()(const FLayerPtr& A, const FLayerPtr& B) const
{
// First order layers by type
int32 PassA = GetLayerTypePriority(A);
int32 PassB = GetLayerTypePriority(B);
if (PassA != PassB)
return PassA < PassB;
// Draw non-FaceLocked layers first
const IStereoLayers::FLayerDesc& DescA = A->GetDesc();
const IStereoLayers::FLayerDesc& DescB = B->GetDesc();
bool bFaceLockedA = (DescA.PositionType == IStereoLayers::ELayerType::FaceLocked);
bool bFaceLockedB = (DescB.PositionType == IStereoLayers::ELayerType::FaceLocked);
if (bFaceLockedA != bFaceLockedB)
return !bFaceLockedA;
// Draw layers by ascending priority
if (DescA.Priority != DescB.Priority)
return DescA.Priority < DescB.Priority;
// Draw layers by ascending id
return A->GetId() < B->GetId();
}
};
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,151 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_Settings.h"
#include "Engine/Engine.h"
#include "OculusXRHMDTypes.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FSettings
//-------------------------------------------------------------------------------------------------
FSettings::FSettings()
: BaseOffset(0, 0, 0)
, BaseOrientation(FQuat::Identity)
, PixelDensity(1.0f)
, SystemHeadset(ovrpSystemHeadset_None)
, SuggestedCpuPerfLevel(EOculusXRProcessorPerformanceLevel::SustainedLow)
, SuggestedGpuPerfLevel(EOculusXRProcessorPerformanceLevel::SustainedHigh)
, FoveatedRenderingMethod(EOculusXRFoveatedRenderingMethod::FixedFoveatedRendering)
, FoveatedRenderingLevel(EOculusXRFoveatedRenderingLevel::Off)
, bDynamicFoveatedRendering(true)
, bSupportEyeTrackedFoveatedRendering(false)
, SystemSplashBackground(ESystemSplashBackgroundType::Black)
, XrApi(EOculusXRXrApi::OVRPluginOpenXR)
, ColorSpace(EOculusXRColorSpace::P3)
, ControllerPoseAlignment(EOculusXRControllerPoseAlignment::Default)
, HandTrackingSupport(EOculusXRHandTrackingSupport::ControllersOnly)
, HandTrackingFrequency(EOculusXRHandTrackingFrequency::LOW)
, HandTrackingVersion(EOculusXRHandTrackingVersion::Default)
, ColorScale(ovrpVector4f{ 1, 1, 1, 1 })
, ColorOffset(ovrpVector4f{ 0, 0, 0, 0 })
, bApplyColorScaleAndOffsetToAllLayers(false)
, CurrentFeatureLevel(GMaxRHIFeatureLevel)
, bLateLatching(false)
, bSupportExperimentalFeatures(false)
, ProcessorFavor(EProcessorFavor::FavorEqually)
, BodyTrackingFidelity(EOculusXRHMDBodyTrackingFidelity::Low)
, BodyTrackingJointSet(EOculusXRHMDBodyJointSet::UpperBody)
, FaceTrackingDataSource()
, bFaceTrackingVisemesEnabled(false)
, MPPoseRestoreType(EOculusXRMPPoseRestoreType::Disabled)
{
Flags.Raw = 0;
Flags.bHMDEnabled = true;
Flags.bUpdateOnRT = true;
Flags.bHQBuffer = false;
Flags.bCompositeDepth = true;
#if PLATFORM_ANDROID
Flags.bsRGBEyeBuffer = true;
// oculus mobile is always-on stereo, no need for enableStereo codepaths
Flags.bStereoEnabled = true;
#else
Flags.bsRGBEyeBuffer = false;
Flags.bStereoEnabled = false;
#endif
CurrentFeatureLevel = GEngine ? GEngine->GetDefaultWorldFeatureLevel() : GMaxRHIFeatureLevel;
CurrentShaderPlatform = GShaderPlatformForFeatureLevel[CurrentFeatureLevel];
Flags.bSupportsDash = true;
Flags.bFocusAware = true;
Flags.bRequiresSystemKeyboard = false;
Flags.bInsightPassthroughEnabled = false;
Flags.bAnchorSupportEnabled = false;
Flags.bAnchorSharingEnabled = false;
Flags.bSceneSupportEnabled = false;
Flags.bBoundaryVisibilitySupportEnabled = false;
Flags.bDefaultBoundaryVisibilitySuppressed = false;
Flags.bColocationSessionsEnabled = false;
Flags.bBodyTrackingEnabled = false;
Flags.bEyeTrackingEnabled = false;
Flags.bFaceTrackingEnabled = false;
EyeRenderViewport[0] = EyeRenderViewport[1] = FIntRect(0, 0, 0, 0);
RenderTargetSize = FIntPoint(0, 0);
Flags.bIterativeCookOnTheFly = false;
Flags.bSupportSBC = false;
// Note: SBCPath should be consistent as other project saved path, e.g. /sdcard/Android/data/[packagename]/files/UnrealGame/[ProjectName]/[ProjectName]/Saved/Profiling/CSV/
FString ProjectName = !FApp::IsProjectNameEmpty() ? FApp::GetProjectName() : FPlatformProcess::ExecutableName();
SBCPath = FString("files/UnrealGame/") + ProjectName + FString("/") + ProjectName + FString("/Saved/VulkanCache");
#ifdef WITH_OCULUS_BRANCH
Flags.bTileTurnOffEnabled = false;
Flags.bSetActivePIEToPrimary = true;
Flags.bSetCVarPIEToPrimary = true;
Flags.bUpdateHeadPoseForInactivePlayer = false;
#else
Flags.bTileTurnOffEnabled = true;
// Vanilla UE doesn't support MultiPlayerTesting well, disable it for now.
Flags.bSetActivePIEToPrimary = false;
Flags.bSetCVarPIEToPrimary = false;
Flags.bUpdateHeadPoseForInactivePlayer = false;
#endif
}
TSharedPtr<FSettings, ESPMode::ThreadSafe> FSettings::Clone() const
{
TSharedPtr<FSettings, ESPMode::ThreadSafe> NewSettings = MakeShareable(new FSettings(*this));
return NewSettings;
}
float FSettings::GetPixelDensityMin() const
{
static const IConsoleVariable* CVarPixelDensityMin = IConsoleManager::Get().FindConsoleVariable(VAR_PixelDensityMin);
return CVarPixelDensityMin ? CVarPixelDensityMin->GetFloat() : 0.8f;
}
float FSettings::GetPixelDensityMax() const
{
static const IConsoleVariable* CVarPixelDensityMax = IConsoleManager::Get().FindConsoleVariable(VAR_PixelDensityMax);
return CVarPixelDensityMax ? CVarPixelDensityMax->GetFloat() : 1.2f;
}
void FSettings::SetPixelDensity(float NewPixelDensity)
{
if (Flags.bPixelDensityAdaptive)
{
PixelDensity = FMath::Clamp(NewPixelDensity, GetPixelDensityMin(), GetPixelDensityMax());
}
else
{
PixelDensity = FMath::Clamp(NewPixelDensity, ClampPixelDensityMin, ClampPixelDensityMax);
}
}
void FSettings::SetPixelDensitySmooth(float NewPixelDensity)
{
// Pixel Density changes need to be smooth both for artifacts with FFR/TTO (FFR/tile-turnoff is one frame late so shouldn't change too fast)
// but also so that if the developer uses the CVar and not the runtime (which is already smooth) there is no jump artifacts.
constexpr float MaxPerFrameIncrease = 0.010;
constexpr float MaxPerFrameDecrease = 0.045;
float NewClampedPixelDensity = FMath::Clamp(NewPixelDensity, PixelDensity - MaxPerFrameDecrease, PixelDensity + MaxPerFrameIncrease);
if (Flags.bPixelDensityAdaptive)
{
PixelDensity = FMath::Clamp(NewClampedPixelDensity, GetPixelDensityMin(), GetPixelDensityMax());
}
else
{
PixelDensity = FMath::Clamp(NewClampedPixelDensity, ClampPixelDensityMin, ClampPixelDensityMax);
}
}
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,201 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
namespace OculusXRHMD
{
static const float ClampPixelDensityMin = 0.5f;
static const float ClampPixelDensityMax = 2.0f;
//-------------------------------------------------------------------------------------------------
// FSettings
//-------------------------------------------------------------------------------------------------
class FSettings : public TSharedFromThis<FSettings, ESPMode::ThreadSafe>
{
public:
union
{
struct
{
/** Whether stereo is currently on or off. */
uint64 bStereoEnabled : 1;
/** Whether or not switching to stereo is allowed */
uint64 bHMDEnabled : 1;
/** Turns on/off updating view's orientation/position on a RenderThread. When it is on,
latency should be significantly lower.
See 'HMD UPDATEONRT ON|OFF' console command.
*/
uint64 bUpdateOnRT : 1;
/** Enforces headtracking to work even in non-stereo mode (for debugging or screenshots).
See 'MOTION ENFORCE' console command. */
uint64 bHeadTrackingEnforced : 1;
/** Allocate an high quality OVR_FORMAT_R11G11B10_FLOAT buffer for Rift */
uint64 bHQBuffer : 1;
/** Rendering should be (could be) paused */
uint64 bPauseRendering : 1;
/** HQ Distortion */
uint64 bHQDistortion : 1;
/** Send the depth buffer to the compositor */
uint64 bCompositeDepth : 1;
/** Supports Dash in-game compositing */
uint64 bSupportsDash : 1;
#if !UE_BUILD_SHIPPING
/** Show status / statistics on screen. See 'hmd stats' cmd */
uint64 bShowStats : 1;
#endif
/** Dynamically update pixel density to maintain framerate */
uint64 bPixelDensityAdaptive : 1;
/** All future eye buffers will need to be created with TexSRGB_Create flag due to the current feature level (ES31) */
uint64 bsRGBEyeBuffer : 1;
/** Supports Focus Aware state on Quest */
uint64 bFocusAware : 1;
/** Requires the Oculus system keyboard */
uint64 bRequiresSystemKeyboard : 1;
/** Whether passthrough functionality can be used with the app */
uint64 bInsightPassthroughEnabled : 1;
/** Whether Anchors can be used with the app */
uint64 bAnchorSupportEnabled : 1;
/** Whether Anchor Sharing can be used with the app */
uint64 bAnchorSharingEnabled : 1;
/** Whether Scene can be used with the app */
uint64 bSceneSupportEnabled : 1;
/** Whether the guardian boundary visibility toggles can be used with the app */
uint64 bBoundaryVisibilitySupportEnabled : 1;
/** Whether the guardian boundary visibility should be suppressed by default, this can only be true if passthrough is enabled */
uint64 bDefaultBoundaryVisibilitySuppressed : 1;
/** Whether Colocation Sessions can be used with the app */
uint64 bColocationSessionsEnabled : 1;
/** Whether body tracking functionality can be used with the app */
uint64 bBodyTrackingEnabled : 1;
/** Whether eye tracking functionality can be used with the app */
uint64 bEyeTrackingEnabled : 1;
/** Whether face tracking functionality can be used with the app */
uint64 bFaceTrackingEnabled : 1;
/** Whether tile turn off can be used with the app */
uint64 bTileTurnOffEnabled : 1;
/** Whether iterative cook-on-the-fly is enabled */
uint64 bIterativeCookOnTheFly : 1;
/** When running multiple player windows in a single process with VRPreview, this option determins whether to set the current active PIE to the primary one on the fly.*/
uint64 bSetActivePIEToPrimary : 1;
/** When running multiple player windows in a single process with VRPreview, this option determins whether to set the PIE specified by CVar vr.PrimaryPIEIndex to the primary one on the fly.*/
uint64 bSetCVarPIEToPrimary : 1;
/* In case of multiple players testing, when the current player is inactive, disabling this setting will make sure the current player does NOT update headpose until it is active again.*/
uint64 bUpdateHeadPoseForInactivePlayer : 1;
/* Enable Shader Binary Cache (SBC) and setup its path */
uint64 bSupportSBC : 1;
};
uint64 Raw;
} Flags;
/** HMD base values, specify forward orientation and zero pos offset */
FVector BaseOffset; // base position, in meters, relatively to the sensor //@todo hmd: clients need to stop using oculus space
FQuat BaseOrientation; // base orientation
/** Viewports for each eye, in render target texture coordinates */
FIntRect EyeRenderViewport[ovrpEye_Count];
/** Viewports for each eye, without DynamicResolution scaling applied */
FIntRect EyeUnscaledRenderViewport[ovrpEye_Count];
ovrpMatrix4f EyeProjectionMatrices[ovrpEye_Count]; // 0 - left, 1 - right same as Views
ovrpMatrix4f MonoProjectionMatrix;
FIntPoint RenderTargetSize;
float PixelDensity;
ovrpSystemHeadset SystemHeadset;
float VsyncToNextVsync;
EOculusXRProcessorPerformanceLevel SuggestedCpuPerfLevel;
EOculusXRProcessorPerformanceLevel SuggestedGpuPerfLevel;
EOculusXRFoveatedRenderingMethod FoveatedRenderingMethod;
EOculusXRFoveatedRenderingLevel FoveatedRenderingLevel;
bool bDynamicFoveatedRendering;
bool bSupportEyeTrackedFoveatedRendering;
ESystemSplashBackgroundType SystemSplashBackground;
EOculusXRXrApi XrApi;
EOculusXRColorSpace ColorSpace;
EOculusXRControllerPoseAlignment ControllerPoseAlignment;
EOculusXRHandTrackingSupport HandTrackingSupport;
EOculusXRHandTrackingFrequency HandTrackingFrequency;
EOculusXRHandTrackingVersion HandTrackingVersion;
ovrpVector4f ColorScale, ColorOffset;
bool bApplyColorScaleAndOffsetToAllLayers;
FStaticFeatureLevel CurrentFeatureLevel;
EShaderPlatform CurrentShaderPlatform;
bool bLateLatching;
bool bSupportExperimentalFeatures;
EProcessorFavor ProcessorFavor;
EOculusXRHMDBodyTrackingFidelity BodyTrackingFidelity;
EOculusXRHMDBodyJointSet BodyTrackingJointSet;
TSet<EFaceTrackingDataSourceConfig> FaceTrackingDataSource;
bool bFaceTrackingVisemesEnabled;
bool bIterativeCookOnTheFly;
EOculusXRMPPoseRestoreType MPPoseRestoreType;
FString SBCPath;
public:
FSettings();
virtual ~FSettings() {}
bool IsStereoEnabled() const { return Flags.bStereoEnabled && Flags.bHMDEnabled; }
float GetPixelDensityMin() const;
float GetPixelDensityMax() const;
void SetPixelDensity(float NewPixelDensity);
void SetPixelDensitySmooth(float NewPixelDensity);
TSharedPtr<FSettings, ESPMode::ThreadSafe> Clone() const;
};
typedef TSharedPtr<FSettings, ESPMode::ThreadSafe> FSettingsPtr;
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,121 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_SpectatorScreenController.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD.h"
#include "TextureResource.h"
#include "Engine/TextureRenderTarget2D.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FSpectatorScreenController
//-------------------------------------------------------------------------------------------------
FSpectatorScreenController::FSpectatorScreenController(FOculusXRHMD* InOculusXRHMD)
: FDefaultSpectatorScreenController(InOculusXRHMD)
, OculusXRHMD(InOculusXRHMD)
, SpectatorMode(EMRSpectatorScreenMode::Default)
, ForegroundRenderTexture(nullptr)
, BackgroundRenderTexture(nullptr)
{
}
void FSpectatorScreenController::RenderSpectatorScreen_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture* BackBuffer, FTextureRHIRef RenderTexture, FVector2D WindowSize)
{
CheckInRenderThread();
if (OculusXRHMD->GetCustomPresent_Internal())
{
if (SpectatorMode == EMRSpectatorScreenMode::ExternalComposition)
{
auto ForegroundResource = ForegroundRenderTexture->GetRenderTargetResource();
auto BackgroundResource = BackgroundRenderTexture->GetRenderTargetResource();
if (ForegroundResource && BackgroundResource)
{
RenderSpectatorModeExternalComposition(
RHICmdList,
FTextureRHIRef(BackBuffer),
ForegroundResource->GetRenderTargetTexture(),
BackgroundResource->GetRenderTargetTexture());
return;
}
}
else if (SpectatorMode == EMRSpectatorScreenMode::DirectComposition)
{
auto BackgroundResource = BackgroundRenderTexture->GetRenderTargetResource();
if (BackgroundResource)
{
RenderSpectatorModeDirectComposition(
RHICmdList,
FTextureRHIRef(BackBuffer),
BackgroundRenderTexture->GetRenderTargetResource()->GetRenderTargetTexture());
return;
}
}
FDefaultSpectatorScreenController::RenderSpectatorScreen_RenderThread(RHICmdList, BackBuffer, RenderTexture, WindowSize);
}
}
void FSpectatorScreenController::RenderSpectatorModeUndistorted(FRHICommandListImmediate& RHICmdList, FTextureRHIRef TargetTexture, FTextureRHIRef EyeTexture, FTextureRHIRef OtherTexture, FVector2D WindowSize)
{
CheckInRenderThread();
FSettings* Settings = OculusXRHMD->GetSettings_RenderThread();
FIntRect DestRect(0, 0, TargetTexture->GetSizeX() / 2, TargetTexture->GetSizeY());
for (int i = 0; i < 2; ++i)
{
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, EyeTexture, Settings->EyeRenderViewport[i], TargetTexture, DestRect, false, true);
DestRect.Min.X += TargetTexture->GetSizeX() / 2;
DestRect.Max.X += TargetTexture->GetSizeX() / 2;
}
}
void FSpectatorScreenController::RenderSpectatorModeDistorted(FRHICommandListImmediate& RHICmdList, FTextureRHIRef TargetTexture, FTextureRHIRef EyeTexture, FTextureRHIRef OtherTexture, FVector2D WindowSize)
{
CheckInRenderThread();
FCustomPresent* CustomPresent = OculusXRHMD->GetCustomPresent_Internal();
FTextureRHIRef MirrorTexture = CustomPresent->GetMirrorTexture();
if (MirrorTexture)
{
FIntRect SrcRect(0, 0, MirrorTexture->GetSizeX(), MirrorTexture->GetSizeY());
FIntRect DestRect(0, 0, TargetTexture->GetSizeX(), TargetTexture->GetSizeY());
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, MirrorTexture, SrcRect, TargetTexture, DestRect, false, true);
}
}
void FSpectatorScreenController::RenderSpectatorModeSingleEye(FRHICommandListImmediate& RHICmdList, FTextureRHIRef TargetTexture, FTextureRHIRef EyeTexture, FTextureRHIRef OtherTexture, FVector2D WindowSize)
{
CheckInRenderThread();
FSettings* Settings = OculusXRHMD->GetSettings_RenderThread();
const FIntRect SrcRect = Settings->EyeRenderViewport[0];
const FIntRect DstRect(0, 0, TargetTexture->GetSizeX(), TargetTexture->GetSizeY());
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, EyeTexture, SrcRect, TargetTexture, DstRect, false, true);
}
void FSpectatorScreenController::RenderSpectatorModeDirectComposition(FRHICommandListImmediate& RHICmdList, FTextureRHIRef TargetTexture, const FTextureRHIRef SrcTexture) const
{
CheckInRenderThread();
const FIntRect SrcRect(0, 0, SrcTexture->GetSizeX(), SrcTexture->GetSizeY());
const FIntRect DstRect(0, 0, TargetTexture->GetSizeX(), TargetTexture->GetSizeY());
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, SrcTexture, SrcRect, TargetTexture, DstRect, false, true);
}
void FSpectatorScreenController::RenderSpectatorModeExternalComposition(FRHICommandListImmediate& RHICmdList, FTextureRHIRef TargetTexture, const FTextureRHIRef FrontTexture, const FTextureRHIRef BackTexture) const
{
CheckInRenderThread();
const FIntRect FrontSrcRect(0, 0, FrontTexture->GetSizeX(), FrontTexture->GetSizeY());
const FIntRect FrontDstRect(0, 0, TargetTexture->GetSizeX() / 2, TargetTexture->GetSizeY());
const FIntRect BackSrcRect(0, 0, BackTexture->GetSizeX(), BackTexture->GetSizeY());
const FIntRect BackDstRect(TargetTexture->GetSizeX() / 2, 0, TargetTexture->GetSizeX(), TargetTexture->GetSizeY());
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, FrontTexture, FrontSrcRect, TargetTexture, FrontDstRect, false, true);
OculusXRHMD->CopyTexture_RenderThread(RHICmdList, BackTexture, BackSrcRect, TargetTexture, BackDstRect, false, true);
}
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,53 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "DefaultSpectatorScreenController.h"
class UTextureRenderTarget2D;
namespace OculusXRHMD
{
// Oculus specific spectator screen modes that override the regular VR spectator screens
enum class EMRSpectatorScreenMode : uint8
{
Default,
ExternalComposition,
DirectComposition
};
//-------------------------------------------------------------------------------------------------
// FSpectatorScreenController
//-------------------------------------------------------------------------------------------------
class FSpectatorScreenController : public FDefaultSpectatorScreenController
{
public:
FSpectatorScreenController(class FOculusXRHMD* InOculusXRHMD);
void SetMRSpectatorScreenMode(EMRSpectatorScreenMode Mode) { SpectatorMode = Mode; }
void SetMRForeground(UTextureRenderTarget2D* Texture) { ForegroundRenderTexture = Texture; }
void SetMRBackground(UTextureRenderTarget2D* Texture) { BackgroundRenderTexture = Texture; }
virtual void RenderSpectatorScreen_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture* BackBuffer, FTextureRHIRef RenderTarget, FVector2D WindowSize) override;
virtual void RenderSpectatorModeUndistorted(FRHICommandListImmediate& RHICmdList, FTextureRHIRef TargetTexture, FTextureRHIRef EyeTexture, FTextureRHIRef OtherTexture, FVector2D WindowSize) override;
virtual void RenderSpectatorModeDistorted(FRHICommandListImmediate& RHICmdList, FTextureRHIRef TargetTexture, FTextureRHIRef EyeTexture, FTextureRHIRef OtherTexture, FVector2D WindowSize) override;
virtual void RenderSpectatorModeSingleEye(FRHICommandListImmediate& RHICmdList, FTextureRHIRef TargetTexture, FTextureRHIRef EyeTexture, FTextureRHIRef OtherTexture, FVector2D WindowSize) override;
private:
FOculusXRHMD* OculusXRHMD;
EMRSpectatorScreenMode SpectatorMode;
UTextureRenderTarget2D* ForegroundRenderTexture;
UTextureRenderTarget2D* BackgroundRenderTexture;
void RenderSpectatorModeDirectComposition(FRHICommandListImmediate& RHICmdList, FTextureRHIRef TargetTexture, const FTextureRHIRef SrcTexture) const;
void RenderSpectatorModeExternalComposition(FRHICommandListImmediate& RHICmdList, FTextureRHIRef TargetTexture, const FTextureRHIRef FrontTexture, const FTextureRHIRef BackTexture) const;
};
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,667 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_Splash.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD.h"
#include "RenderingThread.h"
#include "Misc/ScopeLock.h"
#include "OculusXRHMDRuntimeSettings.h"
#include "TextureResource.h"
#if PLATFORM_ANDROID
#include "Android/AndroidJNI.h"
#include "Android/AndroidApplication.h"
#include "OculusXRHMDTypes.h"
#endif
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FSplash
//-------------------------------------------------------------------------------------------------
FSplash::FSplash(FOculusXRHMD* InOculusXRHMD)
: OculusXRHMD(InOculusXRHMD), CustomPresent(InOculusXRHMD->GetCustomPresent_Internal()), FramesOutstanding(0), NextLayerId(1), bInitialized(false), bIsShown(false), bNeedSplashUpdate(false), bShouldShowSplash(false), SystemDisplayInterval(1 / 90.0f)
{
// Create empty quad layer for UE layer
{
IStereoLayers::FLayerDesc LayerDesc;
LayerDesc.QuadSize = FVector2D(0.01f, 0.01f);
LayerDesc.Priority = 0;
LayerDesc.PositionType = IStereoLayers::TrackerLocked;
LayerDesc.Texture = nullptr;
UELayer = MakeShareable(new FLayer(NextLayerId++));
UELayer->SetDesc(LayerDesc);
}
}
FSplash::~FSplash()
{
// Make sure RenTicker is freed in Shutdown
check(!Ticker.IsValid())
}
void FSplash::Tick_RenderThread(float DeltaTime)
{
CheckInRenderThread();
if (FramesOutstanding > 0)
{
UE_LOG(LogHMD, VeryVerbose, TEXT("Splash skipping frame; too many frames outstanding"));
return;
}
const double TimeInSeconds = FPlatformTime::Seconds();
const double DeltaTimeInSeconds = TimeInSeconds - LastTimeInSeconds;
if (DeltaTimeInSeconds > 2.f * SystemDisplayInterval && Layers_RenderThread_DeltaRotation.Num() > 0)
{
FScopeLock ScopeLock(&RenderThreadLock);
for (TTuple<FLayerPtr, FQuat>& Info : Layers_RenderThread_DeltaRotation)
{
FLayerPtr Layer = Info.Key;
const FQuat& DeltaRotation = Info.Value;
check(Layer.IsValid());
check(!DeltaRotation.Equals(FQuat::Identity)); // Only layers with non-zero delta rotation should be in the DeltaRotation array.
IStereoLayers::FLayerDesc LayerDesc = Layer->GetDesc();
LayerDesc.Transform.SetRotation(LayerDesc.Transform.GetRotation() * DeltaRotation);
LayerDesc.Transform.NormalizeRotation();
Layer->SetDesc(LayerDesc);
}
LastTimeInSeconds = TimeInSeconds;
}
RenderFrame_RenderThread(FRHICommandListExecutor::GetImmediateCommandList());
}
void FSplash::LoadSettings()
{
UOculusXRHMDRuntimeSettings* HMDSettings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
check(HMDSettings);
ClearSplashes();
for (const FOculusXRSplashDesc& SplashDesc : HMDSettings->SplashDescs)
{
AddSplash(SplashDesc);
}
if (HMDSettings->bAutoEnabled)
{
if (!PreLoadLevelDelegate.IsValid())
{
PreLoadLevelDelegate = FCoreUObjectDelegates::PreLoadMap.AddSP(this, &FSplash::OnPreLoadMap);
}
if (!PostLoadLevelDelegate.IsValid())
{
PostLoadLevelDelegate = FCoreUObjectDelegates::PostLoadMapWithWorld.AddSP(this, &FSplash::OnPostLoadMap);
}
}
else
{
if (PreLoadLevelDelegate.IsValid())
{
FCoreUObjectDelegates::PreLoadMap.Remove(PreLoadLevelDelegate);
PreLoadLevelDelegate.Reset();
}
if (PostLoadLevelDelegate.IsValid())
{
FCoreUObjectDelegates::PostLoadMapWithWorld.Remove(PostLoadLevelDelegate);
PostLoadLevelDelegate.Reset();
}
}
}
void FSplash::OnPreLoadMap(const FString&)
{
DoShow();
}
void FSplash::OnPostLoadMap(UWorld* LoadedWorld)
{
// Don't auto-hide splash if show loading screen is called explicitly
if (!bShouldShowSplash)
{
UE_LOG(LogHMD, Log, TEXT("FSplash::OnPostLoadMap Hide Auto Splash"));
HideLoadingScreen();
}
}
#if WITH_EDITOR
void FSplash::OnPieBegin(bool bIsSimulating)
{
LoadSettings();
}
#endif
void FSplash::Startup()
{
CheckInGameThread();
if (!bInitialized)
{
Settings = OculusXRHMD->CreateNewSettings();
Frame = OculusXRHMD->CreateNewGameFrame();
// keep units in meters rather than UU (because UU make not much sense).
Frame->WorldToMetersScale = 1.0f;
float SystemDisplayFrequency;
if (OVRP_SUCCESS(FOculusXRHMDModule::GetPluginWrapper().GetSystemDisplayFrequency2(&SystemDisplayFrequency)))
{
SystemDisplayInterval = 1.0f / SystemDisplayFrequency;
}
LoadSettings();
OculusXRHMD->InitDevice();
#if WITH_EDITOR
PieBeginDelegateHandle = FEditorDelegates::BeginPIE.AddRaw(this, &FSplash::OnPieBegin);
#else
UOculusXRHMDRuntimeSettings* HMDSettings = GetMutableDefault<UOculusXRHMDRuntimeSettings>();
check(HMDSettings);
if (HMDSettings->bAutoEnabled)
{
UE_LOG(LogHMD, Log, TEXT("FSplash::Startup Show Splash on Startup"));
DoShow();
}
#endif
OculusXRHMD->Settings_RenderThread = OculusXRHMD->Settings->Clone();
bInitialized = true;
}
}
void FSplash::StopTicker()
{
CheckInGameThread();
if (!bIsShown)
{
ExecuteOnRenderThread([this]() {
if (Ticker.IsValid())
{
Ticker->Unregister();
Ticker = nullptr;
}
});
UnloadTextures();
}
}
void FSplash::StartTicker()
{
CheckInGameThread();
if (!Ticker.IsValid())
{
Ticker = MakeShareable(new FTicker(this));
ExecuteOnRenderThread([this]() {
LastTimeInSeconds = FPlatformTime::Seconds();
Ticker->Register();
});
}
}
void FSplash::RenderFrame_RenderThread(FRHICommandListImmediate& RHICmdList)
{
CheckInRenderThread();
FScopeLock ScopeLock(&RenderThreadLock);
// RenderFrame
FSettingsPtr XSettings = Settings->Clone();
FGameFramePtr XFrame = Frame->Clone();
XFrame->FrameNumber = OculusXRHMD->NextFrameNumber;
XFrame->ShowFlags.Rendering = true;
TArray<FLayerPtr> XLayers = Layers_RenderThread_Input;
ensure(XLayers.Num() != 0);
ovrpResult Result;
if (FOculusXRHMDModule::GetPluginWrapper().GetInitialized() && OculusXRHMD->WaitFrameNumber != XFrame->FrameNumber)
{
UE_LOG(LogHMD, Verbose, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().WaitToBeginFrame %u"), XFrame->FrameNumber);
if (OVRP_FAILURE(Result = FOculusXRHMDModule::GetPluginWrapper().WaitToBeginFrame(XFrame->FrameNumber)))
{
UE_LOG(LogHMD, Error, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().WaitToBeginFrame %u failed (%d)"), XFrame->FrameNumber, Result);
XFrame->ShowFlags.Rendering = false;
}
else
{
OculusXRHMD->WaitFrameNumber = XFrame->FrameNumber;
OculusXRHMD->NextFrameNumber = XFrame->FrameNumber + 1;
FPlatformAtomics::InterlockedIncrement(&FramesOutstanding);
}
}
else
{
XFrame->ShowFlags.Rendering = false;
}
if (XFrame->ShowFlags.Rendering)
{
if (OVRP_FAILURE(Result = FOculusXRHMDModule::GetPluginWrapper().Update3(ovrpStep_Render, XFrame->FrameNumber, 0.0)))
{
UE_LOG(LogHMD, Error, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().Update3 %u failed (%d)"), XFrame->FrameNumber, Result);
}
}
{
int32 LayerIndex = 0;
int32 LayerIndex_RenderThread = 0;
while (LayerIndex < XLayers.Num() && LayerIndex_RenderThread < Layers_RenderThread.Num())
{
uint32 LayerIdA = XLayers[LayerIndex]->GetId();
uint32 LayerIdB = Layers_RenderThread[LayerIndex_RenderThread]->GetId();
if (LayerIdA < LayerIdB)
{
XLayers[LayerIndex++]->Initialize_RenderThread(XSettings.Get(), CustomPresent, &OculusXRHMD->DeferredDeletion, RHICmdList);
}
else if (LayerIdA > LayerIdB)
{
OculusXRHMD->DeferredDeletion.AddLayerToDeferredDeletionQueue(Layers_RenderThread[LayerIndex_RenderThread++]);
}
else
{
XLayers[LayerIndex++]->Initialize_RenderThread(XSettings.Get(), CustomPresent, &OculusXRHMD->DeferredDeletion, RHICmdList, Layers_RenderThread[LayerIndex_RenderThread++].Get());
}
}
while (LayerIndex < XLayers.Num())
{
XLayers[LayerIndex++]->Initialize_RenderThread(XSettings.Get(), CustomPresent, &OculusXRHMD->DeferredDeletion, RHICmdList);
}
while (LayerIndex_RenderThread < Layers_RenderThread.Num())
{
OculusXRHMD->DeferredDeletion.AddLayerToDeferredDeletionQueue(Layers_RenderThread[LayerIndex_RenderThread++]);
}
}
Layers_RenderThread = XLayers;
for (int32 LayerIndex = 0; LayerIndex < Layers_RenderThread.Num(); LayerIndex++)
{
Layers_RenderThread[LayerIndex]->UpdateTexture_RenderThread(XSettings.Get(), CustomPresent, RHICmdList);
}
// This submit is required since splash happens before the game is rendering, so layers won't be submitted with game render commands
CustomPresent->SubmitGPUCommands_RenderThread(RHICmdList);
// RHIFrame
for (int32 LayerIndex = 0; LayerIndex < XLayers.Num(); LayerIndex++)
{
XLayers[LayerIndex] = XLayers[LayerIndex]->Clone();
}
ExecuteOnRHIThread_DoNotWait([this, XSettings, XFrame, XLayers]() {
ovrpResult ResultT;
if (XFrame->ShowFlags.Rendering)
{
UE_LOG(LogHMD, Verbose, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().BeginFrame4 %u"), XFrame->FrameNumber);
if (OVRP_FAILURE(ResultT = FOculusXRHMDModule::GetPluginWrapper().BeginFrame4(XFrame->FrameNumber, CustomPresent->GetOvrpCommandQueue())))
{
UE_LOG(LogHMD, Error, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().BeginFrame4 %u failed (%d)"), XFrame->FrameNumber, ResultT);
XFrame->ShowFlags.Rendering = false;
}
}
FPlatformAtomics::InterlockedDecrement(&FramesOutstanding);
Layers_RHIThread = XLayers;
Layers_RHIThread.Sort(FLayerPtr_ComparePriority());
if (XFrame->ShowFlags.Rendering)
{
TArray<const ovrpLayerSubmit*> LayerSubmitPtr;
LayerSubmitPtr.SetNum(Layers_RHIThread.Num());
for (int32 LayerIndex = 0; LayerIndex < Layers_RHIThread.Num(); LayerIndex++)
{
LayerSubmitPtr[LayerIndex] = Layers_RHIThread[LayerIndex]->UpdateLayer_RHIThread(XSettings.Get(), XFrame.Get(), LayerIndex);
}
UE_LOG(LogHMD, Verbose, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().EndFrame4 %u"), XFrame->FrameNumber);
if (OVRP_FAILURE(ResultT = FOculusXRHMDModule::GetPluginWrapper().EndFrame4(XFrame->FrameNumber, LayerSubmitPtr.GetData(), LayerSubmitPtr.Num(), CustomPresent->GetOvrpCommandQueue())))
{
UE_LOG(LogHMD, Error, TEXT("Splash FOculusXRHMDModule::GetPluginWrapper().EndFrame4 %u failed (%d)"), XFrame->FrameNumber, ResultT);
}
else
{
for (int32 LayerIndex = 0; LayerIndex < Layers_RHIThread.Num(); LayerIndex++)
{
Layers_RHIThread[LayerIndex]->IncrementSwapChainIndex_RHIThread(CustomPresent);
}
}
}
});
// Splash screen render loop sometimes doesn't trigger RHICmdList flush
CustomPresent->SubmitGPUCommands_RenderThread(FRHICommandListExecutor::GetImmediateCommandList());
}
void FSplash::ReleaseResources_RHIThread()
{
for (int32 LayerIndex = 0; LayerIndex < Layers_RenderThread.Num(); LayerIndex++)
{
Layers_RenderThread[LayerIndex]->ReleaseResources_RHIThread();
}
for (int32 LayerIndex = 0; LayerIndex < Layers_RHIThread.Num(); LayerIndex++)
{
Layers_RHIThread[LayerIndex]->ReleaseResources_RHIThread();
}
Layers_RenderThread.Reset();
Layers_RHIThread.Reset();
}
void FSplash::PreShutdown()
{
CheckInGameThread();
}
void FSplash::Shutdown()
{
CheckInGameThread();
#if WITH_EDITOR
if (PieBeginDelegateHandle.IsValid())
{
FEditorDelegates::BeginPIE.Remove(PieBeginDelegateHandle);
PieBeginDelegateHandle.Reset();
}
#endif
if (PreLoadLevelDelegate.IsValid())
{
FCoreUObjectDelegates::PreLoadMap.Remove(PreLoadLevelDelegate);
PreLoadLevelDelegate.Reset();
}
if (PostLoadLevelDelegate.IsValid())
{
FCoreUObjectDelegates::PostLoadMapWithWorld.Remove(PostLoadLevelDelegate);
PostLoadLevelDelegate.Reset();
}
if (bInitialized)
{
ExecuteOnRenderThread([this]() {
if (Ticker)
{
Ticker->Unregister();
Ticker = nullptr;
}
ExecuteOnRHIThread([this]() {
SplashLayers.Reset();
Layers_RenderThread.Reset();
Layers_RenderThread_Input.Reset();
Layers_RHIThread.Reset();
});
});
bInitialized = false;
}
}
int FSplash::AddSplash(const FOculusXRSplashDesc& Desc)
{
CheckInGameThread();
FScopeLock ScopeLock(&RenderThreadLock);
return SplashLayers.Add(FSplashLayer(Desc));
}
void FSplash::AddSplash(const FSplashDesc& Splash)
{
FOculusXRSplashDesc OculusDesc;
OculusDesc.TransformInMeters = Splash.Transform;
OculusDesc.QuadSizeInMeters = Splash.QuadSize;
OculusDesc.DeltaRotation = Splash.DeltaRotation;
OculusDesc.bNoAlphaChannel = Splash.bIgnoreAlpha;
OculusDesc.bIsDynamic = Splash.bIsDynamic || Splash.bIsExternal;
OculusDesc.TextureOffset = Splash.UVRect.Min;
OculusDesc.TextureScale = Splash.UVRect.Max;
OculusDesc.LoadedTexture = Splash.Texture;
AddSplash(OculusDesc);
}
void FSplash::ClearSplashes()
{
CheckInGameThread();
FScopeLock ScopeLock(&RenderThreadLock);
SplashLayers.Reset();
}
bool FSplash::GetSplash(unsigned InSplashLayerIndex, FOculusXRSplashDesc& OutDesc)
{
CheckInGameThread();
FScopeLock ScopeLock(&RenderThreadLock);
if (InSplashLayerIndex < unsigned(SplashLayers.Num()))
{
OutDesc = SplashLayers[int32(InSplashLayerIndex)].Desc;
return true;
}
return false;
}
IStereoLayers::FLayerDesc FSplash::StereoLayerDescFromOculusSplashDesc(FOculusXRSplashDesc OculusDesc)
{
IStereoLayers::FLayerDesc LayerDesc;
if (OculusDesc.LoadedTexture->GetTextureCube() != nullptr)
{
LayerDesc.SetShape<FCubemapLayer>();
}
// else LayerDesc.Shape defaults to FQuadLayer
LayerDesc.Transform = OculusDesc.TransformInMeters * FTransform(OculusXRHMD->GetSplashRotation().Quaternion());
LayerDesc.QuadSize = OculusDesc.QuadSizeInMeters;
LayerDesc.UVRect = FBox2D(OculusDesc.TextureOffset, OculusDesc.TextureOffset + OculusDesc.TextureScale);
LayerDesc.Priority = INT32_MAX - (int32)(OculusDesc.TransformInMeters.GetTranslation().X * 1000.f);
LayerDesc.PositionType = IStereoLayers::TrackerLocked;
LayerDesc.Texture = OculusDesc.LoadedTexture;
LayerDesc.Flags = IStereoLayers::LAYER_FLAG_QUAD_PRESERVE_TEX_RATIO | (OculusDesc.bNoAlphaChannel ? IStereoLayers::LAYER_FLAG_TEX_NO_ALPHA_CHANNEL : 0) | (OculusDesc.bIsDynamic ? IStereoLayers::LAYER_FLAG_TEX_CONTINUOUS_UPDATE : 0);
return LayerDesc;
}
void FSplash::DoShow()
{
CheckInGameThread();
OculusXRHMD->SetSplashRotationToForward();
// Create new textures
UnloadTextures();
// Make sure all UTextures are loaded and contain Resource->TextureRHI
bool bWaitForRT = false;
for (int32 SplashLayerIndex = 0; SplashLayerIndex < SplashLayers.Num(); ++SplashLayerIndex)
{
FSplashLayer& SplashLayer = SplashLayers[SplashLayerIndex];
if (SplashLayer.Desc.TexturePath.IsValid())
{
// load temporary texture (if TexturePath was specified)
LoadTexture(SplashLayer);
}
if (SplashLayer.Desc.LoadingTexture && SplashLayer.Desc.LoadingTexture->IsValidLowLevel())
{
SplashLayer.Desc.LoadingTexture->UpdateResource();
bWaitForRT = true;
}
}
FlushRenderingCommands();
for (int32 SplashLayerIndex = 0; SplashLayerIndex < SplashLayers.Num(); ++SplashLayerIndex)
{
FSplashLayer& SplashLayer = SplashLayers[SplashLayerIndex];
//@DBG BEGIN
if (SplashLayer.Desc.LoadingTexture && SplashLayer.Desc.LoadingTexture->IsValidLowLevel())
{
if (SplashLayer.Desc.LoadingTexture->GetResource() && SplashLayer.Desc.LoadingTexture->GetResource()->TextureRHI)
{
SplashLayer.Desc.LoadedTexture = SplashLayer.Desc.LoadingTexture->GetResource()->TextureRHI;
}
else
{
UE_LOG(LogHMD, Warning, TEXT("Splash, %s - no Resource"), *SplashLayer.Desc.LoadingTexture->GetDesc());
}
}
//@DBG END
if (SplashLayer.Desc.LoadedTexture)
{
SplashLayer.Layer = MakeShareable(new FLayer(NextLayerId++));
SplashLayer.Layer->SetDesc(StereoLayerDescFromOculusSplashDesc(SplashLayer.Desc));
}
}
{
// add oculus-generated layers through the OculusVR settings area
FScopeLock ScopeLock(&RenderThreadLock);
Layers_RenderThread_DeltaRotation.Reset();
Layers_RenderThread_Input.Reset();
for (int32 SplashLayerIndex = 0; SplashLayerIndex < SplashLayers.Num(); SplashLayerIndex++)
{
const FSplashLayer& SplashLayer = SplashLayers[SplashLayerIndex];
if (SplashLayer.Layer.IsValid())
{
FLayerPtr ClonedLayer = SplashLayer.Layer->Clone();
Layers_RenderThread_Input.Add(ClonedLayer);
// Register layers that need to be rotated every n ticks
if (!SplashLayer.Desc.DeltaRotation.Equals(FQuat::Identity))
{
Layers_RenderThread_DeltaRotation.Emplace(ClonedLayer, SplashLayer.Desc.DeltaRotation);
}
}
}
// add UE VR splash screen
FOculusXRSplashDesc UESplashDesc = OculusXRHMD->GetUESplashScreenDesc();
if (UESplashDesc.LoadedTexture != nullptr)
{
UELayer.Reset();
UELayer = MakeShareable(new FLayer(NextLayerId++));
UELayer->SetDesc(StereoLayerDescFromOculusSplashDesc(UESplashDesc));
Layers_RenderThread_Input.Add(UELayer->Clone());
}
Layers_RenderThread_Input.Sort(FLayerPtr_CompareId());
}
if (Layers_RenderThread_Input.Num() > 0)
{
// If no textures are loaded, this will push black frame
StartTicker();
bIsShown = true;
UE_LOG(LogHMD, Log, TEXT("FSplash::DoShow"));
}
else
{
UE_LOG(LogHMD, Log, TEXT("No splash layers in FSplash::DoShow"));
}
}
void FSplash::DoHide()
{
CheckInGameThread();
UE_LOG(LogHMD, Log, TEXT("FSplash::DoHide"));
bIsShown = false;
StopTicker();
}
void FSplash::UpdateLoadingScreen_GameThread()
{
if (bNeedSplashUpdate)
{
if (bShouldShowSplash)
{
DoShow();
}
else
{
DoHide();
}
bNeedSplashUpdate = false;
}
}
void FSplash::ShowLoadingScreen()
{
bShouldShowSplash = true;
// DoShow will be called from UpdateSplashScreen_Gamethread().
// This can can happen if the splashes are already being shown, as it will reset the relative positions and delta rotations of the layers.
bNeedSplashUpdate = true;
}
void FSplash::HideLoadingScreen()
{
bShouldShowSplash = false;
bNeedSplashUpdate = bIsShown; // no need to call DoHide when the splash is already hidden
}
void FSplash::UnloadTextures()
{
CheckInGameThread();
// unload temporary loaded textures
FScopeLock ScopeLock(&RenderThreadLock);
for (int32 SplashLayerIndex = 0; SplashLayerIndex < SplashLayers.Num(); ++SplashLayerIndex)
{
if (SplashLayers[SplashLayerIndex].Desc.TexturePath.IsValid())
{
UnloadTexture(SplashLayers[SplashLayerIndex]);
}
}
}
void FSplash::LoadTexture(FSplashLayer& InSplashLayer)
{
CheckInGameThread();
UnloadTexture(InSplashLayer);
UE_LOG(LogLoadingSplash, Log, TEXT("Loading texture for splash %s..."), *InSplashLayer.Desc.TexturePath.GetAssetName());
InSplashLayer.Desc.LoadingTexture = Cast<UTexture>(InSplashLayer.Desc.TexturePath.TryLoad());
if (InSplashLayer.Desc.LoadingTexture != nullptr)
{
UE_LOG(LogLoadingSplash, Log, TEXT("...Success. "));
}
InSplashLayer.Desc.LoadedTexture = nullptr;
InSplashLayer.Layer.Reset();
}
void FSplash::UnloadTexture(FSplashLayer& InSplashLayer)
{
CheckInGameThread();
InSplashLayer.Desc.LoadingTexture = nullptr;
InSplashLayer.Desc.LoadedTexture = nullptr;
InSplashLayer.Layer.Reset();
}
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,146 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#include "IXRLoadingScreen.h"
#if WITH_EDITOR
#include "Editor.h"
#endif
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMD_GameFrame.h"
#include "OculusXRHMD_Layer.h"
#include "TickableObjectRenderThread.h"
#include "OculusXRHMDTypes.h"
namespace OculusXRHMD
{
class FOculusXRHMD;
//-------------------------------------------------------------------------------------------------
// FSplashLayer
//-------------------------------------------------------------------------------------------------
struct FSplashLayer
{
FOculusXRSplashDesc Desc;
FLayerPtr Layer;
public:
FSplashLayer(const FOculusXRSplashDesc& InDesc)
: Desc(InDesc) {}
FSplashLayer(const FSplashLayer& InSplashLayer)
: Desc(InSplashLayer.Desc), Layer(InSplashLayer.Layer) {}
};
//-------------------------------------------------------------------------------------------------
// FSplash
//-------------------------------------------------------------------------------------------------
class FSplash : public IXRLoadingScreen, public TSharedFromThis<FSplash>
{
protected:
class FTicker : public FTickableObjectRenderThread, public TSharedFromThis<FTicker>
{
public:
FTicker(FSplash* InSplash)
: FTickableObjectRenderThread(false, true), pSplash(InSplash) {}
virtual void Tick(float DeltaTime) override { pSplash->Tick_RenderThread(DeltaTime); }
virtual TStatId GetStatId() const override { RETURN_QUICK_DECLARE_CYCLE_STAT(FSplash, STATGROUP_Tickables); }
virtual bool IsTickable() const override { return true; }
protected:
FSplash* pSplash;
};
public:
FSplash(FOculusXRHMD* InPlugin);
virtual ~FSplash();
void Tick_RenderThread(float DeltaTime);
void Startup();
void LoadSettings();
void ReleaseResources_RHIThread();
void PreShutdown();
void Shutdown();
void OnPreLoadMap(const FString&);
void OnPostLoadMap(UWorld* LoadedWorld);
#if WITH_EDITOR
void OnPieBegin(bool bIsSimulating);
#endif
// Called from FOculusXRHMD
void UpdateLoadingScreen_GameThread();
// Internal extended API
int AddSplash(const FOculusXRSplashDesc&);
bool GetSplash(unsigned index, FOculusXRSplashDesc& OutDesc);
void StopTicker();
void StartTicker();
// The standard IXRLoadingScreen interface
virtual void ShowLoadingScreen() override;
virtual void HideLoadingScreen() override;
virtual void ClearSplashes() override;
virtual void AddSplash(const FSplashDesc& Splash) override;
virtual bool IsShown() const override { return bIsShown; }
#if !UE_VERSION_OLDER_THAN(5, 3, 0)
virtual bool IsPlayingLoadingMovie() const override
{
return false;
}
#endif
protected:
void DoShow();
void DoHide();
void UnloadTextures();
void LoadTexture(FSplashLayer& InSplashLayer);
void UnloadTexture(FSplashLayer& InSplashLayer);
void RenderFrame_RenderThread(FRHICommandListImmediate& RHICmdList);
IStereoLayers::FLayerDesc StereoLayerDescFromOculusSplashDesc(FOculusXRSplashDesc OculusDesc);
protected:
FOculusXRHMD* OculusXRHMD;
FCustomPresent* CustomPresent;
TSharedPtr<FTicker> Ticker;
int32 FramesOutstanding;
FCriticalSection RenderThreadLock;
FSettingsPtr Settings;
FGameFramePtr Frame;
TArray<FSplashLayer> SplashLayers;
uint32 NextLayerId;
FLayerPtr BlackLayer;
FLayerPtr UELayer;
TArray<TTuple<FLayerPtr, FQuat>> Layers_RenderThread_DeltaRotation;
TArray<FLayerPtr> Layers_RenderThread_Input;
TArray<FLayerPtr> Layers_RenderThread;
TArray<FLayerPtr> Layers_RHIThread;
// All these flags are only modified from the Game thread
bool bInitialized;
bool bIsShown;
bool bNeedSplashUpdate;
bool bShouldShowSplash;
float SystemDisplayInterval;
double LastTimeInSeconds;
FDelegateHandle PreLoadLevelDelegate;
FDelegateHandle PostLoadLevelDelegate;
#if WITH_EDITOR
FDelegateHandle PieBeginDelegateHandle;
#endif
};
typedef TSharedPtr<FSplash> FSplashPtr;
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,309 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_StressTester.h"
#if OCULUS_STRESS_TESTS_ENABLED
#include "OculusXRHMD.h"
#include "GlobalShader.h"
#include "UniformBuffer.h"
#include "RHICommandList.h"
#include "ShaderParameterUtils.h"
#include "RHIStaticStates.h"
#include "PipelineStateCache.h"
#include "OculusShaders.h"
#include "SceneUtils.h" // for SCOPED_DRAW_EVENT()
DECLARE_STATS_GROUP(TEXT("Oculus"), STATGROUP_Oculus, STATCAT_Advanced);
DECLARE_CYCLE_STAT(TEXT("GPUStressRendering"), STAT_GPUStressRendering, STATGROUP_Oculus);
//-------------------------------------------------------------------------------------------------
// Uniform buffers
//-------------------------------------------------------------------------------------------------
// This buffer should contain variables that never, or rarely change
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FOculusPixelShaderConstantParameters, )
// SHADER_PARAMETER(FVector4, Name)
END_GLOBAL_SHADER_PARAMETER_STRUCT()
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FOculusPixelShaderConstantParameters, "PSConstants");
typedef TUniformBufferRef<FOculusPixelShaderConstantParameters> FOculusPixelShaderConstantParametersRef;
// This buffer is for variables that change very often (each frame for example)
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FOculusPixelShaderVariableParameters, )
SHADER_PARAMETER(int, IterationsMultiplier)
END_GLOBAL_SHADER_PARAMETER_STRUCT()
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FOculusPixelShaderVariableParameters, "PSVariables");
typedef TUniformBufferRef<FOculusPixelShaderVariableParameters> FOculusPixelShaderVariableParametersRef;
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FTextureVertexDeclaration
//-------------------------------------------------------------------------------------------------
struct FTextureVertex
{
FVector4 Position;
FVector2f UV;
};
class FTextureVertexDeclaration : public FRenderResource
{
public:
FVertexDeclarationRHIRef VertexDeclarationRHI;
#if UE_VERSION_OLDER_THAN(5, 3, 0)
virtual void InitRHI() override
#else
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
#endif
{
FVertexDeclarationElementList Elements;
uint32 Stride = sizeof(FTextureVertex);
Elements.Add(FVertexElement(0, STRUCT_OFFSET(FTextureVertex, Position), VET_Float4, 0, Stride));
Elements.Add(FVertexElement(0, STRUCT_OFFSET(FTextureVertex, UV), VET_Float2, 1, Stride));
VertexDeclarationRHI = PipelineStateCache::GetOrCreateVertexDeclaration(Elements);
}
virtual void ReleaseRHI() override
{
VertexDeclarationRHI.SafeRelease();
}
};
static TGlobalResource<FTextureVertexDeclaration> GOculusTextureVertexDeclaration;
//-------------------------------------------------------------------------------------------------
// FStressTester
//-------------------------------------------------------------------------------------------------
TSharedPtr<FStressTester, ESPMode::ThreadSafe> FStressTester::SharedInstance;
TSharedRef<class FStressTester, ESPMode::ThreadSafe> FStressTester::Get()
{
CheckInGameThread();
if (!SharedInstance.IsValid())
{
SharedInstance = TSharedPtr<class FStressTester, ESPMode::ThreadSafe>(new FStressTester());
check(SharedInstance.IsValid());
}
return SharedInstance.ToSharedRef();
}
FStressTester::FStressTester()
: Mode(STM_None)
, CPUSpinOffInSeconds(0.011 / 3.) // one third of the frame (default value)
, PDsTimeLimitInSeconds(10.) // 10 secs
, CPUsTimeLimitInSeconds(10.) // 10 secs
, GPUsTimeLimitInSeconds(10.) // 10 secs
, GPUIterationsMultiplier(0.)
, CPUStartTimeInSeconds(0.)
, GPUStartTimeInSeconds(0.)
, PDStartTimeInSeconds(0.)
{
}
// multiple masks could be set, see EStressTestMode
void FStressTester::SetStressMode(uint32 InStressMask)
{
check((InStressMask & (~STM__All)) == 0);
Mode = InStressMask;
for (uint32 m = 1; m < STM__All; m <<= 1)
{
if (InStressMask & m)
{
switch (m)
{
case STM_EyeBufferRealloc:
UE_LOG(LogHMD, Log, TEXT("PD of EyeBuffer stress test is started"));
break;
case STM_CPUSpin:
UE_LOG(LogHMD, Log, TEXT("CPU stress test is started"));
break;
case STM_GPU:
UE_LOG(LogHMD, Log, TEXT("GPU stress test is started"));
break;
}
}
}
}
void FStressTester::DoTickCPU_GameThread(FOculusXRHMD* pPlugin)
{
CheckInGameThread();
if (Mode & STM_EyeBufferRealloc)
{
// Change PixelDensity every frame within MinPixelDensity..MaxPixelDensity range
if (PDStartTimeInSeconds == 0.)
{
PDStartTimeInSeconds = FPlatformTime::Seconds();
}
else
{
const double Now = FPlatformTime::Seconds();
if (Now - PDStartTimeInSeconds >= PDsTimeLimitInSeconds)
{
PDStartTimeInSeconds = 0.;
Mode &= ~STM_EyeBufferRealloc;
UE_LOG(LogHMD, Log, TEXT("PD of EyeBuffer stress test is finished"));
}
}
const int divisor = int((MaxPixelDensity - MinPixelDensity) * 10.f);
float NewPD = float(uint64(FPlatformTime::Seconds() * 1000) % divisor) / 10.f + MinPixelDensity;
pPlugin->SetPixelDensity(NewPD);
}
if (Mode & STM_CPUSpin)
{
// Simulate heavy CPU load within specified time limits
if (CPUStartTimeInSeconds == 0.)
{
CPUStartTimeInSeconds = FPlatformTime::Seconds();
}
else
{
const double Now = FPlatformTime::Seconds();
if (Now - CPUStartTimeInSeconds >= CPUsTimeLimitInSeconds)
{
CPUStartTimeInSeconds = 0.;
Mode &= ~STM_CPUSpin;
UE_LOG(LogHMD, Log, TEXT("CPU stress test is finished"));
}
}
const double StartSeconds = FPlatformTime::Seconds();
int i, num = 1, primes = 0;
bool bFinish = false;
while (!bFinish)
{
i = 2;
while (i <= num)
{
if (num % i == 0)
{
break;
}
i++;
const double NowSeconds = FPlatformTime::Seconds();
if (NowSeconds - StartSeconds >= CPUSpinOffInSeconds)
{
bFinish = true;
}
}
if (i == num)
{
++primes;
}
++num;
}
}
if (Mode & STM_GPU)
{
// Simulate heavy CPU load within specified time limits
if (GPUStartTimeInSeconds == 0.)
{
GPUStartTimeInSeconds = FPlatformTime::Seconds();
}
else
{
const double Now = FPlatformTime::Seconds();
if (Now - GPUStartTimeInSeconds >= GPUsTimeLimitInSeconds)
{
GPUStartTimeInSeconds = 0.;
Mode &= ~STM_GPU;
UE_LOG(LogHMD, Log, TEXT("GPU stress test is finished"));
}
}
}
}
//-------------------------------------------------------------------------------------------------
// Console commands for managing the stress tester:
//-------------------------------------------------------------------------------------------------
static void StressGPUCmdHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar)
{
auto StressTester = FStressTester::Get();
StressTester->SetStressMode(FStressTester::STM_GPU | StressTester->GetStressMode());
if (Args.Num() > 0)
{
const int GpuMult = FCString::Atoi(*Args[0]);
StressTester->SetGPULoadMultiplier(GpuMult);
}
if (Args.Num() > 1)
{
const float GpuTimeLimit = FCString::Atof(*Args[1]);
StressTester->SetGPUsTimeLimitInSeconds(GpuTimeLimit);
}
}
static FAutoConsoleCommand CStressGPUCmd(
TEXT("vr.oculus.Stress.GPU"),
*NSLOCTEXT("OculusRift", "CCommandText_StressGPU", "Initiates a GPU stress test.\n Usage: vr.oculus.Stress.GPU [LoadMultiplier [TimeLimit]]").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic(StressGPUCmdHandler));
static void StressCPUCmdHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar)
{
auto StressTester = FStressTester::Get();
StressTester->SetStressMode(FStressTester::STM_CPUSpin | StressTester->GetStressMode());
if (Args.Num() > 0)
{
const float CpuLimit = FCString::Atof(*Args[0]);
StressTester->SetCPUSpinOffPerFrameInSeconds(CpuLimit);
}
if (Args.Num() > 1)
{
const float CpuTimeLimit = FCString::Atof(*Args[1]);
StressTester->SetCPUsTimeLimitInSeconds(CpuTimeLimit);
}
}
static FAutoConsoleCommand CStressCPUCmd(
TEXT("vr.oculus.Stress.CPU"),
*NSLOCTEXT("OculusRift", "CCommandText_StressCPU", "Initiates a CPU stress test.\n Usage: vr.oculus.Stress.CPU [PerFrameTime [TotalTimeLimit]]").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic(StressCPUCmdHandler));
static void StressPDCmdHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar)
{
auto StressTester = FStressTester::Get();
StressTester->SetStressMode(FStressTester::STM_EyeBufferRealloc | StressTester->GetStressMode());
if (Args.Num() > 0)
{
const float TimeLimit = FCString::Atof(*Args[0]);
StressTester->SetPDsTimeLimitInSeconds(TimeLimit);
}
}
static FAutoConsoleCommand CStressPDCmd(
TEXT("vr.oculus.Stress.PD"),
*NSLOCTEXT("OculusRift", "CCommandText_StressPD", "Initiates a pixel density stress test wher pixel density is changed every frame for TotalTimeLimit seconds.\n Usage: vr.oculus.Stress.PD [TotalTimeLimit]").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic(StressPDCmdHandler));
static void StressResetCmdHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar)
{
auto StressTester = FStressTester::Get();
StressTester->SetStressMode(0);
}
static FAutoConsoleCommand CStressResetCmd(
TEXT("vr.oculus.Stress.Reset"),
*NSLOCTEXT("OculusRift", "CCommandText_StressReset", "Resets the stress tester and stops all currently running stress tests.\n Usage: vr.oculus.Stress.Reset").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic(StressResetCmdHandler));
} // namespace OculusXRHMD
#endif // #if OCULUS_STRESS_TESTS_ENABLED

View File

@@ -0,0 +1,91 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#define OCULUS_STRESS_TESTS_ENABLED (OCULUS_HMD_SUPPORTED_PLATFORMS && !UE_BUILD_SHIPPING && !PLATFORM_ANDROID)
#if OCULUS_STRESS_TESTS_ENABLED
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FStressTester
//-------------------------------------------------------------------------------------------------
class FStressTester
{
public:
const float MinPixelDensity = 0.4f;
const float MaxPixelDensity = 2.0f;
enum EStressTestMode
{
STM_None,
STM_EyeBufferRealloc = 0x01,
STM_CPUSpin = 0x02,
STM_GPU = 0x04,
STM__All = ((STM_GPU << 1) - 1)
};
// multiple masks could be set, see EStressTestMode
void SetStressMode(uint32 InStressMask);
uint32 GetStressMode() const { return Mode; }
// sets limits for CPUSpin mode, per frame
void SetCPUSpinOffPerFrameInSeconds(double InCPUSpinOffInSeconds) { CPUSpinOffInSeconds = InCPUSpinOffInSeconds; }
// set GPU load multiplier
// if IterationsMultiplier is 0 then the multiplier will be randomly changed in 1..20 range.
// the bigger the multiplier the longer it takes GPU to draw the quad.
void SetGPULoadMultiplier(int IterationsMultiplier) { GPUIterationsMultiplier = IterationsMultiplier; }
// sets time limit for STM_EyeBufferRealloc mode; 0 - unlimited
void SetPDsTimeLimitInSeconds(double InSeconds) { PDsTimeLimitInSeconds = InSeconds; }
// sets time limit for STM_CPUSpin mode; 0 - unlimited
void SetCPUsTimeLimitInSeconds(double InSeconds) { CPUsTimeLimitInSeconds = InSeconds; }
// sets time limit for STM_GPU mode; 0 - unlimited
void SetGPUsTimeLimitInSeconds(double InSeconds) { GPUsTimeLimitInSeconds = InSeconds; }
static TSharedRef<class FStressTester, ESPMode::ThreadSafe> Get();
static void TickCPU_GameThread(class FOculusXRHMD* pPlugin)
{
CheckInGameThread();
if (SharedInstance.IsValid())
{
SharedInstance->DoTickCPU_GameThread(pPlugin);
}
}
protected:
void DoTickCPU_GameThread(class FOculusXRHMD* pPlugin);
FStressTester();
uint32 Mode; // bit mask, see EStressTestMode
double CPUSpinOffInSeconds; // limit of additional CPU load per frame, STM_CPUSpin
double PDsTimeLimitInSeconds; // time limit for STM_EyeBufferRealloc mode; 0 - unlimited
double CPUsTimeLimitInSeconds; // time limit for STM_CPUSpin mode; 0 - unlimited
double GPUsTimeLimitInSeconds; // time limit for STM_GPU mode; 0 - unlimited
// the higher multiplier the longer it takes GPU to draw
int GPUIterationsMultiplier; // if 0 - then it is dynamically changed.
double CPUStartTimeInSeconds;
double GPUStartTimeInSeconds;
double PDStartTimeInSeconds;
static TSharedPtr<class FStressTester, ESPMode::ThreadSafe> SharedInstance;
};
} // namespace OculusXRHMD
#endif // #if OCULUS_STRESS_TESTS_ENABLED

View File

@@ -0,0 +1,120 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_VulkanExtensions.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
#include "OculusXRHMDPrivateRHI.h"
#include "OculusXRHMDModule.h"
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FVulkanExtensions
//-------------------------------------------------------------------------------------------------
bool FVulkanExtensions::GetVulkanInstanceExtensionsRequired(TArray<const ANSICHAR*>& Out)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN
// TArray<VkExtensionProperties> Properties = GetIVulkanDynamicRHI()->RHIGetAllInstanceExtensions();
TArray<const char*> Extensions;
{
int32 ExtensionCount = 0;
FOculusXRHMDModule::GetPluginWrapper().GetInstanceExtensionsVk(nullptr, &ExtensionCount);
Extensions.SetNum(ExtensionCount);
FOculusXRHMDModule::GetPluginWrapper().GetInstanceExtensionsVk(Extensions.GetData(), &ExtensionCount);
}
// int32 ExtensionsFound = 0;
for (int32 ExtensionIndex = 0; ExtensionIndex < Extensions.Num(); ExtensionIndex++)
{
// for (int32 PropertyIndex = 0; PropertyIndex < Properties.Num(); PropertyIndex++)
{
// const char* PropertyExtensionName = Properties[PropertyIndex].extensionName;
// if (!FCStringAnsi::Strcmp(PropertyExtensionName, Extensions[ExtensionIndex]))
{
Out.Add(Extensions[ExtensionIndex]);
// ExtensionsFound++;
// break;
}
}
}
return true;
// return ExtensionsFound == Extensions.Num();
#endif
return true;
}
bool FVulkanExtensions::GetVulkanDeviceExtensionsRequired(struct VkPhysicalDevice_T* pPhysicalDevice, TArray<const ANSICHAR*>& Out)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN
// TArray<VkExtensionProperties> Properties = GetIVulkanDynamicRHI()->RHIGetAllDeviceExtensions((VkPhysicalDevice)pPhysicalDevice);
TArray<const char*> Extensions;
{
int32 ExtensionCount = 0;
FOculusXRHMDModule::GetPluginWrapper().GetDeviceExtensionsVk(nullptr, &ExtensionCount);
Extensions.SetNum(ExtensionCount);
FOculusXRHMDModule::GetPluginWrapper().GetDeviceExtensionsVk(Extensions.GetData(), &ExtensionCount);
}
// int32 ExtensionsFound = 0;
for (int32 ExtensionIndex = 0; ExtensionIndex < Extensions.Num(); ExtensionIndex++)
{
// for (int32 PropertyIndex = 0; PropertyIndex < Properties.Num(); PropertyIndex++)
{
// const char* PropertyExtensionName = Properties[PropertyIndex].extensionName;
// if (!FCStringAnsi::Strcmp(PropertyExtensionName, Extensions[ExtensionIndex]))
{
Out.Add(Extensions[ExtensionIndex]);
// ExtensionsFound++;
// break;
}
}
}
return true;
// return ExtensionsFound == Extensions.Num();
#endif
return true;
}
#if WITH_EDITOR
bool FEditorVulkanExtensions::GetVulkanInstanceExtensionsRequired(TArray<const ANSICHAR*>& Out)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN && PLATFORM_WINDOWS
Out.Append({ "VK_KHR_surface",
"VK_KHR_external_memory_capabilities",
"VK_KHR_win32_surface",
"VK_KHR_external_fence_capabilities",
"VK_KHR_external_semaphore_capabilities",
"VK_KHR_get_physical_device_properties2" });
#endif
return true;
}
bool FEditorVulkanExtensions::GetVulkanDeviceExtensionsRequired(struct VkPhysicalDevice_T* pPhysicalDevice, TArray<const ANSICHAR*>& Out)
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS_VULKAN && PLATFORM_WINDOWS
Out.Append({ "VK_KHR_swapchain",
"VK_KHR_external_memory",
"VK_KHR_external_memory_win32",
"VK_KHR_external_fence",
"VK_KHR_external_fence_win32",
"VK_KHR_external_semaphore",
"VK_KHR_external_semaphore_win32",
"VK_KHR_get_memory_requirements2",
"VK_KHR_dedicated_allocation" });
#endif
return true;
}
#endif
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,43 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#include "IHeadMountedDisplayVulkanExtensions.h"
#if OCULUS_HMD_SUPPORTED_PLATFORMS
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FVulkanExtensions
//-------------------------------------------------------------------------------------------------
class FVulkanExtensions : public IHeadMountedDisplayVulkanExtensions, public TSharedFromThis<FVulkanExtensions, ESPMode::ThreadSafe>
{
public:
FVulkanExtensions() {}
virtual ~FVulkanExtensions() {}
// IHeadMountedDisplayVulkanExtensions
virtual bool GetVulkanInstanceExtensionsRequired(TArray<const ANSICHAR*>& Out) override;
virtual bool GetVulkanDeviceExtensionsRequired(struct VkPhysicalDevice_T* pPhysicalDevice, TArray<const ANSICHAR*>& Out) override;
};
#if WITH_EDITOR
class FEditorVulkanExtensions : public IHeadMountedDisplayVulkanExtensions, public TSharedFromThis<FEditorVulkanExtensions, ESPMode::ThreadSafe>
{
public:
FEditorVulkanExtensions() {}
virtual ~FEditorVulkanExtensions() {}
// IHeadMountedDisplayVulkanExtensions
virtual bool GetVulkanInstanceExtensionsRequired(TArray<const ANSICHAR*>& Out) override;
virtual bool GetVulkanDeviceExtensionsRequired(struct VkPhysicalDevice_T* pPhysicalDevice, TArray<const ANSICHAR*>& Out) override;
};
#endif
} // namespace OculusXRHMD
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS

View File

@@ -0,0 +1,379 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRMultiPlayerTestingSubsystem.h"
#include "IHeadMountedDisplay.h"
#include "IXRTrackingSystem.h"
#include "Widgets/SViewport.h"
#include "Slate/SceneViewport.h"
#include "OculusXRHMDModule.h"
#include "OculusXRHMDRuntimeSettings.h"
#include "OculusXRHMD.h"
#if WITH_EDITOR
#include "Editor/UnrealEd/Classes/Editor/EditorEngine.h"
#include "Settings/LevelEditorPlaySettings.h"
#endif
#if PLATFORM_WINDOWS
#include "Windows/WindowsPlatformProcess.h"
#include <TlHelp32.h>
#endif
/**
* Initialize the subsystem. USubsystem override
*/
void UOculusXRMultiPlayerTestingSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
}
/**
* De-initializes the subsystem. USubsystem override
*/
void UOculusXRMultiPlayerTestingSubsystem::Deinitialize()
{
Super::Deinitialize();
}
ETickableTickType UOculusXRMultiPlayerTestingSubsystem::GetTickableTickType() const
{
return IsMultiPlayerTestingSupported() ? ETickableTickType::Always : ETickableTickType::Never;
}
bool UOculusXRMultiPlayerTestingSubsystem::IsAllowedToTick() const
{
return IsMultiPlayerTestingSupported();
}
void UOculusXRMultiPlayerTestingSubsystem::Tick(float)
{
#if PLATFORM_WINDOWS && WITH_EDITOR
bool bSingleProcessMode = true;
if (!IsMultiPlayerTestingEnabled(bSingleProcessMode))
{
return;
}
if (GEngine)
{
const ULevelEditorPlaySettings* PlayInSettings = GetDefault<ULevelEditorPlaySettings>();
bool RunUnderOneProcess = true;
PlayInSettings->GetRunUnderOneProcess(RunUnderOneProcess);
if (GetMutableDefault<UOculusXRHMDRuntimeSettings>()->bSetActivePIEToPrimary && RunUnderOneProcess)
{
SwitchPrimaryPIE_SingleProcess();
}
}
#endif
}
bool UOculusXRMultiPlayerTestingSubsystem::IsMultiPlayerTestingSupported() const
{
#if PLATFORM_WINDOWS && WITH_EDITOR
return true;
#else
return false;
#endif
}
#if PLATFORM_WINDOWS
bool UOculusXRMultiPlayerTestingSubsystem::IsMultiPlayerTestingEnabled(bool& bSingleProcessMode)
{
bSingleProcessMode = false;
NumClients = 1;
// ULevelEditorPlaySettings of 2nd+ player of MultipleProcess mode does NOT have correct GetRunUnderOneProcess() data.
// Let's use InInstanceNum to figure out how many clients are enabled already.
int InInstanceNum = 0;
FParse::Value(FCommandLine::Get(), TEXT("InInstanceNum="), InInstanceNum);
if (InInstanceNum)
{
bSingleProcessMode = false;
NumClients = InInstanceNum + 1;
}
#if WITH_EDITOR
else
{
// SingleProcess mode OR 1st player of MultipleProcess mode are kicked off from editor
const ULevelEditorPlaySettings* PlayInSettings = GetDefault<ULevelEditorPlaySettings>();
if (PlayInSettings)
{
PlayInSettings->GetPlayNumberOfClients(NumClients);
PlayInSettings->GetRunUnderOneProcess(bSingleProcessMode);
}
}
#endif // WITH_EDITOR
#ifdef WITH_OCULUS_BRANCH
return (NumClients > 1
&& GEngine
&& GEngine->XRSystem.IsValid()
&& GEngine->XRSystem->GetStereoRenderingDevice().IsValid()
&& GEngine->XRSystem->GetStereoRenderingDevice()->IsStereoEnabled());
#else
return (NumClients > 1 && OculusXRHMD::FOculusXRHMD::GetOculusXRHMD() && OculusXRHMD::FOculusXRHMD::GetOculusXRHMD()->IsStereoEnabled());
#endif
}
static void SwitchPrimaryPIE(const TArray<FString>& Args, UWorld*, FOutputDevice& Ar)
{
if (!GEngine || !GEngine->XRSystem.IsValid())
{
Ar.Logf(TEXT("XRSystem must be valid to switch PrimaryPIE!"));
return;
}
if (!GetMutableDefault<UOculusXRHMDRuntimeSettings>()->bSetCVarPIEToPrimary)
{
Ar.Logf(TEXT("Please enable \"Set CVar PIE To Primary\" in MetaXRPlugin settings!"));
return;
}
if (Args.Num())
{
if (UOculusXRMultiPlayerTestingSubsystem* MPTSSubsystem = GEngine ? GEngine->GetEngineSubsystem<UOculusXRMultiPlayerTestingSubsystem>() : nullptr)
{
MPTSSubsystem->SwitchPrimaryPIE(nullptr, FCString::Atoi(*Args[0]));
}
}
else
{
Ar.Logf(TEXT("Invalid PrimaryPIEIndex!"));
}
}
const int DefaultNextProcIndex = -1;
static FAutoConsoleCommand CSwitchHMDCmd(
TEXT("vr.PrimaryPIEIndex"),
TEXT("Set primary PIE index on the fly. Setting it to -1 is to set the index to next PIE window. If RunUnderOneProcess is enabled, this index can be set to any PIE index."),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic(SwitchPrimaryPIE));
void UOculusXRMultiPlayerTestingSubsystem::SwitchPrimaryPIE(UGameViewportClient* InViewport, TOptional<int> PrimaryPIEIndex)
{
bool bSingleProcessMode = true;
if (!IsMultiPlayerTestingEnabled(bSingleProcessMode))
{
return;
}
if (!bSingleProcessMode)
{
check(PrimaryPIEIndex.IsSet());
SwitchPrimaryPIE_MultiProcess(PrimaryPIEIndex.GetValue());
}
#if WITH_EDITOR
else
{
SwitchPrimaryPIE_SingleProcess(InViewport, PrimaryPIEIndex);
}
#endif
}
void UOculusXRMultiPlayerTestingSubsystem::GetUnrealEditorProcs(TArray<int32>& ProcIds)
{
FString ProcNameWithExtension = "UnrealEditor.exe";
HANDLE SnapShot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (SnapShot != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32 Entry;
Entry.dwSize = sizeof(PROCESSENTRY32);
const int ProjectNameMaxLength = 512;
if (::Process32First(SnapShot, &Entry))
{
do
{
// find unreal editor processes
if (FCString::Stricmp(*ProcNameWithExtension, Entry.szExeFile) == 0)
{
int32 ProcID = Entry.th32ProcessID;
HWND HWnd = FWindowsPlatformMisc::GetTopLevelWindowHandle(ProcID);
if (HWnd)
{
const TCHAR* ProjectName = FApp::GetProjectName();
WCHAR Buffer[ProjectNameMaxLength];
GetWindowText(HWnd, Buffer, ProjectNameMaxLength);
size_t ProjectNameLen = _tcslen(ProjectName);
if (_tcsnccmp(ProjectName, Buffer, ProjectNameLen) == 0)
{
ProcIds.Add(ProcID);
}
}
}
}
while (::Process32Next(SnapShot, &Entry));
}
::CloseHandle(SnapShot);
}
ProcIds.Sort(TLess<int32>());
}
// Activate PIE window across processes.
void UOculusXRMultiPlayerTestingSubsystem::SwitchPrimaryPIE_MultiProcess(int PrimaryPIEIndex)
{
uint32 CurProcId = FPlatformProcess::GetCurrentProcessId();
uint32 DestProcId = -1;
TArray<int32> ProcIds;
GetUnrealEditorProcs(ProcIds);
// For now, PrimaryPIEIndex must be -1 which means we always switch to next player.
// The reason is users don't know each process' index and cannot easily set it via vr.PrimaryPIEIndex even if we sort processIDs first.
if (PrimaryPIEIndex == DefaultNextProcIndex)
{ // find next process's Id
int32 CurProcIdx = ProcIds.Find(CurProcId);
check(CurProcIdx != INDEX_NONE);
int32 NextProcIdx = (CurProcIdx + 1) % ProcIds.Num();
DestProcId = ProcIds[NextProcIdx];
}
else
{
UE_LOG(LogHMD, Warning, TEXT("vr.PrimaryPIEIndex must be -1 if RunUnderOneProcess is not set."));
return;
}
if (DestProcId == -1)
{
UE_LOG(LogHMD, Warning, TEXT("Cannot find vr.PrimaryPIEIndex: %d"), PrimaryPIEIndex);
return;
}
else
{
UE_LOG(LogHMD, Log, TEXT("SwitchPrimaryPIE from ProcID: %d to: %d"), CurProcId, DestProcId);
HWND MainWindowHandle = FWindowsPlatformMisc::GetTopLevelWindowHandle(DestProcId);
::SwitchToThisWindow(MainWindowHandle, true);
}
}
#if WITH_EDITOR
void UOculusXRMultiPlayerTestingSubsystem::SwitchPrimaryPIE_SingleProcess(UGameViewportClient* InViewport, TOptional<int> PrimaryPIEIndex)
{
if (InViewport && PrimaryPIEIndex.IsSet())
{
UE_LOG(LogHMD, Warning, TEXT("InViewport and PrimaryPIEIndex should not be set at the same time."));
return;
}
if (!GEditor)
{
UE_LOG(LogHMD, Warning, TEXT("SwitchPrimaryPIE_SingleProcess is only supported in editor."));
return;
}
if (PrimaryPIEIndex == DefaultNextProcIndex)
{ // Activate the world next to the current primary one
for (const FWorldContext& WorldContext : GEditor->GetWorldContexts())
{
if (WorldContext.bIsPrimaryPIEInstance)
{
PrimaryPIEIndex = (WorldContext.PIEInstance + 1) % NumClients;
}
}
}
FWorldContext* OldPrimaryWorld = nullptr;
FWorldContext* NewPrimaryWorld = nullptr;
for (const FWorldContext& WorldContext : GEditor->GetWorldContexts())
{
if (WorldContext.bIsPrimaryPIEInstance)
{
OldPrimaryWorld = GEditor->GetWorldContextFromWorld(WorldContext.World());
}
else if (WorldContext.GameViewport == InViewport || (PrimaryPIEIndex.IsSet() && WorldContext.PIEInstance == PrimaryPIEIndex.GetValue()))
{
NewPrimaryWorld = GEditor->GetWorldContextFromWorld(WorldContext.World());
}
}
if (OldPrimaryWorld && NewPrimaryWorld)
{
SwitchPrimaryPIE(OldPrimaryWorld, NewPrimaryWorld);
}
else
{
UE_LOG(LogHMD, Warning, TEXT("Failed to switch primary PIE."));
}
}
void UOculusXRMultiPlayerTestingSubsystem::SwitchPrimaryPIE_SingleProcess()
{
NumClients = 0;
const ULevelEditorPlaySettings* PlayInSettings = GetDefault<ULevelEditorPlaySettings>();
if (PlayInSettings)
{
PlayInSettings->GetPlayNumberOfClients(NumClients);
}
if (!(GEditor && GEditor->GetPlayInEditorSessionInfo().IsSet() && GEditor->GetPlayInEditorSessionInfo()->NumClientInstancesCreated == NumClients))
{
return;
}
FWorldContext* OldPrimaryWorld = nullptr;
FWorldContext* NewPrimaryWorld = nullptr;
for (const FWorldContext& WorldContext : GEditor->GetWorldContexts())
{
if (!WorldContext.GameViewport || !WorldContext.GameViewport->GetGameViewport())
{
UE_LOG(LogHMD, Warning, TEXT("No GameViewport or SceneViewPort for the current world %d."), WorldContext.PIEInstance);
continue;
}
const FSceneViewport* SceneViewPort = WorldContext.GameViewport->GetGameViewport();
if (WorldContext.bIsPrimaryPIEInstance && !SceneViewPort->HasFocus())
{
OldPrimaryWorld = GEditor->GetWorldContextFromWorld(WorldContext.World());
}
else if (!WorldContext.bIsPrimaryPIEInstance && SceneViewPort->HasFocus())
{
NewPrimaryWorld = GEditor->GetWorldContextFromWorld(WorldContext.World());
}
}
if (OldPrimaryWorld && NewPrimaryWorld)
{
UE_LOG(LogHMD, Log, TEXT("SwitchPrimaryPIE from %d to %d."), OldPrimaryWorld->PIEInstance, NewPrimaryWorld->PIEInstance);
SwitchPrimaryPIE(OldPrimaryWorld, NewPrimaryWorld);
}
}
void UOculusXRMultiPlayerTestingSubsystem::SwitchPrimaryPIE(FWorldContext* OldPrimaryWorld, FWorldContext* NewPrimaryWorld)
{
auto TogglePrimaryWorld = [](FWorldContext* PrimaryWorld, bool bEnable) {
check(PrimaryWorld != nullptr);
FSceneViewport* SceneViewPort = PrimaryWorld->GameViewport->GetGameViewport();
PrimaryWorld->bIsPrimaryPIEInstance = bEnable;
SceneViewPort->GetViewportWidget().Pin()->EnableStereoRendering(bEnable);
SceneViewPort->GetViewportWidget().Pin()->SetRenderDirectlyToWindow(bEnable);
SceneViewPort->SetPlayInEditorGetsMouseControl(bEnable);
SceneViewPort->SetViewportSize(SceneViewPort->GetSizeXY().X, SceneViewPort->GetSizeXY().Y);
if (bEnable)
{
SceneViewPort->FindWindow()->GetNativeWindow()->SetWindowFocus();
}
else
{
SceneViewPort->FindWindow()->SetViewportSizeDrivenByWindow(true);
}
};
if (NewPrimaryWorld->GameViewport && NewPrimaryWorld->GameViewport->GetGameViewport() && OldPrimaryWorld->GameViewport && OldPrimaryWorld->GameViewport->GetGameViewport())
{
TogglePrimaryWorld(NewPrimaryWorld, true);
TogglePrimaryWorld(OldPrimaryWorld, false);
// ATM, in Vanilla/Stock UE, no MultiPlayer support in OpenXR plugin.
// TODO: Remove below branch checking and refactor FOculusXRHMD to be the same with FOpenXRHMD once IOpenXRExtensionPlugin::ReCalcPose(...) is upstreamed.
#ifdef WITH_OCULUS_BRANCH
OculusXR::FMultiPlayerStateExtensionPlugin& MPPlugin = FOculusXRHMDModule::Get().GetExtensionPluginManager().GetMultiPlayerStateExtensionPlugin();
MPPlugin.SwitchPrimaryPIE(NewPrimaryWorld->PIEInstance);
#endif // WITH_OCULUS_BRANCH
if (OculusXRHMD::FOculusXRHMD::GetOculusXRHMD())
{
OculusXRHMD::FOculusXRHMD::GetOculusXRHMD()->SwitchPrimaryPIE(NewPrimaryWorld->PIEInstance);
}
}
}
#endif // WITH_EDITOR
#endif // PLATFORM_WINDOWS

View File

@@ -0,0 +1,158 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRPassthroughLayerShapes.h"
#include "OculusXRHMDPrivate.h"
#include "Curves/CurveLinearColor.h"
#include "OculusXRPluginWrapper.h"
#include "OpenXR/OculusXROpenXRUtilities.h"
const FName FReconstructedLayer::ShapeName = FName("ReconstructedLayer");
const FName FUserDefinedLayer::ShapeName = FName("UserDefinedLayer");
FColorLutDesc::FColorLutDesc()
: Weight(0)
, ColorLuts{}
{
}
FColorLutDesc::FColorLutDesc(const TArray<uint64>& InColorLuts, float InWeight)
: Weight(InWeight)
, ColorLuts(InColorLuts)
{
}
FEdgeStyleParameters::FEdgeStyleParameters()
: bEnableEdgeColor(false)
, bEnableColorMap(false)
, bUseColorLuts(false)
, TextureOpacityFactor(1.0f)
, EdgeColor{}
, ColorMapType{}
, ColorMapData{}
, ColorLutDesc{} {
};
FEdgeStyleParameters::FEdgeStyleParameters(
bool bEnableEdgeColor,
bool bEnableColorMap,
float TextureOpacityFactor,
float Brightness,
float Contrast,
float Posterize,
float Saturation,
FLinearColor EdgeColor,
FLinearColor ColorScale,
FLinearColor ColorOffset,
EOculusXRColorMapType InColorMapType,
const TArray<FLinearColor>& InColorMapGradient,
const FColorLutDesc& InLutDesc)
: bEnableEdgeColor(bEnableEdgeColor)
, bEnableColorMap(bEnableColorMap)
, TextureOpacityFactor(TextureOpacityFactor)
, Brightness(Brightness)
, Contrast(Contrast)
, Posterize(Posterize)
, Saturation(Saturation)
, EdgeColor(EdgeColor)
, ColorScale(ColorScale)
, ColorOffset(ColorOffset)
, ColorMapType(InColorMapType)
, ColorLutDesc(InLutDesc)
{
bUseColorLuts = (InColorMapType == ColorMapType_ColorLut && InLutDesc.ColorLuts.Num() == 1)
|| (InColorMapType == ColorMapType_ColorLut_Interpolated && InLutDesc.ColorLuts.Num() == 2);
if ((InColorMapType == ColorMapType_ColorLut || InColorMapType == ColorMapType_ColorLut_Interpolated)
&& !bUseColorLuts)
{
ColorMapType = ColorMapType_None;
}
ColorMapData = GenerateColorMapData(InColorMapType, InColorMapGradient);
};
TArray<uint8> FEdgeStyleParameters::GenerateColorMapData(EOculusXRColorMapType InColorMapType, const TArray<FLinearColor>& InColorMapGradient)
{
switch (InColorMapType)
{
case ColorMapType_GrayscaleToColor:
{
TArray<uint8> NewColorMapData = GenerateMonoBrightnessContrastPosterizeMap();
return GenerateMonoToRGBA(InColorMapGradient, NewColorMapData);
}
case ColorMapType_Grayscale:
return GenerateMonoBrightnessContrastPosterizeMap();
case ColorMapType_ColorAdjustment:
return GenerateBrightnessContrastSaturationColorMap();
default:
return TArray<uint8>();
}
}
TArray<uint8> FEdgeStyleParameters::GenerateMonoToRGBA(const TArray<FLinearColor>& InColorMapGradient, const TArray<uint8>& InColorMapData)
{
TArray<uint8> NewColorMapData;
FInterpCurveLinearColor InterpCurve;
const uint32 TotalEntries = 256;
for (int32 Index = 0; Index < InColorMapGradient.Num(); ++Index)
{
InterpCurve.AddPoint(Index, (InColorMapGradient[Index] * ColorScale) + ColorOffset);
}
// XrColor4f and ovrpColorf need to be same size, as either will be sent to the OVR plugin or OpenXR implementation
static_assert(sizeof(XrColor4f) == sizeof(ovrpColorf));
const int32 ColorSize = sizeof(ovrpColorf);
NewColorMapData.SetNum(TotalEntries * ColorSize);
uint8* Dest = NewColorMapData.GetData();
for (int32 Index = 0; Index < TotalEntries; ++Index)
{
const ovrpColorf Color = OculusXRHMD::ToOvrpColorf(InterpCurve.Eval(InColorMapData[Index]));
FMemory::Memcpy(Dest, &Color, sizeof(Color));
Dest += ColorSize;
}
return NewColorMapData;
}
TArray<uint8> FEdgeStyleParameters::GenerateMonoBrightnessContrastPosterizeMap()
{
TArray<uint8> NewColorMapData;
const int32 TotalEntries = 256;
NewColorMapData.SetNum(TotalEntries * sizeof(uint8));
for (int32 Index = 0; Index < TotalEntries; ++Index)
{
float Alpha = ((float)Index / TotalEntries);
float ContrastFactor = Contrast + 1.0;
Alpha = (Alpha - 0.5) * ContrastFactor + 0.5 + Brightness;
if (Posterize > 0.0f)
{
const float PosterizationBase = 50.0f;
float FinalPosterize = (FMath::Pow(PosterizationBase, Posterize) - 1.0) / (PosterizationBase - 1.0);
Alpha = FMath::RoundToFloat(Alpha / FinalPosterize) * FinalPosterize;
}
NewColorMapData[Index] = (uint8)(FMath::Min(FMath::Max(Alpha, 0.0f), 1.0f) * 255.0f);
}
return NewColorMapData;
}
TArray<uint8> FEdgeStyleParameters::GenerateBrightnessContrastSaturationColorMap()
{
TArray<uint8> NewColorMapData;
NewColorMapData.SetNum(3 * sizeof(float));
float newB = Brightness * 100.0f;
float newC = Contrast + 1.0f;
float newS = Saturation + 1.0f;
uint8* Dest = NewColorMapData.GetData();
FMemory::Memcpy(Dest, &newB, sizeof(float));
Dest += sizeof(float);
FMemory::Memcpy(Dest, &newC, sizeof(float));
Dest += sizeof(float);
FMemory::Memcpy(Dest, &newS, sizeof(float));
return NewColorMapData;
}

View File

@@ -0,0 +1,503 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRPluginWrapper.h"
#include "OculusXRHMDModule.h"
#if PLATFORM_ANDROID
#include <dlfcn.h>
#define MIN_SDK_VERSION 29
#endif
DEFINE_LOG_CATEGORY(LogOculusPluginWrapper);
static void* LoadEntryPoint(void* handle, const char* EntryPointName);
bool OculusPluginWrapper::InitializeOculusPluginWrapper(OculusPluginWrapper* wrapper)
{
if (wrapper->IsInitialized())
{
UE_LOG(LogOculusPluginWrapper, Warning, TEXT("wrapper already initialized"));
return true;
}
#if OCULUS_HMD_SUPPORTED_PLATFORMS
void* LibraryHandle = nullptr;
#if PLATFORM_ANDROID
const bool VersionValid = FAndroidMisc::GetAndroidBuildVersion() >= MIN_SDK_VERSION;
#else
const bool VersionValid = true;
#endif
if (VersionValid)
{
LibraryHandle = FOculusXRHMDModule::GetOVRPluginHandle();
if (LibraryHandle == nullptr)
{
UE_LOG(LogOculusPluginWrapper, Warning, TEXT("GetOVRPluginHandle() returned NULL"));
return false;
}
}
else
{
return false;
}
#else
return false;
#endif
struct OculusEntryPoint
{
const char* EntryPointName;
void** EntryPointPtr;
};
#define OCULUS_BIND_ENTRY_POINT(Func) \
{ \
"ovrp_" #Func, (void**)&wrapper->Func \
}
OculusEntryPoint entryPointArray[] = {
// OVR_Plugin.h
OCULUS_BIND_ENTRY_POINT(PreInitialize5),
OCULUS_BIND_ENTRY_POINT(GetInitialized),
OCULUS_BIND_ENTRY_POINT(Initialize7),
OCULUS_BIND_ENTRY_POINT(Shutdown2),
OCULUS_BIND_ENTRY_POINT(SetLogCallback2),
OCULUS_BIND_ENTRY_POINT(GetVersion2),
OCULUS_BIND_ENTRY_POINT(GetNativeSDKVersion2),
OCULUS_BIND_ENTRY_POINT(GetNativeSDKPointer2),
OCULUS_BIND_ENTRY_POINT(GetNativeOpenXRHandles),
OCULUS_BIND_ENTRY_POINT(GetDisplayAdapterId2),
OCULUS_BIND_ENTRY_POINT(GetAudioOutId2),
OCULUS_BIND_ENTRY_POINT(GetAudioOutDeviceId2),
OCULUS_BIND_ENTRY_POINT(GetAudioInId2),
OCULUS_BIND_ENTRY_POINT(GetAudioInDeviceId2),
OCULUS_BIND_ENTRY_POINT(GetInstanceExtensionsVk),
OCULUS_BIND_ENTRY_POINT(GetDeviceExtensionsVk),
OCULUS_BIND_ENTRY_POINT(SetupDistortionWindow3),
OCULUS_BIND_ENTRY_POINT(DestroyDistortionWindow2),
OCULUS_BIND_ENTRY_POINT(GetDominantHand),
OCULUS_BIND_ENTRY_POINT(SetRemoteHandedness),
OCULUS_BIND_ENTRY_POINT(SetColorScaleAndOffset),
OCULUS_BIND_ENTRY_POINT(SetupLayer),
OCULUS_BIND_ENTRY_POINT(SetupLayerDepth),
OCULUS_BIND_ENTRY_POINT(SetEyeFovPremultipliedAlphaMode),
OCULUS_BIND_ENTRY_POINT(GetEyeFovLayerId),
OCULUS_BIND_ENTRY_POINT(GetLayerTextureStageCount),
OCULUS_BIND_ENTRY_POINT(GetLayerTexture2),
OCULUS_BIND_ENTRY_POINT(GetLayerTextureFoveation),
OCULUS_BIND_ENTRY_POINT(GetLayerOcclusionMesh),
OCULUS_BIND_ENTRY_POINT(GetLayerAndroidSurfaceObject),
OCULUS_BIND_ENTRY_POINT(GetLayerTextureSpaceWarp),
OCULUS_BIND_ENTRY_POINT(CalculateEyeLayerDesc3),
OCULUS_BIND_ENTRY_POINT(DestroyLayer),
OCULUS_BIND_ENTRY_POINT(CalculateLayerDesc),
OCULUS_BIND_ENTRY_POINT(CalculateEyeLayerDesc2),
OCULUS_BIND_ENTRY_POINT(CalculateEyePreviewRect),
OCULUS_BIND_ENTRY_POINT(SetupMirrorTexture2),
OCULUS_BIND_ENTRY_POINT(DestroyMirrorTexture2),
OCULUS_BIND_ENTRY_POINT(GetAdaptiveGpuPerformanceScale2),
OCULUS_BIND_ENTRY_POINT(GetAppCpuStartToGpuEndTime2),
OCULUS_BIND_ENTRY_POINT(GetEyePixelsPerTanAngleAtCenter2),
OCULUS_BIND_ENTRY_POINT(GetHmdToEyeOffset2),
OCULUS_BIND_ENTRY_POINT(Update3),
OCULUS_BIND_ENTRY_POINT(WaitToBeginFrame),
OCULUS_BIND_ENTRY_POINT(BeginFrame4),
OCULUS_BIND_ENTRY_POINT(UpdateFoveation),
OCULUS_BIND_ENTRY_POINT(EndFrame4),
OCULUS_BIND_ENTRY_POINT(GetTrackingOrientationSupported2),
OCULUS_BIND_ENTRY_POINT(GetTrackingOrientationEnabled2),
OCULUS_BIND_ENTRY_POINT(SetTrackingOrientationEnabled2),
OCULUS_BIND_ENTRY_POINT(GetTrackingPositionSupported2),
OCULUS_BIND_ENTRY_POINT(GetTrackingPositionEnabled2),
OCULUS_BIND_ENTRY_POINT(SetTrackingPositionEnabled2),
OCULUS_BIND_ENTRY_POINT(GetTrackingPoseEnabledForInvisibleSession),
OCULUS_BIND_ENTRY_POINT(SetTrackingPoseEnabledForInvisibleSession),
OCULUS_BIND_ENTRY_POINT(GetTrackingIPDEnabled2),
OCULUS_BIND_ENTRY_POINT(SetTrackingIPDEnabled2),
OCULUS_BIND_ENTRY_POINT(GetTrackingCalibratedOrigin2),
OCULUS_BIND_ENTRY_POINT(SetTrackingCalibratedOrigin2),
OCULUS_BIND_ENTRY_POINT(GetTrackingOriginType2),
OCULUS_BIND_ENTRY_POINT(SetTrackingOriginType2),
OCULUS_BIND_ENTRY_POINT(RecenterTrackingOrigin2),
OCULUS_BIND_ENTRY_POINT(GetNodePresent2),
OCULUS_BIND_ENTRY_POINT(GetNodeOrientationTracked2),
OCULUS_BIND_ENTRY_POINT(GetNodeOrientationValid),
OCULUS_BIND_ENTRY_POINT(GetNodePositionTracked2),
OCULUS_BIND_ENTRY_POINT(GetNodePositionValid),
OCULUS_BIND_ENTRY_POINT(SetNodePositionTracked2),
OCULUS_BIND_ENTRY_POINT(GetNodePoseState3),
OCULUS_BIND_ENTRY_POINT(GetNodePoseStateRaw),
OCULUS_BIND_ENTRY_POINT(GetNodeFrustum2),
OCULUS_BIND_ENTRY_POINT(SetHeadPoseModifier),
OCULUS_BIND_ENTRY_POINT(GetHeadPoseModifier),
OCULUS_BIND_ENTRY_POINT(GetControllerState4),
OCULUS_BIND_ENTRY_POINT(GetControllerState5),
OCULUS_BIND_ENTRY_POINT(GetControllerState6),
OCULUS_BIND_ENTRY_POINT(GetActiveController2),
OCULUS_BIND_ENTRY_POINT(GetConnectedControllers2),
OCULUS_BIND_ENTRY_POINT(SetControllerVibration2),
OCULUS_BIND_ENTRY_POINT(SetControllerLocalizedVibration),
OCULUS_BIND_ENTRY_POINT(SetControllerHapticsAmplitudeEnvelope),
OCULUS_BIND_ENTRY_POINT(SetControllerHapticsPcm),
OCULUS_BIND_ENTRY_POINT(GetControllerHapticsDesc2),
OCULUS_BIND_ENTRY_POINT(GetControllerHapticsState2),
OCULUS_BIND_ENTRY_POINT(GetControllerSampleRateHz),
OCULUS_BIND_ENTRY_POINT(SetControllerHaptics2),
OCULUS_BIND_ENTRY_POINT(SetSuggestedCpuPerformanceLevel),
OCULUS_BIND_ENTRY_POINT(GetSuggestedCpuPerformanceLevel),
OCULUS_BIND_ENTRY_POINT(SetSuggestedGpuPerformanceLevel),
OCULUS_BIND_ENTRY_POINT(GetSuggestedGpuPerformanceLevel),
OCULUS_BIND_ENTRY_POINT(GetAppCPUPriority2),
OCULUS_BIND_ENTRY_POINT(SetAppCPUPriority2),
OCULUS_BIND_ENTRY_POINT(GetSystemPowerSavingMode2),
OCULUS_BIND_ENTRY_POINT(GetSystemDisplayFrequency2),
OCULUS_BIND_ENTRY_POINT(GetSystemDisplayAvailableFrequencies),
OCULUS_BIND_ENTRY_POINT(SetSystemDisplayFrequency),
OCULUS_BIND_ENTRY_POINT(GetSystemVSyncCount2),
OCULUS_BIND_ENTRY_POINT(SetSystemVSyncCount2),
OCULUS_BIND_ENTRY_POINT(GetSystemProductName2),
OCULUS_BIND_ENTRY_POINT(GetSystemRegion2),
OCULUS_BIND_ENTRY_POINT(ShowSystemUI2),
OCULUS_BIND_ENTRY_POINT(GetAppHasVrFocus2),
OCULUS_BIND_ENTRY_POINT(GetAppHasInputFocus),
OCULUS_BIND_ENTRY_POINT(GetAppHasSystemOverlayPresent),
OCULUS_BIND_ENTRY_POINT(GetAppShouldQuit2),
OCULUS_BIND_ENTRY_POINT(GetAppShouldRecenter2),
OCULUS_BIND_ENTRY_POINT(GetAppShouldRecreateDistortionWindow2),
OCULUS_BIND_ENTRY_POINT(GetAppSpace),
OCULUS_BIND_ENTRY_POINT(GetAppLatencyTimings2),
OCULUS_BIND_ENTRY_POINT(SetAppEngineInfo2),
OCULUS_BIND_ENTRY_POINT(GetUserPresent2),
OCULUS_BIND_ENTRY_POINT(GetUserIPD2),
OCULUS_BIND_ENTRY_POINT(SetUserIPD2),
OCULUS_BIND_ENTRY_POINT(GetUserEyeHeight2),
OCULUS_BIND_ENTRY_POINT(SetUserEyeHeight2),
OCULUS_BIND_ENTRY_POINT(GetUserNeckEyeDistance2),
OCULUS_BIND_ENTRY_POINT(SetUserNeckEyeDistance2),
OCULUS_BIND_ENTRY_POINT(SetupDisplayObjects2),
OCULUS_BIND_ENTRY_POINT(GetSystemMultiViewSupported2),
OCULUS_BIND_ENTRY_POINT(GetEyeTextureArraySupported2),
OCULUS_BIND_ENTRY_POINT(GetBoundaryConfigured2),
OCULUS_BIND_ENTRY_POINT(GetDepthCompositingSupported),
OCULUS_BIND_ENTRY_POINT(TestBoundaryNode2),
OCULUS_BIND_ENTRY_POINT(TestBoundaryPoint2),
OCULUS_BIND_ENTRY_POINT(GetBoundaryGeometry3),
OCULUS_BIND_ENTRY_POINT(GetBoundaryDimensions2),
OCULUS_BIND_ENTRY_POINT(GetBoundaryVisible2),
OCULUS_BIND_ENTRY_POINT(SetBoundaryVisible2),
OCULUS_BIND_ENTRY_POINT(GetSystemHeadsetType2),
OCULUS_BIND_ENTRY_POINT(GetAppPerfStats2),
OCULUS_BIND_ENTRY_POINT(ResetAppPerfStats2),
OCULUS_BIND_ENTRY_POINT(GetAppFramerate2),
OCULUS_BIND_ENTRY_POINT(IsPerfMetricsSupported),
OCULUS_BIND_ENTRY_POINT(GetPerfMetricsFloat),
OCULUS_BIND_ENTRY_POINT(GetPerfMetricsInt),
OCULUS_BIND_ENTRY_POINT(SetHandNodePoseStateLatency),
OCULUS_BIND_ENTRY_POINT(GetHandNodePoseStateLatency),
OCULUS_BIND_ENTRY_POINT(GetSystemRecommendedMSAALevel2),
OCULUS_BIND_ENTRY_POINT(SetInhibitSystemUX2),
OCULUS_BIND_ENTRY_POINT(GetTiledMultiResSupported),
OCULUS_BIND_ENTRY_POINT(GetTiledMultiResLevel),
OCULUS_BIND_ENTRY_POINT(SetTiledMultiResLevel),
OCULUS_BIND_ENTRY_POINT(GetTiledMultiResDynamic),
OCULUS_BIND_ENTRY_POINT(SetTiledMultiResDynamic),
OCULUS_BIND_ENTRY_POINT(GetFoveationEyeTrackedSupported),
OCULUS_BIND_ENTRY_POINT(GetFoveationEyeTracked),
OCULUS_BIND_ENTRY_POINT(SetFoveationEyeTracked),
OCULUS_BIND_ENTRY_POINT(GetFoveationEyeTrackedCenter),
OCULUS_BIND_ENTRY_POINT(GetGPUUtilSupported),
OCULUS_BIND_ENTRY_POINT(GetGPUUtilLevel),
OCULUS_BIND_ENTRY_POINT(SetThreadPerformance),
OCULUS_BIND_ENTRY_POINT(AutoThreadScheduling),
OCULUS_BIND_ENTRY_POINT(GetGPUFrameTime),
OCULUS_BIND_ENTRY_POINT(GetViewportStencil),
OCULUS_BIND_ENTRY_POINT(SetDeveloperTelemetryConsent),
OCULUS_BIND_ENTRY_POINT(SendEvent),
OCULUS_BIND_ENTRY_POINT(SendEvent2),
OCULUS_BIND_ENTRY_POINT(AddCustomMetadata),
OCULUS_BIND_ENTRY_POINT(SetDeveloperMode),
OCULUS_BIND_ENTRY_POINT(GetCurrentTrackingTransformPose),
OCULUS_BIND_ENTRY_POINT(GetTrackingTransformRawPose),
OCULUS_BIND_ENTRY_POINT(GetTrackingTransformRelativePose),
OCULUS_BIND_ENTRY_POINT(GetTimeInSeconds),
// OCULUS_BIND_ENTRY_POINT(GetPTWNear),
OCULUS_BIND_ENTRY_POINT(GetASWVelocityScale),
OCULUS_BIND_ENTRY_POINT(GetASWDepthScale),
OCULUS_BIND_ENTRY_POINT(GetASWAdaptiveMode),
OCULUS_BIND_ENTRY_POINT(SetASWAdaptiveMode),
OCULUS_BIND_ENTRY_POINT(IsRequestingASWData),
OCULUS_BIND_ENTRY_POINT(GetPredictedDisplayTime),
OCULUS_BIND_ENTRY_POINT(GetHandTrackingEnabled),
OCULUS_BIND_ENTRY_POINT(GetHandState),
OCULUS_BIND_ENTRY_POINT(GetHandState2),
OCULUS_BIND_ENTRY_POINT(GetSkeleton2),
OCULUS_BIND_ENTRY_POINT(GetSkeleton3),
OCULUS_BIND_ENTRY_POINT(GetMesh),
OCULUS_BIND_ENTRY_POINT(GetLocalTrackingSpaceRecenterCount),
OCULUS_BIND_ENTRY_POINT(GetSystemHmd3DofModeEnabled),
OCULUS_BIND_ENTRY_POINT(SetClientColorDesc),
OCULUS_BIND_ENTRY_POINT(GetHmdColorDesc),
OCULUS_BIND_ENTRY_POINT(PollEvent),
OCULUS_BIND_ENTRY_POINT(RegisterOpenXREventHandler),
OCULUS_BIND_ENTRY_POINT(UnregisterOpenXREventHandler),
OCULUS_BIND_ENTRY_POINT(GetNativeXrApiType),
OCULUS_BIND_ENTRY_POINT(GetLocalDimmingSupported),
OCULUS_BIND_ENTRY_POINT(SetLocalDimming),
OCULUS_BIND_ENTRY_POINT(GetCurrentInteractionProfile),
OCULUS_BIND_ENTRY_POINT(GetLayerRecommendedResolution),
OCULUS_BIND_ENTRY_POINT(IsLayerShapeSupported),
OCULUS_BIND_ENTRY_POINT(SetEyeBufferSharpenType),
OCULUS_BIND_ENTRY_POINT(GetOpenXRInstanceProcAddrFunc),
OCULUS_BIND_ENTRY_POINT(SaveUnifiedConsent),
OCULUS_BIND_ENTRY_POINT(SaveUnifiedConsentWithOlderVersion),
OCULUS_BIND_ENTRY_POINT(GetUnifiedConsent),
OCULUS_BIND_ENTRY_POINT(GetConsentTitle),
OCULUS_BIND_ENTRY_POINT(GetConsentMarkdownText),
OCULUS_BIND_ENTRY_POINT(GetConsentNotificationMarkdownText),
OCULUS_BIND_ENTRY_POINT(ShouldShowTelemetryConsentWindow),
OCULUS_BIND_ENTRY_POINT(ShouldShowTelemetryNotification),
OCULUS_BIND_ENTRY_POINT(SetNotificationShown),
OCULUS_BIND_ENTRY_POINT(GetConsentSettingsChangeText),
OCULUS_BIND_ENTRY_POINT(IsConsentSettingsChangeEnabled),
OCULUS_BIND_ENTRY_POINT(InitializeEnvironmentDepth),
OCULUS_BIND_ENTRY_POINT(DestroyEnvironmentDepth),
OCULUS_BIND_ENTRY_POINT(GetEnvironmentDepthTextureDesc),
OCULUS_BIND_ENTRY_POINT(GetEnvironmentDepthTextureStageCount),
OCULUS_BIND_ENTRY_POINT(GetEnvironmentDepthTexture),
OCULUS_BIND_ENTRY_POINT(SetEnvironmentDepthHandRemoval),
OCULUS_BIND_ENTRY_POINT(StartEnvironmentDepth),
OCULUS_BIND_ENTRY_POINT(StopEnvironmentDepth),
OCULUS_BIND_ENTRY_POINT(GetEnvironmentDepthFrameDesc),
#ifndef OVRPLUGIN_JNI_LIB_EXCLUDED
OCULUS_BIND_ENTRY_POINT(GetSystemVolume2),
OCULUS_BIND_ENTRY_POINT(GetSystemHeadphonesPresent2),
#endif
// Anchors
OCULUS_BIND_ENTRY_POINT(LocateSpace),
OCULUS_BIND_ENTRY_POINT(LocateSpace2),
OCULUS_BIND_ENTRY_POINT(CreateSpatialAnchor),
OCULUS_BIND_ENTRY_POINT(DestroySpace),
OCULUS_BIND_ENTRY_POINT(SetSpaceComponentStatus),
OCULUS_BIND_ENTRY_POINT(GetSpaceComponentStatus),
OCULUS_BIND_ENTRY_POINT(EnumerateSpaceSupportedComponents),
OCULUS_BIND_ENTRY_POINT(QuerySpaces),
OCULUS_BIND_ENTRY_POINT(QuerySpaces2),
OCULUS_BIND_ENTRY_POINT(RetrieveSpaceQueryResults),
OCULUS_BIND_ENTRY_POINT(SaveSpace),
OCULUS_BIND_ENTRY_POINT(EraseSpace),
OCULUS_BIND_ENTRY_POINT(GetSpaceUuid),
OCULUS_BIND_ENTRY_POINT(SaveSpaceList),
OCULUS_BIND_ENTRY_POINT(ShareSpaces),
OCULUS_BIND_ENTRY_POINT(ShareSpaces2),
OCULUS_BIND_ENTRY_POINT(CreateSpaceUser),
OCULUS_BIND_ENTRY_POINT(DestroySpaceUser),
// Anchors 2.0 (APD)
OCULUS_BIND_ENTRY_POINT(DiscoverSpaces),
OCULUS_BIND_ENTRY_POINT(RetrieveSpaceDiscoveryResults),
OCULUS_BIND_ENTRY_POINT(SaveSpaces),
OCULUS_BIND_ENTRY_POINT(EraseSpaces),
// Scene
OCULUS_BIND_ENTRY_POINT(GetSpaceContainer),
OCULUS_BIND_ENTRY_POINT(GetSpaceBoundingBox2D),
OCULUS_BIND_ENTRY_POINT(GetSpaceBoundingBox3D),
OCULUS_BIND_ENTRY_POINT(GetSpaceSemanticLabels),
OCULUS_BIND_ENTRY_POINT(GetSpaceRoomLayout),
OCULUS_BIND_ENTRY_POINT(GetSpaceBoundary2D),
OCULUS_BIND_ENTRY_POINT(RequestSceneCapture),
OCULUS_BIND_ENTRY_POINT(GetSpaceTriangleMesh),
// Boundary Visibility
OCULUS_BIND_ENTRY_POINT(RequestBoundaryVisibility),
OCULUS_BIND_ENTRY_POINT(GetBoundaryVisibility),
// Colocation Session
OCULUS_BIND_ENTRY_POINT(StartColocationDiscovery),
OCULUS_BIND_ENTRY_POINT(StopColocationDiscovery),
OCULUS_BIND_ENTRY_POINT(StartColocationAdvertisement),
OCULUS_BIND_ENTRY_POINT(StopColocationAdvertisement),
// MovementSDK
OCULUS_BIND_ENTRY_POINT(GetBodyTrackingEnabled),
OCULUS_BIND_ENTRY_POINT(GetBodyTrackingSupported),
OCULUS_BIND_ENTRY_POINT(StopBodyTracking),
OCULUS_BIND_ENTRY_POINT(GetBodyState4),
OCULUS_BIND_ENTRY_POINT(GetFullBodyTrackingEnabled),
OCULUS_BIND_ENTRY_POINT(StartBodyTracking2),
OCULUS_BIND_ENTRY_POINT(RequestBodyTrackingFidelity),
OCULUS_BIND_ENTRY_POINT(ResetBodyTrackingCalibration),
OCULUS_BIND_ENTRY_POINT(SuggestBodyTrackingCalibrationOverride),
OCULUS_BIND_ENTRY_POINT(GetFaceTracking2Enabled),
OCULUS_BIND_ENTRY_POINT(GetFaceTracking2Supported),
OCULUS_BIND_ENTRY_POINT(GetFaceState2),
OCULUS_BIND_ENTRY_POINT(StartFaceTracking2),
OCULUS_BIND_ENTRY_POINT(StopFaceTracking2),
OCULUS_BIND_ENTRY_POINT(GetEyeTrackingEnabled),
OCULUS_BIND_ENTRY_POINT(GetEyeTrackingSupported),
OCULUS_BIND_ENTRY_POINT(GetEyeGazesState),
OCULUS_BIND_ENTRY_POINT(StartEyeTracking),
OCULUS_BIND_ENTRY_POINT(StopEyeTracking),
OCULUS_BIND_ENTRY_POINT(GetFaceTrackingVisemesSupported),
OCULUS_BIND_ENTRY_POINT(GetFaceTrackingVisemesEnabled),
OCULUS_BIND_ENTRY_POINT(GetFaceVisemesState),
OCULUS_BIND_ENTRY_POINT(SetFaceTrackingVisemesEnabled),
// QPL
OCULUS_BIND_ENTRY_POINT(QplMarkerStart),
OCULUS_BIND_ENTRY_POINT(QplMarkerEnd),
OCULUS_BIND_ENTRY_POINT(QplMarkerPoint),
OCULUS_BIND_ENTRY_POINT(QplMarkerPointCached),
OCULUS_BIND_ENTRY_POINT(QplMarkerAnnotation),
OCULUS_BIND_ENTRY_POINT(QplCreateMarkerHandle),
OCULUS_BIND_ENTRY_POINT(QplDestroyMarkerHandle),
OCULUS_BIND_ENTRY_POINT(OnEditorShutdown),
OCULUS_BIND_ENTRY_POINT(QplSetConsent),
// OVR_Plugin_Insight.h
OCULUS_BIND_ENTRY_POINT(InitializeInsightPassthrough),
OCULUS_BIND_ENTRY_POINT(ShutdownInsightPassthrough),
OCULUS_BIND_ENTRY_POINT(GetInsightPassthroughInitialized),
OCULUS_BIND_ENTRY_POINT(GetInsightPassthroughInitializationState),
OCULUS_BIND_ENTRY_POINT(CreateInsightTriangleMesh),
OCULUS_BIND_ENTRY_POINT(DestroyInsightTriangleMesh),
OCULUS_BIND_ENTRY_POINT(AddInsightPassthroughSurfaceGeometry),
OCULUS_BIND_ENTRY_POINT(DestroyInsightPassthroughGeometryInstance),
OCULUS_BIND_ENTRY_POINT(UpdateInsightPassthroughGeometryTransform),
OCULUS_BIND_ENTRY_POINT(SetInsightPassthroughStyle),
OCULUS_BIND_ENTRY_POINT(SetInsightPassthroughStyle2),
OCULUS_BIND_ENTRY_POINT(GetPassthroughCapabilityFlags),
OCULUS_BIND_ENTRY_POINT(CreatePassthroughColorLut),
OCULUS_BIND_ENTRY_POINT(DestroyPassthroughColorLut),
OCULUS_BIND_ENTRY_POINT(UpdatePassthroughColorLut),
OCULUS_BIND_ENTRY_POINT(GetPassthroughCapabilities),
OCULUS_BIND_ENTRY_POINT(GetPassthroughPreferences),
// OVR_Plugin_MixedReality.h
OCULUS_BIND_ENTRY_POINT(InitializeMixedReality),
OCULUS_BIND_ENTRY_POINT(ShutdownMixedReality),
OCULUS_BIND_ENTRY_POINT(GetMixedRealityInitialized),
OCULUS_BIND_ENTRY_POINT(UpdateExternalCamera),
OCULUS_BIND_ENTRY_POINT(GetExternalCameraCount),
OCULUS_BIND_ENTRY_POINT(GetExternalCameraName),
OCULUS_BIND_ENTRY_POINT(GetExternalCameraIntrinsics),
OCULUS_BIND_ENTRY_POINT(GetExternalCameraExtrinsics),
// OVR_Plugin_Media.h
OCULUS_BIND_ENTRY_POINT(Media_Initialize),
OCULUS_BIND_ENTRY_POINT(Media_Shutdown),
OCULUS_BIND_ENTRY_POINT(Media_GetInitialized),
OCULUS_BIND_ENTRY_POINT(Media_Update),
OCULUS_BIND_ENTRY_POINT(Media_GetMrcActivationMode),
OCULUS_BIND_ENTRY_POINT(Media_SetMrcActivationMode),
OCULUS_BIND_ENTRY_POINT(Media_IsMrcEnabled),
OCULUS_BIND_ENTRY_POINT(Media_IsMrcActivated),
OCULUS_BIND_ENTRY_POINT(Media_UseMrcDebugCamera),
OCULUS_BIND_ENTRY_POINT(Media_SetMrcInputVideoBufferType),
OCULUS_BIND_ENTRY_POINT(Media_GetMrcInputVideoBufferType),
OCULUS_BIND_ENTRY_POINT(Media_SetMrcFrameSize),
OCULUS_BIND_ENTRY_POINT(Media_GetMrcFrameSize),
OCULUS_BIND_ENTRY_POINT(Media_SetMrcAudioSampleRate),
OCULUS_BIND_ENTRY_POINT(Media_GetMrcAudioSampleRate),
OCULUS_BIND_ENTRY_POINT(Media_SetMrcFrameImageFlipped),
OCULUS_BIND_ENTRY_POINT(Media_GetMrcFrameImageFlipped),
OCULUS_BIND_ENTRY_POINT(Media_SetMrcFrameInverseAlpha),
OCULUS_BIND_ENTRY_POINT(Media_GetMrcFrameInverseAlpha),
OCULUS_BIND_ENTRY_POINT(Media_SetAvailableQueueIndexVulkan),
OCULUS_BIND_ENTRY_POINT(Media_EncodeMrcFrame),
OCULUS_BIND_ENTRY_POINT(Media_EncodeMrcFrameWithDualTextures),
OCULUS_BIND_ENTRY_POINT(Media_SyncMrcFrame),
OCULUS_BIND_ENTRY_POINT(Media_EncodeMrcFrameWithPoseTime),
OCULUS_BIND_ENTRY_POINT(Media_EncodeMrcFrameDualTexturesWithPoseTime),
OCULUS_BIND_ENTRY_POINT(Media_SetHeadsetControllerPose),
OCULUS_BIND_ENTRY_POINT(Media_EnumerateCameraAnchorHandles),
OCULUS_BIND_ENTRY_POINT(Media_GetCurrentCameraAnchorHandle),
OCULUS_BIND_ENTRY_POINT(Media_GetCameraAnchorName),
OCULUS_BIND_ENTRY_POINT(Media_GetCameraAnchorHandle),
OCULUS_BIND_ENTRY_POINT(Media_GetCameraAnchorType),
OCULUS_BIND_ENTRY_POINT(Media_CreateCustomCameraAnchor),
OCULUS_BIND_ENTRY_POINT(Media_DestroyCustomCameraAnchor),
OCULUS_BIND_ENTRY_POINT(Media_GetCustomCameraAnchorPose),
OCULUS_BIND_ENTRY_POINT(Media_SetCustomCameraAnchorPose),
OCULUS_BIND_ENTRY_POINT(Media_GetCameraMinMaxDistance),
OCULUS_BIND_ENTRY_POINT(Media_SetCameraMinMaxDistance),
OCULUS_BIND_ENTRY_POINT(SetControllerDrivenHandPoses),
OCULUS_BIND_ENTRY_POINT(SetControllerDrivenHandPosesAreNatural),
};
#undef OCULUS_BIND_ENTRY_POINT
bool result = true;
for (int i = 0; i < UE_ARRAY_COUNT(entryPointArray); ++i)
{
*(entryPointArray[i].EntryPointPtr) = LoadEntryPoint(LibraryHandle, entryPointArray[i].EntryPointName);
if (*entryPointArray[i].EntryPointPtr == nullptr)
{
UE_LOG(LogOculusPluginWrapper, Error, TEXT("OculusPlugin EntryPoint could not be loaded: %s"), ANSI_TO_TCHAR(entryPointArray[i].EntryPointName));
result = false;
}
}
wrapper->Initialized = true;
if (result)
{
UE_LOG(LogOculusPluginWrapper, Log, TEXT("OculusPlugin initialized successfully"));
}
else
{
DestroyOculusPluginWrapper(wrapper);
}
return result;
}
void OculusPluginWrapper::DestroyOculusPluginWrapper(OculusPluginWrapper* wrapper)
{
if (!wrapper->Initialized)
return;
wrapper->Reset();
UE_LOG(LogOculusPluginWrapper, Log, TEXT("OculusPlugin destroyed successfully"));
}
static void* LoadEntryPoint(void* Handle, const char* EntryPointName)
{
if (Handle == nullptr)
return nullptr;
#if PLATFORM_WINDOWS
void* ptr = GetProcAddress((HMODULE)Handle, EntryPointName);
if (ptr == nullptr)
{
UE_LOG(LogOculusPluginWrapper, Error, TEXT("Unable to load entry point: %s"), ANSI_TO_TCHAR(EntryPointName));
}
return ptr;
#elif PLATFORM_ANDROID
void* ptr = dlsym(Handle, EntryPointName);
if (ptr == nullptr)
{
UE_LOG(LogOculusPluginWrapper, Error, TEXT("Unable to load entry point: %s, error %s"), ANSI_TO_TCHAR(EntryPointName), ANSI_TO_TCHAR(dlerror()));
}
return ptr;
#else
UE_LOG(LogOculusPluginWrapper, Error, TEXT("LoadEntryPoint: Unsupported platform"));
return nullptr;
#endif
}

View File

@@ -0,0 +1,452 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include <memory.h>
#if PLATFORM_SUPPORTS_PRAGMA_PACK
#pragma pack(push, 8)
#endif
#if PLATFORM_WINDOWS
#include "Windows/AllowWindowsPlatformTypes.h"
#endif
#pragma warning(push)
#pragma warning(disable : 4201) // nonstandard extension used: nameless struct/union
// #pragma warning(disable:4668) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives'
#define OVRP_EXPORT typedef
#include "OVR_Plugin.h"
#include "OVR_Plugin_Insight.h"
#include "OVR_Plugin_MixedReality.h"
#include "OVR_Plugin_Media.h"
#undef OVRP_EXPORT
#pragma warning(pop)
#if PLATFORM_WINDOWS
#include "Windows/HideWindowsPlatformTypes.h"
#endif
#if PLATFORM_SUPPORTS_PRAGMA_PACK
#pragma pack(pop)
#endif
#if PLATFORM_WINDOWS
#include "Windows/WindowsHWrapper.h"
#endif
DECLARE_LOG_CATEGORY_EXTERN(LogOculusPluginWrapper, Log, All);
#define OCULUS_DECLARE_ENTRY_POINT(Func) ovrp_##Func* Func
struct OculusPluginWrapper
{
OculusPluginWrapper()
{
Reset();
}
void Reset()
{
memset(this, 0, sizeof(OculusPluginWrapper));
ovrpHeaderVersion.MajorVersion = OVRP_MAJOR_VERSION;
ovrpHeaderVersion.MinorVersion = OVRP_MINOR_VERSION;
ovrpHeaderVersion.PatchVersion = OVRP_PATCH_VERSION;
}
bool IsInitialized() const
{
return Initialized;
}
// OVR_Plugin.h
OCULUS_DECLARE_ENTRY_POINT(PreInitialize5);
OCULUS_DECLARE_ENTRY_POINT(GetInitialized);
OCULUS_DECLARE_ENTRY_POINT(Initialize7);
OCULUS_DECLARE_ENTRY_POINT(Shutdown2);
OCULUS_DECLARE_ENTRY_POINT(SetLogCallback2);
OCULUS_DECLARE_ENTRY_POINT(GetVersion2);
OCULUS_DECLARE_ENTRY_POINT(GetNativeSDKVersion2);
OCULUS_DECLARE_ENTRY_POINT(GetNativeSDKPointer2);
OCULUS_DECLARE_ENTRY_POINT(GetNativeOpenXRHandles);
OCULUS_DECLARE_ENTRY_POINT(GetDisplayAdapterId2);
OCULUS_DECLARE_ENTRY_POINT(GetAudioOutId2);
OCULUS_DECLARE_ENTRY_POINT(GetAudioOutDeviceId2);
OCULUS_DECLARE_ENTRY_POINT(GetAudioInId2);
OCULUS_DECLARE_ENTRY_POINT(GetAudioInDeviceId2);
OCULUS_DECLARE_ENTRY_POINT(GetInstanceExtensionsVk);
OCULUS_DECLARE_ENTRY_POINT(GetDeviceExtensionsVk);
OCULUS_DECLARE_ENTRY_POINT(SetupDistortionWindow3);
OCULUS_DECLARE_ENTRY_POINT(DestroyDistortionWindow2);
OCULUS_DECLARE_ENTRY_POINT(GetDominantHand);
OCULUS_DECLARE_ENTRY_POINT(SetRemoteHandedness);
OCULUS_DECLARE_ENTRY_POINT(SetColorScaleAndOffset);
OCULUS_DECLARE_ENTRY_POINT(SetupLayer);
OCULUS_DECLARE_ENTRY_POINT(SetupLayerDepth);
OCULUS_DECLARE_ENTRY_POINT(SetEyeFovPremultipliedAlphaMode);
OCULUS_DECLARE_ENTRY_POINT(GetEyeFovLayerId);
OCULUS_DECLARE_ENTRY_POINT(GetLayerTextureStageCount);
OCULUS_DECLARE_ENTRY_POINT(GetLayerTexture2);
OCULUS_DECLARE_ENTRY_POINT(GetLayerTextureFoveation);
OCULUS_DECLARE_ENTRY_POINT(GetLayerTextureSpaceWarp);
OCULUS_DECLARE_ENTRY_POINT(CalculateEyeLayerDesc3);
OCULUS_DECLARE_ENTRY_POINT(GetLayerAndroidSurfaceObject);
OCULUS_DECLARE_ENTRY_POINT(GetLayerOcclusionMesh);
OCULUS_DECLARE_ENTRY_POINT(DestroyLayer);
OCULUS_DECLARE_ENTRY_POINT(CalculateLayerDesc);
OCULUS_DECLARE_ENTRY_POINT(CalculateEyeLayerDesc2);
OCULUS_DECLARE_ENTRY_POINT(CalculateEyePreviewRect);
OCULUS_DECLARE_ENTRY_POINT(SetupMirrorTexture2);
OCULUS_DECLARE_ENTRY_POINT(DestroyMirrorTexture2);
OCULUS_DECLARE_ENTRY_POINT(GetAdaptiveGpuPerformanceScale2);
OCULUS_DECLARE_ENTRY_POINT(GetAppCpuStartToGpuEndTime2);
OCULUS_DECLARE_ENTRY_POINT(GetEyePixelsPerTanAngleAtCenter2);
OCULUS_DECLARE_ENTRY_POINT(GetHmdToEyeOffset2);
OCULUS_DECLARE_ENTRY_POINT(Update3);
OCULUS_DECLARE_ENTRY_POINT(WaitToBeginFrame);
OCULUS_DECLARE_ENTRY_POINT(BeginFrame4);
OCULUS_DECLARE_ENTRY_POINT(UpdateFoveation);
OCULUS_DECLARE_ENTRY_POINT(EndFrame4);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingOrientationSupported2);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingOrientationEnabled2);
OCULUS_DECLARE_ENTRY_POINT(SetTrackingOrientationEnabled2);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingPositionSupported2);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingPositionEnabled2);
OCULUS_DECLARE_ENTRY_POINT(SetTrackingPositionEnabled2);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingPoseEnabledForInvisibleSession);
OCULUS_DECLARE_ENTRY_POINT(SetTrackingPoseEnabledForInvisibleSession);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingIPDEnabled2);
OCULUS_DECLARE_ENTRY_POINT(SetTrackingIPDEnabled2);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingCalibratedOrigin2);
OCULUS_DECLARE_ENTRY_POINT(SetTrackingCalibratedOrigin2);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingOriginType2);
OCULUS_DECLARE_ENTRY_POINT(SetTrackingOriginType2);
OCULUS_DECLARE_ENTRY_POINT(RecenterTrackingOrigin2);
OCULUS_DECLARE_ENTRY_POINT(GetNodePresent2);
OCULUS_DECLARE_ENTRY_POINT(GetNodeOrientationTracked2);
OCULUS_DECLARE_ENTRY_POINT(GetNodeOrientationValid);
OCULUS_DECLARE_ENTRY_POINT(GetNodePositionTracked2);
OCULUS_DECLARE_ENTRY_POINT(GetNodePositionValid);
OCULUS_DECLARE_ENTRY_POINT(SetNodePositionTracked2);
OCULUS_DECLARE_ENTRY_POINT(GetNodePoseState3);
OCULUS_DECLARE_ENTRY_POINT(GetNodePoseStateRaw);
OCULUS_DECLARE_ENTRY_POINT(GetNodeFrustum2);
OCULUS_DECLARE_ENTRY_POINT(SetHeadPoseModifier);
OCULUS_DECLARE_ENTRY_POINT(GetHeadPoseModifier);
OCULUS_DECLARE_ENTRY_POINT(GetControllerState4);
OCULUS_DECLARE_ENTRY_POINT(GetControllerState5);
OCULUS_DECLARE_ENTRY_POINT(GetControllerState6);
OCULUS_DECLARE_ENTRY_POINT(GetActiveController2);
OCULUS_DECLARE_ENTRY_POINT(GetConnectedControllers2);
OCULUS_DECLARE_ENTRY_POINT(SetControllerVibration2);
OCULUS_DECLARE_ENTRY_POINT(SetControllerLocalizedVibration);
OCULUS_DECLARE_ENTRY_POINT(SetControllerHapticsAmplitudeEnvelope);
OCULUS_DECLARE_ENTRY_POINT(SetControllerHapticsPcm);
OCULUS_DECLARE_ENTRY_POINT(GetControllerHapticsDesc2);
OCULUS_DECLARE_ENTRY_POINT(GetControllerHapticsState2);
OCULUS_DECLARE_ENTRY_POINT(GetControllerSampleRateHz);
OCULUS_DECLARE_ENTRY_POINT(SetControllerHaptics2);
OCULUS_DECLARE_ENTRY_POINT(SetSuggestedCpuPerformanceLevel);
OCULUS_DECLARE_ENTRY_POINT(GetSuggestedCpuPerformanceLevel);
OCULUS_DECLARE_ENTRY_POINT(SetSuggestedGpuPerformanceLevel);
OCULUS_DECLARE_ENTRY_POINT(GetSuggestedGpuPerformanceLevel);
OCULUS_DECLARE_ENTRY_POINT(GetAppCPUPriority2);
OCULUS_DECLARE_ENTRY_POINT(SetAppCPUPriority2);
OCULUS_DECLARE_ENTRY_POINT(GetSystemPowerSavingMode2);
OCULUS_DECLARE_ENTRY_POINT(GetSystemDisplayFrequency2);
OCULUS_DECLARE_ENTRY_POINT(GetSystemDisplayAvailableFrequencies);
OCULUS_DECLARE_ENTRY_POINT(SetSystemDisplayFrequency);
OCULUS_DECLARE_ENTRY_POINT(GetSystemVSyncCount2);
OCULUS_DECLARE_ENTRY_POINT(SetSystemVSyncCount2);
OCULUS_DECLARE_ENTRY_POINT(GetSystemProductName2);
OCULUS_DECLARE_ENTRY_POINT(GetSystemRegion2);
OCULUS_DECLARE_ENTRY_POINT(ShowSystemUI2);
OCULUS_DECLARE_ENTRY_POINT(GetAppHasVrFocus2);
OCULUS_DECLARE_ENTRY_POINT(GetAppHasInputFocus);
OCULUS_DECLARE_ENTRY_POINT(GetAppHasSystemOverlayPresent);
OCULUS_DECLARE_ENTRY_POINT(GetAppShouldQuit2);
OCULUS_DECLARE_ENTRY_POINT(GetAppShouldRecenter2);
OCULUS_DECLARE_ENTRY_POINT(GetAppShouldRecreateDistortionWindow2);
OCULUS_DECLARE_ENTRY_POINT(GetAppSpace);
OCULUS_DECLARE_ENTRY_POINT(GetAppLatencyTimings2);
OCULUS_DECLARE_ENTRY_POINT(SetAppEngineInfo2);
OCULUS_DECLARE_ENTRY_POINT(GetUserPresent2);
OCULUS_DECLARE_ENTRY_POINT(GetUserIPD2);
OCULUS_DECLARE_ENTRY_POINT(SetUserIPD2);
OCULUS_DECLARE_ENTRY_POINT(GetUserEyeHeight2);
OCULUS_DECLARE_ENTRY_POINT(SetUserEyeHeight2);
OCULUS_DECLARE_ENTRY_POINT(GetUserNeckEyeDistance2);
OCULUS_DECLARE_ENTRY_POINT(SetUserNeckEyeDistance2);
OCULUS_DECLARE_ENTRY_POINT(SetupDisplayObjects2);
OCULUS_DECLARE_ENTRY_POINT(GetSystemMultiViewSupported2);
OCULUS_DECLARE_ENTRY_POINT(GetEyeTextureArraySupported2);
OCULUS_DECLARE_ENTRY_POINT(GetBoundaryConfigured2);
OCULUS_DECLARE_ENTRY_POINT(GetDepthCompositingSupported);
OCULUS_DECLARE_ENTRY_POINT(TestBoundaryNode2);
OCULUS_DECLARE_ENTRY_POINT(TestBoundaryPoint2);
OCULUS_DECLARE_ENTRY_POINT(GetBoundaryGeometry3);
OCULUS_DECLARE_ENTRY_POINT(GetBoundaryDimensions2);
OCULUS_DECLARE_ENTRY_POINT(GetBoundaryVisible2);
OCULUS_DECLARE_ENTRY_POINT(SetBoundaryVisible2);
OCULUS_DECLARE_ENTRY_POINT(GetSystemHeadsetType2);
OCULUS_DECLARE_ENTRY_POINT(GetAppPerfStats2);
OCULUS_DECLARE_ENTRY_POINT(ResetAppPerfStats2);
OCULUS_DECLARE_ENTRY_POINT(GetAppFramerate2);
OCULUS_DECLARE_ENTRY_POINT(IsPerfMetricsSupported);
OCULUS_DECLARE_ENTRY_POINT(GetPerfMetricsFloat);
OCULUS_DECLARE_ENTRY_POINT(GetPerfMetricsInt);
OCULUS_DECLARE_ENTRY_POINT(SetHandNodePoseStateLatency);
OCULUS_DECLARE_ENTRY_POINT(GetHandNodePoseStateLatency);
OCULUS_DECLARE_ENTRY_POINT(GetSystemRecommendedMSAALevel2);
OCULUS_DECLARE_ENTRY_POINT(SetInhibitSystemUX2);
OCULUS_DECLARE_ENTRY_POINT(GetTiledMultiResSupported);
OCULUS_DECLARE_ENTRY_POINT(GetTiledMultiResLevel);
OCULUS_DECLARE_ENTRY_POINT(SetTiledMultiResLevel);
OCULUS_DECLARE_ENTRY_POINT(GetTiledMultiResDynamic);
OCULUS_DECLARE_ENTRY_POINT(SetTiledMultiResDynamic);
OCULUS_DECLARE_ENTRY_POINT(GetFoveationEyeTrackedSupported);
OCULUS_DECLARE_ENTRY_POINT(GetFoveationEyeTracked);
OCULUS_DECLARE_ENTRY_POINT(SetFoveationEyeTracked);
OCULUS_DECLARE_ENTRY_POINT(GetFoveationEyeTrackedCenter);
OCULUS_DECLARE_ENTRY_POINT(GetGPUUtilSupported);
OCULUS_DECLARE_ENTRY_POINT(GetGPUUtilLevel);
OCULUS_DECLARE_ENTRY_POINT(SetThreadPerformance);
OCULUS_DECLARE_ENTRY_POINT(AutoThreadScheduling);
OCULUS_DECLARE_ENTRY_POINT(GetGPUFrameTime);
OCULUS_DECLARE_ENTRY_POINT(GetViewportStencil);
OCULUS_DECLARE_ENTRY_POINT(SetDeveloperTelemetryConsent);
OCULUS_DECLARE_ENTRY_POINT(SendEvent);
OCULUS_DECLARE_ENTRY_POINT(SendEvent2);
OCULUS_DECLARE_ENTRY_POINT(AddCustomMetadata);
OCULUS_DECLARE_ENTRY_POINT(SetDeveloperMode);
OCULUS_DECLARE_ENTRY_POINT(GetCurrentTrackingTransformPose);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingTransformRawPose);
OCULUS_DECLARE_ENTRY_POINT(GetTrackingTransformRelativePose);
OCULUS_DECLARE_ENTRY_POINT(GetTimeInSeconds);
// OCULUS_DECLARE_ENTRY_POINT(GetPTWNear);
OCULUS_DECLARE_ENTRY_POINT(GetASWVelocityScale);
OCULUS_DECLARE_ENTRY_POINT(GetASWDepthScale);
OCULUS_DECLARE_ENTRY_POINT(GetASWAdaptiveMode);
OCULUS_DECLARE_ENTRY_POINT(SetASWAdaptiveMode);
OCULUS_DECLARE_ENTRY_POINT(IsRequestingASWData);
OCULUS_DECLARE_ENTRY_POINT(GetPredictedDisplayTime);
OCULUS_DECLARE_ENTRY_POINT(GetHandTrackingEnabled);
OCULUS_DECLARE_ENTRY_POINT(GetHandState);
OCULUS_DECLARE_ENTRY_POINT(GetHandState2);
OCULUS_DECLARE_ENTRY_POINT(GetSkeleton2);
OCULUS_DECLARE_ENTRY_POINT(GetSkeleton3);
OCULUS_DECLARE_ENTRY_POINT(GetMesh);
OCULUS_DECLARE_ENTRY_POINT(GetLocalTrackingSpaceRecenterCount);
OCULUS_DECLARE_ENTRY_POINT(GetSystemHmd3DofModeEnabled);
OCULUS_DECLARE_ENTRY_POINT(SetClientColorDesc);
OCULUS_DECLARE_ENTRY_POINT(GetHmdColorDesc);
OCULUS_DECLARE_ENTRY_POINT(PollEvent);
OCULUS_DECLARE_ENTRY_POINT(RegisterOpenXREventHandler);
OCULUS_DECLARE_ENTRY_POINT(UnregisterOpenXREventHandler);
OCULUS_DECLARE_ENTRY_POINT(SaveUnifiedConsent);
OCULUS_DECLARE_ENTRY_POINT(SaveUnifiedConsentWithOlderVersion);
OCULUS_DECLARE_ENTRY_POINT(GetUnifiedConsent);
OCULUS_DECLARE_ENTRY_POINT(GetConsentTitle);
OCULUS_DECLARE_ENTRY_POINT(GetConsentMarkdownText);
OCULUS_DECLARE_ENTRY_POINT(GetConsentNotificationMarkdownText);
OCULUS_DECLARE_ENTRY_POINT(ShouldShowTelemetryConsentWindow);
OCULUS_DECLARE_ENTRY_POINT(ShouldShowTelemetryNotification);
OCULUS_DECLARE_ENTRY_POINT(SetNotificationShown);
OCULUS_DECLARE_ENTRY_POINT(GetConsentSettingsChangeText);
OCULUS_DECLARE_ENTRY_POINT(IsConsentSettingsChangeEnabled);
OCULUS_DECLARE_ENTRY_POINT(GetNativeXrApiType);
OCULUS_DECLARE_ENTRY_POINT(GetLocalDimmingSupported);
OCULUS_DECLARE_ENTRY_POINT(SetLocalDimming);
OCULUS_DECLARE_ENTRY_POINT(GetCurrentInteractionProfile);
OCULUS_DECLARE_ENTRY_POINT(GetLayerRecommendedResolution);
OCULUS_DECLARE_ENTRY_POINT(IsLayerShapeSupported);
OCULUS_DECLARE_ENTRY_POINT(SetEyeBufferSharpenType);
OCULUS_DECLARE_ENTRY_POINT(GetOpenXRInstanceProcAddrFunc);
OCULUS_DECLARE_ENTRY_POINT(InitializeEnvironmentDepth);
OCULUS_DECLARE_ENTRY_POINT(DestroyEnvironmentDepth);
OCULUS_DECLARE_ENTRY_POINT(GetEnvironmentDepthTextureDesc);
OCULUS_DECLARE_ENTRY_POINT(GetEnvironmentDepthTextureStageCount);
OCULUS_DECLARE_ENTRY_POINT(GetEnvironmentDepthTexture);
OCULUS_DECLARE_ENTRY_POINT(SetEnvironmentDepthHandRemoval);
OCULUS_DECLARE_ENTRY_POINT(StartEnvironmentDepth);
OCULUS_DECLARE_ENTRY_POINT(StopEnvironmentDepth);
OCULUS_DECLARE_ENTRY_POINT(GetEnvironmentDepthFrameDesc);
#ifndef OVRPLUGIN_JNI_LIB_EXCLUDED
OCULUS_DECLARE_ENTRY_POINT(GetSystemVolume2);
OCULUS_DECLARE_ENTRY_POINT(GetSystemHeadphonesPresent2);
#endif
// Anchors
OCULUS_DECLARE_ENTRY_POINT(LocateSpace);
OCULUS_DECLARE_ENTRY_POINT(LocateSpace2);
OCULUS_DECLARE_ENTRY_POINT(CreateSpatialAnchor);
OCULUS_DECLARE_ENTRY_POINT(DestroySpace);
OCULUS_DECLARE_ENTRY_POINT(SetSpaceComponentStatus);
OCULUS_DECLARE_ENTRY_POINT(GetSpaceComponentStatus);
OCULUS_DECLARE_ENTRY_POINT(EnumerateSpaceSupportedComponents);
OCULUS_DECLARE_ENTRY_POINT(QuerySpaces);
OCULUS_DECLARE_ENTRY_POINT(QuerySpaces2);
OCULUS_DECLARE_ENTRY_POINT(RetrieveSpaceQueryResults);
OCULUS_DECLARE_ENTRY_POINT(SaveSpace);
OCULUS_DECLARE_ENTRY_POINT(EraseSpace);
OCULUS_DECLARE_ENTRY_POINT(GetSpaceUuid);
OCULUS_DECLARE_ENTRY_POINT(SaveSpaceList);
OCULUS_DECLARE_ENTRY_POINT(ShareSpaces);
OCULUS_DECLARE_ENTRY_POINT(ShareSpaces2);
OCULUS_DECLARE_ENTRY_POINT(CreateSpaceUser);
OCULUS_DECLARE_ENTRY_POINT(DestroySpaceUser);
// Anchors 2.0 (APD)
OCULUS_DECLARE_ENTRY_POINT(DiscoverSpaces);
OCULUS_DECLARE_ENTRY_POINT(RetrieveSpaceDiscoveryResults);
OCULUS_DECLARE_ENTRY_POINT(SaveSpaces);
OCULUS_DECLARE_ENTRY_POINT(EraseSpaces);
// Scene
OCULUS_DECLARE_ENTRY_POINT(GetSpaceContainer);
OCULUS_DECLARE_ENTRY_POINT(GetSpaceBoundingBox2D);
OCULUS_DECLARE_ENTRY_POINT(GetSpaceBoundingBox3D);
OCULUS_DECLARE_ENTRY_POINT(GetSpaceSemanticLabels);
OCULUS_DECLARE_ENTRY_POINT(GetSpaceRoomLayout);
OCULUS_DECLARE_ENTRY_POINT(GetSpaceBoundary2D);
OCULUS_DECLARE_ENTRY_POINT(RequestSceneCapture);
OCULUS_DECLARE_ENTRY_POINT(GetSpaceTriangleMesh);
// Boundary Visibility
OCULUS_DECLARE_ENTRY_POINT(RequestBoundaryVisibility);
OCULUS_DECLARE_ENTRY_POINT(GetBoundaryVisibility);
// Colocation Session
OCULUS_DECLARE_ENTRY_POINT(StartColocationDiscovery);
OCULUS_DECLARE_ENTRY_POINT(StopColocationDiscovery);
OCULUS_DECLARE_ENTRY_POINT(StartColocationAdvertisement);
OCULUS_DECLARE_ENTRY_POINT(StopColocationAdvertisement);
// MovementSDK
OCULUS_DECLARE_ENTRY_POINT(GetBodyTrackingEnabled);
OCULUS_DECLARE_ENTRY_POINT(GetBodyTrackingSupported);
OCULUS_DECLARE_ENTRY_POINT(StopBodyTracking);
OCULUS_DECLARE_ENTRY_POINT(GetBodyState4);
OCULUS_DECLARE_ENTRY_POINT(GetFullBodyTrackingEnabled);
OCULUS_DECLARE_ENTRY_POINT(StartBodyTracking2);
OCULUS_DECLARE_ENTRY_POINT(RequestBodyTrackingFidelity);
OCULUS_DECLARE_ENTRY_POINT(ResetBodyTrackingCalibration);
OCULUS_DECLARE_ENTRY_POINT(SuggestBodyTrackingCalibrationOverride);
OCULUS_DECLARE_ENTRY_POINT(GetFaceTracking2Enabled);
OCULUS_DECLARE_ENTRY_POINT(GetFaceTracking2Supported);
OCULUS_DECLARE_ENTRY_POINT(GetFaceState2);
OCULUS_DECLARE_ENTRY_POINT(StartFaceTracking2);
OCULUS_DECLARE_ENTRY_POINT(StopFaceTracking2);
OCULUS_DECLARE_ENTRY_POINT(GetEyeTrackingEnabled);
OCULUS_DECLARE_ENTRY_POINT(GetEyeTrackingSupported);
OCULUS_DECLARE_ENTRY_POINT(GetEyeGazesState);
OCULUS_DECLARE_ENTRY_POINT(StartEyeTracking);
OCULUS_DECLARE_ENTRY_POINT(StopEyeTracking);
OCULUS_DECLARE_ENTRY_POINT(GetFaceTrackingVisemesSupported);
OCULUS_DECLARE_ENTRY_POINT(GetFaceTrackingVisemesEnabled);
OCULUS_DECLARE_ENTRY_POINT(GetFaceVisemesState);
OCULUS_DECLARE_ENTRY_POINT(SetFaceTrackingVisemesEnabled);
// QPL
OCULUS_DECLARE_ENTRY_POINT(QplMarkerStart);
OCULUS_DECLARE_ENTRY_POINT(QplMarkerEnd);
OCULUS_DECLARE_ENTRY_POINT(QplMarkerPoint);
OCULUS_DECLARE_ENTRY_POINT(QplMarkerPointCached);
OCULUS_DECLARE_ENTRY_POINT(QplMarkerAnnotation);
OCULUS_DECLARE_ENTRY_POINT(QplCreateMarkerHandle);
OCULUS_DECLARE_ENTRY_POINT(QplDestroyMarkerHandle);
OCULUS_DECLARE_ENTRY_POINT(OnEditorShutdown);
OCULUS_DECLARE_ENTRY_POINT(QplSetConsent);
// OVR_Plugin_Insight.h
OCULUS_DECLARE_ENTRY_POINT(InitializeInsightPassthrough);
OCULUS_DECLARE_ENTRY_POINT(ShutdownInsightPassthrough);
OCULUS_DECLARE_ENTRY_POINT(GetInsightPassthroughInitialized);
OCULUS_DECLARE_ENTRY_POINT(GetInsightPassthroughInitializationState);
OCULUS_DECLARE_ENTRY_POINT(CreateInsightTriangleMesh);
OCULUS_DECLARE_ENTRY_POINT(DestroyInsightTriangleMesh);
OCULUS_DECLARE_ENTRY_POINT(AddInsightPassthroughSurfaceGeometry);
OCULUS_DECLARE_ENTRY_POINT(DestroyInsightPassthroughGeometryInstance);
OCULUS_DECLARE_ENTRY_POINT(UpdateInsightPassthroughGeometryTransform);
OCULUS_DECLARE_ENTRY_POINT(SetInsightPassthroughStyle);
OCULUS_DECLARE_ENTRY_POINT(SetInsightPassthroughStyle2);
OCULUS_DECLARE_ENTRY_POINT(GetPassthroughCapabilityFlags);
OCULUS_DECLARE_ENTRY_POINT(CreatePassthroughColorLut);
OCULUS_DECLARE_ENTRY_POINT(DestroyPassthroughColorLut);
OCULUS_DECLARE_ENTRY_POINT(UpdatePassthroughColorLut);
OCULUS_DECLARE_ENTRY_POINT(GetPassthroughCapabilities);
OCULUS_DECLARE_ENTRY_POINT(GetPassthroughPreferences);
// OVR_Plugin_MixedReality.h
OCULUS_DECLARE_ENTRY_POINT(InitializeMixedReality);
OCULUS_DECLARE_ENTRY_POINT(ShutdownMixedReality);
OCULUS_DECLARE_ENTRY_POINT(GetMixedRealityInitialized);
OCULUS_DECLARE_ENTRY_POINT(UpdateExternalCamera);
OCULUS_DECLARE_ENTRY_POINT(GetExternalCameraCount);
OCULUS_DECLARE_ENTRY_POINT(GetExternalCameraName);
OCULUS_DECLARE_ENTRY_POINT(GetExternalCameraIntrinsics);
OCULUS_DECLARE_ENTRY_POINT(GetExternalCameraExtrinsics);
// OVR_Plugin_Media.h
OCULUS_DECLARE_ENTRY_POINT(Media_Initialize);
OCULUS_DECLARE_ENTRY_POINT(Media_Shutdown);
OCULUS_DECLARE_ENTRY_POINT(Media_GetInitialized);
OCULUS_DECLARE_ENTRY_POINT(Media_Update);
OCULUS_DECLARE_ENTRY_POINT(Media_GetMrcActivationMode);
OCULUS_DECLARE_ENTRY_POINT(Media_SetMrcActivationMode);
OCULUS_DECLARE_ENTRY_POINT(Media_IsMrcEnabled);
OCULUS_DECLARE_ENTRY_POINT(Media_IsMrcActivated);
OCULUS_DECLARE_ENTRY_POINT(Media_UseMrcDebugCamera);
OCULUS_DECLARE_ENTRY_POINT(Media_SetMrcInputVideoBufferType);
OCULUS_DECLARE_ENTRY_POINT(Media_GetMrcInputVideoBufferType);
OCULUS_DECLARE_ENTRY_POINT(Media_SetMrcFrameSize);
OCULUS_DECLARE_ENTRY_POINT(Media_GetMrcFrameSize);
OCULUS_DECLARE_ENTRY_POINT(Media_SetMrcAudioSampleRate);
OCULUS_DECLARE_ENTRY_POINT(Media_GetMrcAudioSampleRate);
OCULUS_DECLARE_ENTRY_POINT(Media_SetMrcFrameImageFlipped);
OCULUS_DECLARE_ENTRY_POINT(Media_GetMrcFrameImageFlipped);
OCULUS_DECLARE_ENTRY_POINT(Media_SetMrcFrameInverseAlpha);
OCULUS_DECLARE_ENTRY_POINT(Media_GetMrcFrameInverseAlpha);
OCULUS_DECLARE_ENTRY_POINT(Media_SetAvailableQueueIndexVulkan);
OCULUS_DECLARE_ENTRY_POINT(Media_EncodeMrcFrame);
OCULUS_DECLARE_ENTRY_POINT(Media_EncodeMrcFrameWithDualTextures);
OCULUS_DECLARE_ENTRY_POINT(Media_SyncMrcFrame);
OCULUS_DECLARE_ENTRY_POINT(Media_EncodeMrcFrameWithPoseTime);
OCULUS_DECLARE_ENTRY_POINT(Media_EncodeMrcFrameDualTexturesWithPoseTime);
OCULUS_DECLARE_ENTRY_POINT(Media_SetHeadsetControllerPose);
OCULUS_DECLARE_ENTRY_POINT(Media_EnumerateCameraAnchorHandles);
OCULUS_DECLARE_ENTRY_POINT(Media_GetCurrentCameraAnchorHandle);
OCULUS_DECLARE_ENTRY_POINT(Media_GetCameraAnchorName);
OCULUS_DECLARE_ENTRY_POINT(Media_GetCameraAnchorHandle);
OCULUS_DECLARE_ENTRY_POINT(Media_GetCameraAnchorType);
OCULUS_DECLARE_ENTRY_POINT(Media_CreateCustomCameraAnchor);
OCULUS_DECLARE_ENTRY_POINT(Media_DestroyCustomCameraAnchor);
OCULUS_DECLARE_ENTRY_POINT(Media_GetCustomCameraAnchorPose);
OCULUS_DECLARE_ENTRY_POINT(Media_SetCustomCameraAnchorPose);
OCULUS_DECLARE_ENTRY_POINT(Media_GetCameraMinMaxDistance);
OCULUS_DECLARE_ENTRY_POINT(Media_SetCameraMinMaxDistance);
OCULUS_DECLARE_ENTRY_POINT(SetControllerDrivenHandPoses);
OCULUS_DECLARE_ENTRY_POINT(SetControllerDrivenHandPosesAreNatural);
static bool InitializeOculusPluginWrapper(OculusPluginWrapper* wrapper);
static void DestroyOculusPluginWrapper(OculusPluginWrapper* wrapper);
private:
ovrpVersion ovrpHeaderVersion;
bool Initialized;
};
#undef OCULUS_DECLARE_ENTRY_POINT

View File

@@ -0,0 +1,130 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRQPL.h"
#include "OculusXRHMDModule.h"
#include "OculusXRPluginWrapper.h"
namespace OculusXRTelemetry
{
namespace QPL
{
bool MarkerStart(const int MarkerId, const FTelemetryInstanceKey InstanceKey, const FTelemetryTimestamp Timestamp)
{
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().QplMarkerStart(
MarkerId,
InstanceKey.GetValue(),
Timestamp.GetTimestamp());
return OVRP_SUCCESS(Result);
}
bool MarkerEnd(const int MarkerId, const EAction Action, const FTelemetryInstanceKey InstanceKey, const FTelemetryTimestamp Timestamp)
{
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().QplMarkerEnd(
MarkerId,
static_cast<short>(Action),
InstanceKey.GetValue(),
Timestamp.GetTimestamp());
return OVRP_SUCCESS(Result);
}
bool MarkerPoint(const int MarkerId, const char* Name, const FTelemetryInstanceKey InstanceKey, const FTelemetryTimestamp Timestamp)
{
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().QplMarkerPoint(
MarkerId,
Name,
InstanceKey.GetValue(),
Timestamp.GetTimestamp());
return OVRP_SUCCESS(Result);
}
bool MarkerPointCached(const int MarkerId, const int NameHandle, const FTelemetryInstanceKey InstanceKey, const FTelemetryTimestamp Timestamp)
{
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().QplMarkerPointCached(
MarkerId,
NameHandle,
InstanceKey.GetValue(),
Timestamp.GetTimestamp());
return OVRP_SUCCESS(Result);
}
bool MarkerAnnotation(const int MarkerId, const char* AnnotationKey, const char* AnnotationValue, const FTelemetryInstanceKey InstanceKey)
{
if (nullptr == AnnotationValue)
{
return false;
}
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().QplMarkerAnnotation(
MarkerId,
AnnotationKey,
AnnotationValue,
InstanceKey.GetValue());
return OVRP_SUCCESS(Result);
}
bool CreateMarkerHandle(const char* Name, int* NameHandle)
{
if (nullptr == NameHandle)
{
return false;
}
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().QplCreateMarkerHandle(
Name,
NameHandle);
return OVRP_SUCCESS(Result);
}
bool DestroyMarkerHandle(const int NameHandle)
{
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().QplDestroyMarkerHandle(
NameHandle);
return OVRP_SUCCESS(Result);
}
bool OnEditorShutdown()
{
const ovrpResult Result = FOculusXRHMDModule::GetPluginWrapper().OnEditorShutdown();
return OVRP_SUCCESS(Result);
}
} // namespace QPL
bool FQPLBackend::MarkerStart(const int MarkerId, const FTelemetryInstanceKey InstanceKey, const FTelemetryTimestamp Timestamp)
{
return QPL::MarkerStart(MarkerId, InstanceKey, Timestamp);
}
bool FQPLBackend::MarkerEnd(const int MarkerId, const EAction Action, const FTelemetryInstanceKey InstanceKey, const FTelemetryTimestamp Timestamp)
{
return QPL::MarkerEnd(MarkerId, Action, InstanceKey, Timestamp);
};
bool FQPLBackend::MarkerPoint(const int MarkerId, const char* Name, const FTelemetryInstanceKey InstanceKey, const FTelemetryTimestamp Timestamp)
{
return QPL::MarkerPoint(MarkerId, Name, InstanceKey, Timestamp);
};
bool FQPLBackend::MarkerPointCached(const int MarkerId, const int NameHandle, const FTelemetryInstanceKey InstanceKey, const FTelemetryTimestamp Timestamp)
{
return QPL::MarkerPointCached(MarkerId, NameHandle, InstanceKey, Timestamp);
};
bool FQPLBackend::MarkerAnnotation(const int MarkerId, const char* AnnotationKey, const char* AnnotationValue, const FTelemetryInstanceKey InstanceKey)
{
return QPL::MarkerAnnotation(MarkerId, AnnotationKey, AnnotationValue, InstanceKey);
};
bool FQPLBackend::CreateMarkerHandle(const char* Name, int* NameHandle)
{
return QPL::CreateMarkerHandle(Name, NameHandle);
};
bool FQPLBackend::DestroyMarkerHandle(const int NameHandle)
{
return QPL::DestroyMarkerHandle(NameHandle);
};
bool FQPLBackend::OnEditorShutdown()
{
return QPL::OnEditorShutdown();
};
} // namespace OculusXRTelemetry

View File

@@ -0,0 +1,27 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRResourceHolder.h"
#include "HeadMountedDisplayTypes.h" // for LogHMD
#include "UObject/ConstructorHelpers.h"
#include "Materials/Material.h"
//////////////////////////////////////////////////////////////////////////
// UOculusResourceManager
UOculusXRResourceHolder::UOculusXRResourceHolder(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
static ConstructorHelpers::FObjectFinder<UMaterial> StaticPokeAHoleMaterial(TEXT("/OculusXR/Materials/PokeAHoleMaterial"));
PokeAHoleMaterial = StaticPokeAHoleMaterial.Object;
if (!PokeAHoleMaterial)
{
UE_LOG(LogHMD, Error, TEXT("Unable to load PokeAHoleMaterial"));
}
else
{
UE_LOG(LogHMD, Log, TEXT("PokeAHoleMaterial loaded successfully"));
}
}

View File

@@ -0,0 +1,22 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "Materials/MaterialInterface.h"
#include "OculusXRResourceHolder.generated.h"
/**
*
*/
UCLASS()
class UOculusXRResourceHolder : public UObject
{
GENERATED_UCLASS_BODY()
public:
UPROPERTY()
UMaterial* PokeAHoleMaterial;
};

View File

@@ -0,0 +1,193 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRSceneCaptureCubemap.h"
#include "OculusXRHMDPrivate.h"
#include "IImageWrapper.h"
#include "IImageWrapperModule.h"
#include "Kismet/GameplayStatics.h"
#include "GameFramework/PlayerController.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Engine/World.h"
#include "Engine/StaticMeshActor.h"
#include "Engine/TextureRenderTarget2D.h"
#include "TextureResource.h"
#include "HAL/FileManager.h"
#include "Misc/FileHelper.h"
#include "XRThreadUtils.h"
#include "RenderingThread.h"
//-------------------------------------------------------------------------------------------------
// UOculusXRSceneCaptureCubemap
//-------------------------------------------------------------------------------------------------
UOculusXRSceneCaptureCubemap::UOculusXRSceneCaptureCubemap()
: Stage(None)
, CaptureBoxSideRes(2048)
, CaptureFormat(EPixelFormat::PF_A16B16G16R16)
, OverriddenLocation(FVector::ZeroVector)
, OverriddenOrientation(FQuat::Identity)
, CaptureOffset(FVector::ZeroVector)
{
}
void UOculusXRSceneCaptureCubemap::StartCapture(UWorld* World, uint32 InCaptureBoxSideRes, EPixelFormat InFormat)
{
CaptureBoxSideRes = InCaptureBoxSideRes;
CaptureFormat = InFormat;
FVector Location = OverriddenLocation;
FQuat Orientation = OverriddenOrientation;
APlayerController* CapturePlayerController = UGameplayStatics::GetPlayerController(GWorld, 0);
if (CapturePlayerController)
{
FRotator Rotation;
CapturePlayerController->GetPlayerViewPoint(Location, Rotation);
Rotation.Pitch = Rotation.Roll = 0;
Orientation = FQuat(Rotation);
Location += CaptureOffset;
}
if (!OverriddenOrientation.IsIdentity())
{
Orientation = OverriddenOrientation;
}
if (!OverriddenLocation.IsZero())
{
Location = OverriddenLocation;
}
const FVector ZAxis(0, 0, 1);
const FVector YAxis(0, 1, 0);
const FQuat FaceOrientations[] = { { ZAxis, PI / 2 }, { ZAxis, -PI / 2 }, // right, left
{ YAxis, -PI / 2 }, { YAxis, PI / 2 }, // top, bottom
{ ZAxis, 0 }, { ZAxis, -PI } }; // front, back
for (int i = 0; i < 6; ++i)
{
USceneCaptureComponent2D* CaptureComponent = NewObject<USceneCaptureComponent2D>();
CaptureComponent->SetVisibility(true);
CaptureComponent->SetHiddenInGame(false);
CaptureComponent->FOVAngle = 90.f;
CaptureComponent->bCaptureEveryFrame = true;
CaptureComponent->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;
const FName TargetName = MakeUniqueObjectName(this, UTextureRenderTarget2D::StaticClass(), TEXT("SceneCaptureTextureTarget"));
CaptureComponent->TextureTarget = NewObject<UTextureRenderTarget2D>(this, TargetName);
CaptureComponent->TextureTarget->InitCustomFormat(CaptureBoxSideRes, CaptureBoxSideRes, CaptureFormat, false);
CaptureComponents.Add(CaptureComponent);
CaptureComponent->RegisterComponentWithWorld(GWorld);
CaptureComponent->SetWorldLocationAndRotation(Location, Orientation * FaceOrientations[i]);
CaptureComponent->UpdateContent();
}
Stage = SettingPos;
FActorSpawnParameters SpawnInfo;
SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
SpawnInfo.bNoFail = true;
SpawnInfo.ObjectFlags = RF_Transient;
AStaticMeshActor* InGameActor;
InGameActor = World->SpawnActor<AStaticMeshActor>(SpawnInfo);
OutputDir = FPaths::ProjectSavedDir() + TEXT("/Cubemaps");
IFileManager::Get().MakeDirectory(*OutputDir);
}
void UOculusXRSceneCaptureCubemap::Tick(float DeltaTime)
{
ExecuteOnRenderThread([]() {
TickRenderingTickables();
});
if (Stage == SettingPos)
{
Stage = Capturing;
return;
}
// Read Whole Capture Buffer
IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
TArray<FColor> OneFaceSurface, WholeCubemapData;
OneFaceSurface.AddUninitialized(CaptureBoxSideRes * CaptureBoxSideRes);
WholeCubemapData.AddUninitialized(CaptureBoxSideRes * 6 * CaptureBoxSideRes);
// Read pixels
for (int cubeFaceIdx = 0; cubeFaceIdx < 6; ++cubeFaceIdx)
{
auto RenderTarget = CaptureComponents[cubeFaceIdx]->TextureTarget->GameThread_GetRenderTargetResource();
RenderTarget->ReadPixelsPtr(OneFaceSurface.GetData(), FReadSurfaceDataFlags());
// enforce alpha to be 1
for (FColor& Color : OneFaceSurface)
{
Color.A = 255;
}
// copy subimage into whole cubemap array
const uint32 Stride = CaptureBoxSideRes * 6;
const uint32 XOff = cubeFaceIdx * CaptureBoxSideRes;
const uint32 StripSizeInBytes = CaptureBoxSideRes * sizeof(FColor);
for (uint32 y = 0; y < CaptureBoxSideRes; ++y)
{
FMemory::Memcpy(WholeCubemapData.GetData() + XOff + y * Stride, OneFaceSurface.GetData() + y * CaptureBoxSideRes, StripSizeInBytes);
}
}
ImageWrapper->SetRaw(WholeCubemapData.GetData(), WholeCubemapData.GetAllocatedSize(), CaptureBoxSideRes * 6, CaptureBoxSideRes, ERGBFormat::BGRA, 8);
const TArray64<uint8>& PNGData = ImageWrapper->GetCompressed(100);
const FString Filename = OutputDir + FString::Printf(TEXT("/Cubemap-%d-%s.png"), CaptureBoxSideRes, *FDateTime::Now().ToString(TEXT("%m.%d-%H.%M.%S")));
FFileHelper::SaveArrayToFile(PNGData, *Filename);
check(Stage == Capturing);
Stage = Finished;
for (int i = 0; i < CaptureComponents.Num(); ++i)
{
CaptureComponents[i]->UnregisterComponent();
}
CaptureComponents.SetNum(0);
RemoveFromRoot(); // We're done here, so remove ourselves from the root set. @TODO: Fix this later
}
#if !UE_BUILD_SHIPPING
void UOculusXRSceneCaptureCubemap::CaptureCubemapCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar)
{
bool bCreateOculusMobileCubemap = false;
FVector CaptureOffset(FVector::ZeroVector);
float Yaw = 0.f;
for (const FString& Arg : Args)
{
FParse::Value(*Arg, TEXT("XOFF="), CaptureOffset.X);
FParse::Value(*Arg, TEXT("YOFF="), CaptureOffset.Y);
FParse::Value(*Arg, TEXT("ZOFF="), CaptureOffset.Z);
FParse::Value(*Arg, TEXT("YAW="), Yaw);
if (Arg.Equals(TEXT("MOBILE"), ESearchCase::IgnoreCase))
{
bCreateOculusMobileCubemap = true;
}
}
UOculusXRSceneCaptureCubemap* CubemapCapturer = NewObject<UOculusXRSceneCaptureCubemap>();
CubemapCapturer->AddToRoot(); // TODO: Don't add the object to the GC root
CubemapCapturer->SetOffset((FVector)CaptureOffset);
if (Yaw != 0.f)
{
FRotator Rotation(FRotator::ZeroRotator);
Rotation.Yaw = Yaw;
const FQuat Orient(Rotation);
CubemapCapturer->SetInitialOrientation(Orient);
}
const uint32 CaptureHeight = 2048;
CubemapCapturer->StartCapture(World, bCreateOculusMobileCubemap ? CaptureHeight / 2 : CaptureHeight);
}
#endif

View File

@@ -0,0 +1,79 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "OculusXRHMDPrivate.h"
#include "UObject/ObjectMacros.h"
#include "Tickable.h"
#include "OculusXRSceneCaptureCubemap.generated.h"
//-------------------------------------------------------------------------------------------------
// UOculusXRSceneCaptureCubemap
//-------------------------------------------------------------------------------------------------
class USceneCaptureComponent2D;
UCLASS()
class UOculusXRSceneCaptureCubemap : public UObject, public FTickableGameObject
{
GENERATED_BODY()
public:
UOculusXRSceneCaptureCubemap();
virtual void Tick(float DeltaTime) override;
virtual bool IsTickable() const override
{
return CaptureComponents.Num() != 0 && Stage != None;
}
virtual bool IsTickableWhenPaused() const override
{
return IsTickable();
}
virtual TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(USceneCapturer, STATGROUP_Tickables);
}
// init capture params and start
void StartCapture(UWorld* World, uint32 InCaptureBoxSideRes, EPixelFormat InFormat = EPixelFormat::PF_A16B16G16R16);
// sets offset for the capture, in UU, relatively to current player 0 location
void SetOffset(FVector InOffset) { CaptureOffset = InOffset; }
// overrides player's 0 orientation for the capture.
void SetInitialOrientation(const FQuat& InOrientation) { OverriddenOrientation = InOrientation; }
// overrides player's 0 location for the capture.
void SetInitialLocation(FVector InLocation) { OverriddenLocation = InLocation; }
bool IsFinished() const { return Stage == Finished; }
bool IsCapturing() const { return Stage == Capturing || Stage == SettingPos; }
#if !UE_BUILD_SHIPPING
static void CaptureCubemapCommandHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar);
#endif // UE_BUILD_SHIPPING
private:
enum EStage
{
None,
SettingPos,
Capturing,
Finished
} Stage;
UPROPERTY()
TArray<USceneCaptureComponent2D*> CaptureComponents;
uint32 CaptureBoxSideRes;
EPixelFormat CaptureFormat;
FString OutputDir;
FVector OverriddenLocation; // overridden location of the capture, world coordinates, UU
FQuat OverriddenOrientation; // overridden orientation of the capture. Full orientation is used (not only yaw, like with player's rotation).
FVector CaptureOffset; // offset relative to current player's 0 location
};

View File

@@ -0,0 +1,515 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRSimulator.h"
#if PLATFORM_WINDOWS
#include "HttpModule.h"
#include "Interfaces/IHttpResponse.h"
#include "Interfaces/IHttpRequest.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "HAL/PlatformFilemanager.h"
#include "GenericPlatform/GenericPlatformFile.h"
#include "libzip/zip.h"
#include "HAL/FileManager.h"
#include "OculusXRHMDRuntimeSettings.h"
#include "OculusXRTelemetryEvents.h"
#include "Misc/MessageDialog.h"
#include "OpenXR/OculusXROpenXRUtilities.h"
#include "Internationalization/Regex.h"
#include "Windows/WindowsPlatformMisc.h"
#include "Interfaces/IPluginManager.h"
#include "Framework/Notifications/NotificationManager.h"
#include "Widgets/Notifications/SNotificationList.h"
#if WITH_EDITOR
#include "UnrealEdMisc.h"
#endif // WITH_EDITOR
const FString OpenXrRuntimeEnvKey = "XR_RUNTIME_JSON";
const FString PreviousOpenXrRuntimeEnvKey = "XR_RUNTIME_JSON_PREV";
namespace
{
class FZipArchiveReader
{
public:
FZipArchiveReader(IFileHandle* InFileHandle);
~FZipArchiveReader();
bool IsValid() const;
TArray<FString> GetFileNames() const;
bool TryReadFile(FStringView FileName, TArray<uint8>& OutData) const;
private:
TMap<FString, zip_int64_t> EmbeddedFileToIndex;
IFileHandle* FileHandle = nullptr;
zip_source_t* ZipFileSource = nullptr;
zip_t* ZipFile = nullptr;
uint64 FilePos = 0;
uint64 FileSize = 0;
void Destruct();
zip_int64_t ZipSourceFunctionReader(void* OutData, zip_uint64_t DataLen, zip_source_cmd_t Command);
static zip_int64_t ZipSourceFunctionReaderStatic(void* InUserData, void* OutData, zip_uint64_t DataLen,
zip_source_cmd_t Command);
};
FZipArchiveReader::FZipArchiveReader(IFileHandle* InFileHandle)
: FileHandle(InFileHandle)
{
if (!FileHandle)
{
Destruct();
return;
}
if (FileHandle->Tell() != 0)
{
FileHandle->Seek(0);
}
FilePos = 0;
FileSize = FileHandle->Size();
zip_error_t ZipError;
zip_error_init(&ZipError);
ZipFileSource = zip_source_function_create(ZipSourceFunctionReaderStatic, this, &ZipError);
if (!ZipFileSource)
{
zip_error_fini(&ZipError);
Destruct();
return;
}
zip_error_init(&ZipError);
ZipFile = zip_open_from_source(ZipFileSource, ZIP_RDONLY, &ZipError);
if (!ZipFile)
{
zip_error_fini(&ZipError);
Destruct();
return;
}
zip_int64_t NumberOfFiles = zip_get_num_entries(ZipFile, 0);
if (NumberOfFiles < 0 || MAX_int32 < NumberOfFiles)
{
Destruct();
return;
}
EmbeddedFileToIndex.Reserve(NumberOfFiles);
// produce the manifest file first in case the operation gets canceled while unzipping
for (zip_int64_t i = 0; i < NumberOfFiles; i++)
{
zip_stat_t ZipFileStat;
if (zip_stat_index(ZipFile, i, 0, &ZipFileStat) != 0)
{
Destruct();
return;
}
zip_uint64_t ValidStat = ZipFileStat.valid;
if (!(ValidStat & ZIP_STAT_NAME))
{
Destruct();
return;
}
EmbeddedFileToIndex.Add(FString(ANSI_TO_TCHAR(ZipFileStat.name)), i);
}
}
FZipArchiveReader::~FZipArchiveReader()
{
Destruct();
}
void FZipArchiveReader::Destruct()
{
EmbeddedFileToIndex.Empty();
if (ZipFile)
{
zip_close(ZipFile);
ZipFile = nullptr;
}
if (ZipFileSource)
{
zip_source_close(ZipFileSource);
ZipFileSource = nullptr;
}
delete FileHandle;
FileHandle = nullptr;
}
bool FZipArchiveReader::IsValid() const
{
return ZipFile != nullptr;
}
TArray<FString> FZipArchiveReader::GetFileNames() const
{
TArray<FString> Result;
EmbeddedFileToIndex.GenerateKeyArray(Result);
return Result;
}
bool FZipArchiveReader::TryReadFile(FStringView FileName, TArray<uint8>& OutData) const
{
OutData.Reset();
const zip_int64_t* Index = EmbeddedFileToIndex.FindByHash(GetTypeHash(FileName), FileName);
if (!Index)
{
return false;
}
zip_stat_t ZipFileStat;
if (zip_stat_index(ZipFile, *Index, 0, &ZipFileStat) != 0)
{
return false;
}
if (!(ZipFileStat.valid & ZIP_STAT_SIZE))
{
return false;
}
if (ZipFileStat.size == 0)
{
return true;
}
if (ZipFileStat.size > MAX_int32)
{
return false;
}
OutData.SetNumUninitialized(ZipFileStat.size, EAllowShrinking::No);
zip_file* EmbeddedFile = zip_fopen_index(ZipFile, *Index, 0 /* flags */);
if (!EmbeddedFile)
{
OutData.Reset();
return false;
}
bool bReadSuccess = zip_fread(EmbeddedFile, OutData.GetData(), ZipFileStat.size) == ZipFileStat.size;
zip_fclose(EmbeddedFile);
if (!bReadSuccess)
{
OutData.Reset();
return false;
}
return true;
}
zip_int64_t FZipArchiveReader::ZipSourceFunctionReaderStatic(
void* InUserData, void* OutData, zip_uint64_t DataLen, zip_source_cmd_t Command)
{
return reinterpret_cast<FZipArchiveReader*>(InUserData)->ZipSourceFunctionReader(OutData, DataLen, Command);
}
zip_int64_t FZipArchiveReader::ZipSourceFunctionReader(
void* OutData, zip_uint64_t DataLen, zip_source_cmd_t Command)
{
switch (Command)
{
case ZIP_SOURCE_OPEN:
return 0;
case ZIP_SOURCE_READ:
if (FilePos == FileSize)
{
return 0;
}
DataLen = FMath::Min(static_cast<zip_uint64_t>(FileSize - FilePos), DataLen);
if (!FileHandle->Read(reinterpret_cast<uint8*>(OutData), DataLen))
{
return 0;
}
FilePos += DataLen;
return DataLen;
case ZIP_SOURCE_CLOSE:
return 0;
case ZIP_SOURCE_STAT:
{
zip_stat_t* OutStat = reinterpret_cast<zip_stat_t*>(OutData);
zip_stat_init(OutStat);
OutStat->size = FileSize;
OutStat->comp_size = FileSize;
OutStat->comp_method = ZIP_CM_STORE;
OutStat->encryption_method = ZIP_EM_NONE;
OutStat->valid = ZIP_STAT_SIZE | ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD | ZIP_STAT_ENCRYPTION_METHOD;
return sizeof(*OutStat);
}
case ZIP_SOURCE_ERROR:
{
zip_uint32_t* OutLibZipError = reinterpret_cast<zip_uint32_t*>(OutData);
zip_uint32_t* OutSystemError = OutLibZipError + 1;
*OutLibZipError = ZIP_ER_INTERNAL;
*OutSystemError = 0;
return 2 * sizeof(*OutLibZipError);
}
case ZIP_SOURCE_FREE:
return 0;
case ZIP_SOURCE_SEEK:
{
zip_int64_t NewOffset = zip_source_seek_compute_offset(FilePos, FileSize, OutData, DataLen, nullptr);
if (NewOffset < 0 || FileSize < static_cast<uint64>(NewOffset))
{
return -1;
}
if (!FileHandle->Seek(NewOffset))
{
return -1;
}
FilePos = NewOffset;
return 0;
}
case ZIP_SOURCE_TELL:
return static_cast<zip_int64_t>(FilePos);
case ZIP_SOURCE_SUPPORTS:
return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT,
ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_SUPPORTS, -1);
default:
return 0;
}
}
bool Unzip(const FString& Path, const FString& TargetPath, const TSharedPtr<SNotificationItem>& Notification)
{
IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
IFileHandle* ArchiveFileHandle = FileManager.OpenRead(*Path);
const FZipArchiveReader ZipArchiveReader(ArchiveFileHandle);
if (!ZipArchiveReader.IsValid())
{
return false;
}
const TArray<FString> ArchiveFiles = ZipArchiveReader.GetFileNames();
uint64 Size = ArchiveFiles.Num();
uint64 Index = 0;
for (const FString& FileName : ArchiveFiles)
{
Index++;
if (Notification.IsValid())
{
Notification->SetText(FText::FromString(FString::Format(TEXT("Unzipping {0} / {1}"), { Index, Size })));
}
if (FileName.EndsWith("/") || FileName.EndsWith("\\"))
continue;
if (TArray<uint8> FileBuffer; ZipArchiveReader.TryReadFile(FileName, FileBuffer))
{
if (!FFileHelper::SaveArrayToFile(FileBuffer, *(TargetPath / FileName)))
{
return false;
}
}
}
return true;
}
} // namespace
bool FMetaXRSimulator::IsSimulatorActivated()
{
FString MetaXRSimPath = GetSimulatorJsonPath();
FString CurRuntimePath = FWindowsPlatformMisc::GetEnvironmentVariable(*OpenXrRuntimeEnvKey);
return (!MetaXRSimPath.IsEmpty() && MetaXRSimPath == CurRuntimePath);
}
void FMetaXRSimulator::ToggleOpenXRRuntime()
{
OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FSimulator> Event;
FString MetaXRSimPath = GetSimulatorJsonPath();
if (!IFileManager::Get().FileExists(*MetaXRSimPath))
{
InstallSimulator(ToggleOpenXRRuntime);
UE_LOG(LogMetaXRSim, Log, TEXT("Meta XR Simulator Not Installed.\nInstalling Meta XR Simulator."));
return;
}
#if WITH_EDITOR
if (OculusXR::IsOpenXRSystem())
{
FString ActivationText = IsSimulatorActivated() ? "deactivate" : "activate";
FString Message = FString::Format(TEXT("A restart is required in order to {0} XR simulator. The restart must be performed from this dialog, opening and closing the editor manually will not work. Restart now?"), { ActivationText });
if (FMessageDialog::Open(EAppMsgType::OkCancel, FText::FromString(Message)) == EAppReturnType::Cancel)
{
UE_LOG(LogMetaXRSim, Log, TEXT("Meta XR Simulator %s action canceled."), *ActivationText);
const auto& NotEnd = Event.SetResult(OculusXRTelemetry::EAction::Fail).AddAnnotation("reason", "restart canceled");
return;
}
}
#endif // WITH_EDITOR
if (IsSimulatorActivated())
{
// Deactivate MetaXR Simulator
FString PrevOpenXrRuntimeEnvKey = FWindowsPlatformMisc::GetEnvironmentVariable(*PreviousOpenXrRuntimeEnvKey);
FWindowsPlatformMisc::SetEnvironmentVar(*PreviousOpenXrRuntimeEnvKey,
TEXT(""));
FWindowsPlatformMisc::SetEnvironmentVar(*OpenXrRuntimeEnvKey, *PrevOpenXrRuntimeEnvKey);
UE_LOG(LogMetaXRSim, Log, TEXT("Meta XR Simulator is deactivated. (%s : %s)"), *OpenXrRuntimeEnvKey, *PrevOpenXrRuntimeEnvKey);
const auto& NotEnd = Event.AddAnnotation("action", "deactivated");
}
else
{
// Activate MetaXR Simulator
FString CurOpenXrRuntimeEnvKey = FWindowsPlatformMisc::GetEnvironmentVariable(*OpenXrRuntimeEnvKey);
FWindowsPlatformMisc::SetEnvironmentVar(*PreviousOpenXrRuntimeEnvKey,
*CurOpenXrRuntimeEnvKey);
FWindowsPlatformMisc::SetEnvironmentVar(*OpenXrRuntimeEnvKey, *MetaXRSimPath);
UE_LOG(LogMetaXRSim, Log, TEXT("Meta XR Simulator is activated. (%s : %s)"), *OpenXrRuntimeEnvKey, *MetaXRSimPath);
const auto& NotEnd = Event.AddAnnotation("action", "activated");
}
#if WITH_EDITOR
if (OculusXR::IsOpenXRSystem())
{
FUnrealEdMisc::Get().RestartEditor(false);
}
#endif // WITH_EDITOR
}
FString FMetaXRSimulator::GetSimulatorJsonPath()
{
return FPaths::Combine(GetPackagePath(), TEXT("meta_openxr_simulator.json"));
}
bool FMetaXRSimulator::IsSimulatorInstalled()
{
return FPaths::FileExists(GetSimulatorJsonPath());
}
void FMetaXRSimulator::TryActivateOnStartup()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS && WITH_EDITOR
// If -HMDSimulator is used as the command option to launch UE, use simulator runtime instead of the physical HMD runtime (like PC-Link).
if (FParse::Param(FCommandLine::Get(), TEXT("HMDSimulator")))
{
if (IsSimulatorActivated())
{
return;
}
ToggleOpenXRRuntime();
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS && WITH_EDITOR
}
FString FMetaXRSimulator::GetPackagePath()
{
return FPaths::Combine(FPlatformMisc::GetEnvironmentVariable(TEXT("LOCALAPPDATA")), TEXT("MetaXR"), TEXT("MetaXRSimulator"), GetVersion());
}
void FMetaXRSimulator::InstallSimulator(const TFunction<void()>& OnSuccess)
{
FNotificationInfo Progress(FText::FromString("Installing Meta XR Simulator..."));
Progress.bFireAndForget = false;
Progress.FadeInDuration = 0.5f;
Progress.FadeOutDuration = 0.5f;
Progress.ExpireDuration = 5.0f;
Progress.bUseThrobber = true;
Progress.bUseSuccessFailIcons = true;
TSharedPtr<SNotificationItem> NotificationItem = FSlateNotificationManager::Get().AddNotification(Progress);
if (NotificationItem.IsValid())
{
NotificationItem->SetCompletionState(SNotificationItem::CS_Pending);
}
auto DestinationFolder = GetPackagePath();
auto DownloadPath = FPaths::Combine(FPaths::EngineSavedDir(), TEXT("Downloads"), TEXT("MetaXRSimulator"), GetVersion(), TEXT("MetaXRSimulator.zip"));
if (FPaths::FileExists(DownloadPath))
{
UnzipSimulator(DownloadPath, DestinationFolder, NotificationItem, OnSuccess);
return;
}
TSharedPtr<IHttpRequest, ESPMode::ThreadSafe> Request = FHttpModule::Get().CreateRequest();
Request->OnProcessRequestComplete().BindLambda([DownloadPath, DestinationFolder, NotificationItem, OnSuccess](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) {
Request->OnRequestProgress64().Unbind();
if (Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode()))
{
// Save the downloaded zip file
FFileHelper::SaveArrayToFile(Response->GetContent(), *DownloadPath);
if (NotificationItem.IsValid())
{
NotificationItem->SetText(FText::FromString("Unzipping ... "));
}
UnzipSimulator(DownloadPath, DestinationFolder, NotificationItem, OnSuccess);
return;
}
UE_LOG(LogMetaXRSim, Error, TEXT("Failed to install Meta XR Simulator."));
if (NotificationItem.IsValid())
{
NotificationItem->SetText(FText::FromString("Installation failed!"));
NotificationItem->SetCompletionState(SNotificationItem::CS_Fail);
NotificationItem->ExpireAndFadeout();
}
});
Request->OnRequestProgress64().BindLambda([NotificationItem](const FHttpRequestPtr& Request, uint64 /* BytesSent */, uint64 BytesReceived) {
uint64 ContentLength = Request->GetResponse()->GetContentLength();
if (NotificationItem.IsValid())
{
NotificationItem->SetText(FText::FromString(FString::Format(TEXT("Downloading {0} / {1}"), { BytesReceived, ContentLength })));
}
});
Request->SetURL("https://www.facebook.com/horizon_devcenter_download?app_id=28549923061320041&sdk_version=" + GetVersion());
Request->SetVerb(TEXT("GET"));
Request->ProcessRequest();
}
FString FMetaXRSimulator::GetVersion()
{
TSharedPtr<IPlugin> Plugin = IPluginManager::Get().FindPlugin(TEXT("OculusXR"));
if (Plugin.IsValid())
{
FString VersionName = Plugin->GetDescriptor().VersionName;
TArray<FString> ParsedParts;
VersionName.ParseIntoArray(ParsedParts, TEXT("."), true);
return FString::FromInt(FCString::Atoi(*ParsedParts[1]) - 32);
}
return "0";
}
void FMetaXRSimulator::UnzipSimulator(const FString& Path, const FString& TargetPath, const TSharedPtr<SNotificationItem>& Notification,
const TFunction<void()>& OnSuccess)
{
bool bSuccess = Unzip(Path, TargetPath, Notification);
if (!bSuccess || !IsSimulatorInstalled())
{
UE_LOG(LogMetaXRSim, Error, TEXT("Failed to unzip the file."));
if (Notification.IsValid())
{
Notification->SetText(FText::FromString("Installation failed!"));
Notification->SetCompletionState(SNotificationItem::CS_Fail);
Notification->ExpireAndFadeout();
}
return;
}
if (Notification.IsValid())
{
Notification->SetText(FText::FromString("Installation succeeded!"));
Notification->SetCompletionState(SNotificationItem::CS_Success);
Notification->ExpireAndFadeout();
}
if (OnSuccess)
{
OnSuccess();
}
}
#endif // PLATFORM_WINDOWS

View File

@@ -0,0 +1,30 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#if PLATFORM_WINDOWS
#include "Widgets/Notifications/SNotificationList.h"
DEFINE_LOG_CATEGORY_STATIC(LogMetaXRSim, Log, All);
/** */
class FMetaXRSimulator
{
public:
static bool IsSimulatorActivated();
static void ToggleOpenXRRuntime();
static void TryActivateOnStartup();
static FString GetPackagePath();
static bool IsSimulatorInstalled();
private:
static FString GetSimulatorJsonPath();
static void InstallSimulator(const TFunction<void()>& OnSuccess);
static FString GetVersion();
static void UnzipSimulator(const FString& Path, const FString& TargetPath, const TSharedPtr<SNotificationItem>& Notification, const TFunction<void()>& OnSuccess);
};
#endif

View File

@@ -0,0 +1,85 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRStereoLayersFlagsSupplier.h"
#include "IStereoLayers.h"
#include "OVR_Plugin_Types.h"
TSharedPtr<FOculusXRStereoLayersFlagsSupplier> FOculusXRStereoLayersFlagsSupplier::Instance = NULL;
TSharedPtr<FOculusXRStereoLayersFlagsSupplier> FOculusXRStereoLayersFlagsSupplier::Get()
{
if (!Instance.IsValid())
{
Instance = MakeShared<FOculusXRStereoLayersFlagsSupplier>();
}
return Instance;
}
FOculusXRStereoLayersFlagsSupplier::FOculusXRStereoLayersFlagsSupplier()
{
#if !UE_VERSION_OLDER_THAN(5, 4, 0)
uint32 Value = IStereoLayers::ELayerFlags::LAYER_FLAG_MAX_VALUE << 1;
UnrealFlagValOvrpFlagValMap.Add(Value, ovrpLayerFlag_BicubicFiltering);
SupportedFilters.Add(FName("Bicubic Filtering"));
Value = Value << 1;
UnrealFlagValOvrpFlagValMap.Add(Value, ovrpLayerSubmitFlag_AutoLayerFilter);
SupportedFilters.Add(FName("Auto Filtering"));
Value = Value << 1;
UnrealFlagValOvrpFlagValMap.Add(Value, ovrpLayerSubmitFlag_EfficientSuperSample);
SupportedFilters.Add(FName("Normal SuperSampling"));
Value = Value << 1;
UnrealFlagValOvrpFlagValMap.Add(Value, ovrpLayerSubmitFlag_ExpensiveSuperSample);
SupportedFilters.Add(FName("Quality SuperSampling"));
Value = Value << 1;
UnrealFlagValOvrpFlagValMap.Add(Value, ovrpLayerSubmitFlag_EfficientSharpen);
SupportedFilters.Add(FName("Normal Sharpening"));
Value = Value << 1;
UnrealFlagValOvrpFlagValMap.Add(Value, ovrpLayerSubmitFlag_QualitySharpen);
SupportedFilters.Add(FName("Quality Sharpening"));
#endif // !UE_VERSION_OLDER_THAN(5, 4, 0)
}
int FOculusXRStereoLayersFlagsSupplier::GetOvrpFlag(uint32 DescFlags, bool bMQSR)
{
int LayerSubmitFlags = 0;
#if !UE_VERSION_OLDER_THAN(5, 4, 0)
for (auto& FlagPair : UnrealFlagValOvrpFlagValMap)
{
if (bMQSR && FlagPair.Value == ovrpLayerFlag_BicubicFiltering)
{
continue;
}
else if (!bMQSR && FlagPair.Value != ovrpLayerFlag_BicubicFiltering)
{
continue;
}
if (DescFlags & FlagPair.Key)
{
LayerSubmitFlags |= FlagPair.Value;
}
}
if (bMQSR)
{ // validate if autofilter conflicts with supersampling and sharpening
bool bAutoFiltering = LayerSubmitFlags & ovrpLayerSubmitFlag_AutoLayerFilter;
bool bSuperSamplingType = ((LayerSubmitFlags & ovrpLayerSubmitFlag_EfficientSuperSample) || (LayerSubmitFlags & ovrpLayerSubmitFlag_ExpensiveSuperSample));
bool bSSharpenType = ((LayerSubmitFlags & ovrpLayerSubmitFlag_EfficientSharpen) || (LayerSubmitFlags & ovrpLayerSubmitFlag_QualitySharpen));
if (!bAutoFiltering && bSuperSamplingType && bSSharpenType)
{
UE_LOG(LogTemp, Error, TEXT("XR sharpening and supersampling cannot be enabled simultaneously.\n Either enable autofiltering or disable one of the options."));
return 0;
}
}
#endif // !UE_VERSION_OLDER_THAN(5, 4, 0)
return LayerSubmitFlags;
}

View File

@@ -0,0 +1,176 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRSyntheticEnvironmentServer.h"
#include "OculusXRSimulator.h"
#if PLATFORM_WINDOWS
#include "HAL/FileManager.h"
#include "Internationalization/Regex.h"
#include "OculusXRHMDRuntimeSettings.h"
#include "OculusXRTelemetryEvents.h"
#include "Misc/FileHelper.h"
#include "Misc/MessageDialog.h"
#include "Windows/WindowsPlatformMisc.h"
const FString SynthEnvServer = "Synthetic Environment Server";
const FString LocalSharingServer = "Local Sharing Server";
FProcHandle FMetaXRSES::EnvProcHandle;
FProcHandle FMetaXRSES::LSSProcHandle;
void FMetaXRSES::StopServer()
{
StopProcess(EnvProcHandle, SynthEnvServer);
StopProcess(LSSProcHandle, LocalSharingServer);
}
void FMetaXRSES::LaunchEnvironment(int32 EnvironmentIndex)
{
const FString EnvironmentName = SynthEnvRooms[EnvironmentIndex].SynthName;
const FString SESPath = SynthEnvRooms[EnvironmentIndex].Executable;
if (FMetaXRSimulator::GetPackagePath().IsEmpty() || SESPath.IsEmpty() || EnvironmentName.IsEmpty())
{
return;
}
StopServer();
OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FSimulator> Event;
const bool bLaunched = LaunchProcess(SESPath, EnvironmentName, LocalSharingServer, EnvProcHandle);
const auto& _ = Event.SetResult(bLaunched ? OculusXRTelemetry::EAction::Success : OculusXRTelemetry::EAction::Fail).AddAnnotation("launch", StringCast<ANSICHAR>(*EnvironmentName).Get());
LaunchLocalSharingServer();
}
void FMetaXRSES::LaunchLocalSharingServer()
{
OculusXRTelemetry::TScopedMarker<OculusXRTelemetry::Events::FSimulator> Event;
FString LSSPath = GetLocalSharingServerPath();
const bool bLaunched = LaunchProcess(LSSPath, "", LocalSharingServer, LSSProcHandle);
const auto& _ = Event.SetResult(bLaunched ? OculusXRTelemetry::EAction::Success : OculusXRTelemetry::EAction::Fail).AddAnnotation("launch", "localsharingserver");
}
bool FMetaXRSES::LaunchProcess(FString BinaryPath, FString Arguments, FString LogContext, FProcHandle& OutProcHandle)
{
if (!IFileManager::Get().FileExists(*BinaryPath))
{
UE_LOG(LogMetaXRSES, Error, TEXT("Failed to find %s."), *BinaryPath);
return false;
}
UE_LOG(LogMetaXRSES, Log, TEXT("Launching %s."), *BinaryPath);
uint32 OutProcessId = 0;
OutProcHandle = FPlatformProcess::CreateProc(*BinaryPath, *Arguments, false, false, false, &OutProcessId, 0, NULL, NULL);
if (!OutProcHandle.IsValid())
{
UE_LOG(LogMetaXRSES, Error, TEXT("Failed to launch %s."), *BinaryPath);
FPlatformProcess::CloseProc(OutProcHandle);
return false;
}
UE_LOG(LogMetaXRSES, Log, TEXT("Launched %s."), *BinaryPath);
return true;
}
void FMetaXRSES::StopProcess(FProcHandle& ProcHandle, FString LogContext)
{
if (ProcHandle.IsValid())
{
if (FPlatformProcess::IsProcRunning(ProcHandle))
{
UE_LOG(LogMetaXRSES, Log, TEXT("Stopping %s."), *LogContext);
FPlatformProcess::TerminateProc(ProcHandle);
}
FPlatformProcess::CloseProc(ProcHandle);
}
else
{
UE_LOG(LogMetaXRSES, Warning, TEXT("Failed to stop process %s because it is not active anymore."), *LogContext);
}
}
TArray<FMetaXRSES::ServerInfo> FMetaXRSES::SynthEnvRooms = {};
bool FMetaXRSES::SynthEnvParsed = false;
TArray<FMetaXRSES::ServerInfo> GatherServers(const FString& path)
{
TArray<FMetaXRSES::ServerInfo> servers{};
TArray<FString> batFiles{};
const FString ext("bat");
IFileManager::Get().FindFiles(batFiles, *path, *ext);
const static FRegexPattern pattern(TEXT("start (\\S+) (\\S+)"));
for (const FString& stem : batFiles)
{
const FString file = path + "/" + stem;
FString result;
if (!FFileHelper::LoadFileToString(result, *file))
{
continue;
}
FRegexMatcher matcher(pattern, result);
if (!matcher.FindNext())
{
continue;
}
UE_LOG(LogMetaXRSES, Warning, TEXT("In %s, found %s && %s"), *file, *matcher.GetCaptureGroup(1), *matcher.GetCaptureGroup(2));
FString left;
stem.Split(TEXT("."), &left, NULL);
auto absPath = path + "/" + matcher.GetCaptureGroup(1);
servers.Push({ left, matcher.GetCaptureGroup(2), IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(absPath.GetCharArray().GetData()) });
}
return servers;
}
TArray<FMetaXRSES::ServerInfo>& FMetaXRSES::GetSynthEnvRooms()
{
if (SynthEnvParsed)
{
return SynthEnvRooms;
}
SynthEnvRooms = {};
FString dirPath = FMetaXRSimulator::GetPackagePath();
if (dirPath.IsEmpty())
{
return SynthEnvRooms;
}
TArray<FString> dirNames;
IFileManager::Get().IterateDirectory(dirPath.GetCharArray().GetData(), [&dirNames](const TCHAR* name, bool) -> bool {
dirNames.Add(name);
return true;
});
for (auto dir : dirNames)
{
// find all of the servers in the subdirectories
SynthEnvRooms.Append(GatherServers(dir));
}
SynthEnvParsed = true;
return SynthEnvRooms;
}
FString FMetaXRSES::GetLocalSharingServerPath()
{
FString Path = FMetaXRSimulator::GetPackagePath() + "/local_sharing_server~/local_sharing_server.exe";
if (!IFileManager::Get().FileExists(*Path))
{
UE_LOG(LogMetaXRSES, Warning, TEXT("Failed to find %s, trying the previous version"), *Path);
Path = FMetaXRSimulator::GetPackagePath() + "/.local_sharing_server/local_sharing_server.exe";
if (!IFileManager::Get().FileExists(*Path))
{
UE_LOG(LogMetaXRSES, Error, TEXT("Failed to find LocalSharingServer, giving up"));
Path = "";
}
}
return Path;
}
#endif

View File

@@ -0,0 +1,41 @@
// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#if PLATFORM_WINDOWS
DEFINE_LOG_CATEGORY_STATIC(LogMetaXRSES, Log, All);
struct FProcHandle;
/** */
class FMetaXRSES
{
public:
static void LaunchEnvironment(int32 EnvironmentIndex);
static void StopServer();
struct ServerInfo
{
FString GuiName;
FString SynthName;
FString Executable;
};
OCULUSXRHMD_API static TArray<ServerInfo>& GetSynthEnvRooms();
private:
static void LaunchLocalSharingServer();
static bool LaunchProcess(FString BinaryPath, FString Arguments, FString LogContext, FProcHandle& OutProcHandle);
static void StopProcess(FProcHandle& ProcHandle, FString LogContext);
static FString GetLocalSharingServerPath();
static FProcHandle EnvProcHandle;
static FProcHandle LSSProcHandle;
static TArray<ServerInfo> SynthEnvRooms;
static bool SynthEnvParsed;
};
#endif

View File

@@ -0,0 +1,93 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRTelemetry.h"
#include "OculusXRHMDModule.h"
#include "OculusXRTelemetryPrivacySettings.h"
#include "Async/Async.h"
#include "GeneralProjectSettings.h"
namespace OculusXRTelemetry
{
namespace
{
const char* TelemetrySource = "UE5Integration";
}
bool IsActive()
{
#if OCULUS_HMD_SUPPORTED_PLATFORMS
if constexpr (FTelemetryBackend::IsNullBackend())
{
return false;
}
// IsActive() can be called during shutdown, after the FOculusXRHMDModule has been unloaded.
// This means we can't use the checked FOculusXRHMDModule::Get(), we instead need to use a fallible
// GetModule(..) and check that the module exists.
FOculusXRHMDModule* HMDModule = static_cast<FOculusXRHMDModule*>(FModuleManager::Get().GetModule(IOculusXRHMDModule::NAME_OculusXRHMD));
if (HMDModule && HMDModule->IsOVRPluginAvailable() && FOculusXRHMDModule::GetPluginWrapper().IsInitialized())
{
return true;
}
#endif // OCULUS_HMD_SUPPORTED_PLATFORMS
return false;
}
void IfActiveThen(TUniqueFunction<void()> Function)
{
AsyncTask(ENamedThreads::GameThread, [F = MoveTemp(Function)]() {
if (IsActive())
{
F();
}
});
}
void PropagateTelemetryConsent()
{
#ifdef WITH_EDITOR
if (FOculusXRHMDModule::Get().IsOVRPluginAvailable() && FOculusXRHMDModule::GetPluginWrapper().IsInitialized())
{
const bool bHasConsent = FOculusXRHMDModule::GetPluginWrapper().GetUnifiedConsent(UNREAL_TOOL_ID) == ovrpBool_True;
FOculusXRHMDModule::GetPluginWrapper().QplSetConsent(bHasConsent);
FOculusXRHMDModule::GetPluginWrapper().SetDeveloperTelemetryConsent(bHasConsent);
}
#endif
}
FString GetProjectId()
{
const UGeneralProjectSettings& ProjectSettings = *GetDefault<UGeneralProjectSettings>();
return ProjectSettings.ProjectID.ToString();
}
bool IsConsentGiven()
{
#ifdef WITH_EDITOR
if (const UOculusXRTelemetryPrivacySettings* EditorPrivacySettings = GetDefault<UOculusXRTelemetryPrivacySettings>())
{
return EditorPrivacySettings->bIsEnabled;
}
#endif
return false;
}
void SendEvent(const TCHAR* EventName, float Param)
{
const FString StrVal = FString::Printf(TEXT("%f"), Param);
SendEvent(EventName, *StrVal);
}
void SendEvent(const TCHAR* EventName, bool bParam)
{
SendEvent(EventName, bParam ? TEXT("true") : TEXT("false"));
}
void SendEvent(const TCHAR* EventName, const TCHAR* Param)
{
if (IsActive())
{
FOculusXRHMDModule::GetPluginWrapper().SendEvent2(TCHAR_TO_ANSI(EventName), TCHAR_TO_ANSI(Param), TelemetrySource);
}
}
} // namespace OculusXRTelemetry

View File

@@ -0,0 +1,14 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "OculusXRTelemetry.h"
namespace OculusXRTelemetry::Events
{
using FEditorConsent = TMarker<191965622>;
using FSimulator = TMarker<191963436>;
using FEnableHardOcclusions = TMarker<191958638>;
using FEnableSoftOcclusions = TMarker<191958877>;
constexpr const char* ConsentOriginKey = "Origin";
} // namespace OculusXRTelemetry::Events

View File

@@ -0,0 +1,124 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#include "OculusXRTelemetryPrivacySettings.h"
#include "OculusXRHMDModule.h"
#include "OculusXRTelemetry.h"
#include <regex>
#define LOCTEXT_NAMESPACE "OculusXRTelemetryPrivacySettings"
constexpr int CONSENT_NOTIFICATION_MAX_LENGTH = 1024;
TMap<FString, FString> GetLinks(const std::string& markdown, std::string& textWithoutLink)
{
TMap<FString, FString> links;
const std::regex linkRegex(R"(\[(.*?)\]\((.*?)\))");
std::smatch matches;
std::string::const_iterator searchStart(markdown.cbegin());
while (std::regex_search(searchStart, markdown.cend(), matches, linkRegex))
{
links.Add(matches[1].str().c_str(), matches[2].str().c_str());
searchStart = matches.suffix().first;
}
const std::string linkReplacement = "";
textWithoutLink = std::regex_replace(markdown, linkRegex, linkReplacement);
return links;
}
UOculusXRTelemetryPrivacySettings::UOculusXRTelemetryPrivacySettings(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
if (!FOculusXRHMDModule::Get().IsOVRPluginAvailable() || !FOculusXRHMDModule::GetPluginWrapper().IsInitialized())
{
return;
}
bIsEnabled = FOculusXRHMDModule::GetPluginWrapper().GetUnifiedConsent(OculusXRTelemetry::UNREAL_TOOL_ID) == ovrpBool_True;
char SettingsText[CONSENT_NOTIFICATION_MAX_LENGTH];
if (FOculusXRHMDModule::GetPluginWrapper().GetConsentSettingsChangeText(SettingsText) == ovrpFailure)
{
return;
}
std::string settingsDesc = "";
Links = GetLinks(SettingsText, settingsDesc);
Description = FText::FromString(settingsDesc.c_str());
}
void UOculusXRTelemetryPrivacySettings::GetToggleCategoryAndPropertyNames(FName& OutCategory, FName& OutProperty) const
{
OutCategory = FName("Options");
OutProperty = FName("bIsEnabled");
}
FText UOculusXRTelemetryPrivacySettings::GetFalseStateLabel() const
{
return LOCTEXT("FalseStateLabel", "Only share essential data");
}
FText UOculusXRTelemetryPrivacySettings::GetFalseStateTooltip() const
{
return LOCTEXT("FalseStateTooltip", "Only share essential data");
}
FText UOculusXRTelemetryPrivacySettings::GetFalseStateDescription() const
{
return Description;
}
FText UOculusXRTelemetryPrivacySettings::GetTrueStateLabel() const
{
return LOCTEXT("TrueStateLabel", "Share additional data");
}
FText UOculusXRTelemetryPrivacySettings::GetTrueStateTooltip() const
{
return LOCTEXT("TrueStateTooltip", "Share additional data");
}
FText UOculusXRTelemetryPrivacySettings::GetTrueStateDescription() const
{
return Description;
}
FString UOculusXRTelemetryPrivacySettings::GetAdditionalInfoUrl() const
{
if (Links.Num() > 0)
{
return Links.begin().Value();
}
return FString();
}
FText UOculusXRTelemetryPrivacySettings::GetAdditionalInfoUrlLabel() const
{
if (Links.Num() > 0)
{
return FText::FromString(Links.begin().Key());
}
return FText();
}
#if WITH_EDITOR
void UOculusXRTelemetryPrivacySettings::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
const FName PropertyName = (PropertyChangedEvent.Property != nullptr) ? PropertyChangedEvent.Property->GetFName() : NAME_None;
if (PropertyName == GET_MEMBER_NAME_CHECKED(UOculusXRTelemetryPrivacySettings, bIsEnabled))
{
using namespace OculusXRTelemetry;
if (FOculusXRHMDModule::Get().IsOVRPluginAvailable() && FOculusXRHMDModule::GetPluginWrapper().IsInitialized())
{
FOculusXRHMDModule::GetPluginWrapper().SaveUnifiedConsent(UNREAL_TOOL_ID, bIsEnabled ? ovrpBool_True : ovrpBool_False);
PropagateTelemetryConsent();
}
}
}
#endif
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,40 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "Engine/ImportantToggleSettingInterface.h"
#include "OculusXRTelemetryPrivacySettings.generated.h"
UCLASS(MinimalAPI, hidecategories = Object, config = EditorSettings)
class UOculusXRTelemetryPrivacySettings : public UObject, public IImportantToggleSettingInterface
{
GENERATED_UCLASS_BODY()
UPROPERTY(EditAnywhere, Category = Options)
bool bIsEnabled = false;
public:
// BEGIN IImportantToggleSettingInterface
virtual void GetToggleCategoryAndPropertyNames(FName& OutCategory, FName& OutProperty) const override;
virtual FText GetFalseStateLabel() const override;
virtual FText GetFalseStateTooltip() const override;
virtual FText GetFalseStateDescription() const override;
virtual FText GetTrueStateLabel() const override;
virtual FText GetTrueStateTooltip() const override;
virtual FText GetTrueStateDescription() const override;
virtual FString GetAdditionalInfoUrl() const override;
virtual FText GetAdditionalInfoUrlLabel() const override;
// END IImportantToggleSettingInterface
#if WITH_EDITOR
//~ Begin UObject Interface
virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
//~ End UObject Interface
#endif // WITH_EDITOR
private:
FText Description;
TMap<FString, FString> Links;
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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