Giant_Beast_2025/Plugins/MetaXR/Source/OculusXRHMD/Private/OpenXR/OculusXRLayerExtensionPlugin.cpp

345 lines
12 KiB
C++

// 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