2025-03-10 09:43:27 +08:00
// Copyright 2023 PICO Inc. All Rights Reserved.
# include "PICO_HMD.h"
# include "PICOOpenXRRuntimeSettings.h"
# include "OpenXRCore.h"
# include "IXRTrackingSystem.h"
# include "OpenXRCore.h"
# include "IOpenXRHMDModule.h"
# include "OpenXRHMD_Swapchain.h"
# include "OpenXRHMD_RenderBridge.h"
# include "PixelShaderUtils.h"
# include "Shader.h"
# include "ClearQuad.h"
# include "ScreenRendering.h"
# include "PICO_Shaders.h"
# include "HDRHelper.h"
# include "DataDrivenShaderPlatformInfo.h"
# include "TextureResource.h"
# include "GameFramework/Actor.h"
# include "PICO_MRCCamera.h"
# include "Runtime/Launch/Resources/Version.h"
# if PLATFORM_ANDROID
# include <dlfcn.h>
# endif //PLATFORM_ANDROID
static TAutoConsoleVariable < int32 > CVarPICOEnableSuperResolution (
TEXT ( " r.Mobile.PICO.EnableSuperResolution " ) ,
0 ,
TEXT ( " 0: Disable SuperResolution (Default) \n " )
TEXT ( " 1: Enable SuperResolution on supported platforms \n " ) ,
ECVF_Scalability | ECVF_RenderThreadSafe ) ;
static TAutoConsoleVariable < int32 > CVarPICOSharpeningSetting (
TEXT ( " r.Mobile.PICO.SharpeningSetting " ) ,
0 ,
TEXT ( " 0: Disable Sharpening (Default) \n " )
TEXT ( " 1: Enable NormalSharpening on supported platforms \n " )
TEXT ( " 2: Enable QualitySharpening on supported platforms \n " ) ,
ECVF_Scalability | ECVF_RenderThreadSafe ) ;
static TAutoConsoleVariable < int32 > CVarPICOSharpeningEnhanceMode (
TEXT ( " r.Mobile.PICO.SharpeningEnhanceMode " ) ,
0 ,
TEXT ( " 0: Disable Sharpening EnhanceMode (Default) \n " )
TEXT ( " 1: Enable Fixed Foveated on supported platforms \n " )
TEXT ( " 2: Enable Adaptive on supported platforms \n " )
TEXT ( " 3: Enable FixedFoveated and Adaptive on supported platforms \n " ) ,
ECVF_Scalability | ECVF_RenderThreadSafe ) ;
FHMDPICO : : FHMDPICO ( )
: LoaderHandle ( nullptr )
, bPICORuntime ( false )
, Instance ( XR_NULL_HANDLE )
, System ( XR_NULL_SYSTEM_ID )
, Session ( XR_NULL_HANDLE )
, bSupportLocalFloorLevelEXT ( false )
, bSupportDisplayRefreshRate ( false )
, CurrentDisplayRefreshRate ( 0 )
, bContentProtectEnabled ( false )
, CurrentDisplayTime ( 0 )
, IsSupportsUserPresence ( false )
, WornState ( EHMDWornState : : Type : : Unknown )
, bSupportMRCExtension ( false )
, bIsMRCRunning ( false )
, MRCDebugMode ( FMRCDebugModePICO ( ) )
, bSupportedBDCompositionLayerSettingsExt ( false )
{
SupportedDisplayRefreshRates . Empty ( ) ;
}
void FHMDPICO : : Register ( )
{
RegisterOpenXRExtensionModularFeature ( ) ;
WorldLoadDelegate = FCoreUObjectDelegates : : PostLoadMapWithWorld . AddRaw ( this , & FHMDPICO : : OnWorldCreated ) ;
}
void FHMDPICO : : Unregister ( )
{
UnregisterOpenXRExtensionModularFeature ( ) ;
if ( LoaderHandle )
{
FPlatformProcess : : FreeDllHandle ( LoaderHandle ) ;
LoaderHandle = nullptr ;
}
if ( WorldLoadDelegate . IsValid ( ) )
{
FCoreUObjectDelegates : : PostLoadMapWithWorld . Remove ( WorldLoadDelegate ) ;
WorldLoadDelegate . Reset ( ) ;
}
}
bool FHMDPICO : : GetCustomLoader ( PFN_xrGetInstanceProcAddr * OutGetProcAddr )
{
# if PLATFORM_ANDROID
// clear errors
dlerror ( ) ;
LoaderHandle = FPlatformProcess : : GetDllHandle ( TEXT ( " libopenxr_loader_pico.so " ) ) ;
if ( LoaderHandle = = nullptr )
{
UE_LOG ( LogPICOOpenXRHMD , Error , TEXT ( " Unable to load libopenxr_loader_pico.so, error %s " ) , ANSI_TO_TCHAR ( dlerror ( ) ) ) ;
return false ;
}
// clear errors
dlerror ( ) ;
PFN_xrGetInstanceProcAddr xrGetInstanceProcAddrPtr = ( PFN_xrGetInstanceProcAddr ) FPlatformProcess : : GetDllExport ( LoaderHandle , TEXT ( " xrGetInstanceProcAddr " ) ) ;
if ( xrGetInstanceProcAddrPtr = = nullptr )
{
UE_LOG ( LogPICOOpenXRHMD , Error , TEXT ( " Unable to load OpenXR xrGetInstanceProcAddr, error %s " ) , ANSI_TO_TCHAR ( dlerror ( ) ) ) ;
return false ;
}
* OutGetProcAddr = xrGetInstanceProcAddrPtr ;
extern struct android_app * GNativeAndroidApp ;
PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR ;
xrGetInstanceProcAddrPtr ( XR_NULL_HANDLE , " xrInitializeLoaderKHR " , ( PFN_xrVoidFunction * ) & xrInitializeLoaderKHR ) ;
if ( xrInitializeLoaderKHR = = nullptr )
{
UE_LOG ( LogPICOOpenXRHMD , Error , TEXT ( " Unable to load OpenXR xrInitializeLoaderKHR " ) ) ;
return false ;
}
XrLoaderInitInfoAndroidKHR LoaderInitializeInfoAndroid ;
LoaderInitializeInfoAndroid . type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR ;
LoaderInitializeInfoAndroid . next = NULL ;
LoaderInitializeInfoAndroid . applicationVM = GNativeAndroidApp - > activity - > vm ;
LoaderInitializeInfoAndroid . applicationContext = GNativeAndroidApp - > activity - > clazz ;
XR_ENSURE ( xrInitializeLoaderKHR ( ( XrLoaderInitInfoBaseHeaderKHR * ) & LoaderInitializeInfoAndroid ) ) ;
//Used to determine whether the pico runtime loads successfully
{
PFN_xrEnumerateInstanceExtensionProperties xrEnumerateInstanceExtensionPropertiesPtr ;
xrGetInstanceProcAddrPtr ( XR_NULL_HANDLE , " xrEnumerateInstanceExtensionProperties " , ( PFN_xrVoidFunction * ) & xrEnumerateInstanceExtensionPropertiesPtr ) ;
if ( xrEnumerateInstanceExtensionPropertiesPtr = = nullptr )
{
UE_LOG ( LogPICOOpenXRHMD , Error , TEXT ( " Unable to load OpenXR xrEnumerateInstanceExtensionProperties! " ) ) ;
return false ;
}
uint32_t ExtensionsCount = 0 ;
if ( XR_FAILED ( xrEnumerateInstanceExtensionPropertiesPtr ( nullptr , 0 , & ExtensionsCount , nullptr ) ) )
{
UE_LOG ( LogPICOOpenXRHMD , Error , TEXT ( " xrEnumerateInstanceExtensionPropertiesPtr Failed! " ) ) ;
return false ;
}
}
UE_LOG ( LogPICOOpenXRHMD , Log , TEXT ( " Loaded PICO OpenXR Loader " ) ) ;
bPICORuntime = true ;
return true ;
# endif //PLATFORM_ANDROID
return false ;
}
bool FHMDPICO : : GetOptionalExtensions ( TArray < const ANSICHAR * > & OutExtensions )
{
OutExtensions . Add ( " XR_FB_display_refresh_rate " ) ;
OutExtensions . Add ( " XR_EXT_local_floor " ) ;
OutExtensions . Add ( " XR_FB_composition_layer_secure_content " ) ;
OutExtensions . Add ( " XR_EXT_performance_settings " ) ;
OutExtensions . Add ( " XR_EXT_user_presence " ) ;
return true ;
}
void FHMDPICO : : PostCreateInstance ( XrInstance InInstance )
{
Instance = InInstance ;
XrInstanceProperties InstanceProps = { XR_TYPE_INSTANCE_PROPERTIES , nullptr } ;
XR_ENSURE ( xrGetInstanceProperties ( InInstance , & InstanceProps ) ) ;
InstanceProps . runtimeName [ XR_MAX_RUNTIME_NAME_SIZE - 1 ] = 0 ; // Ensure the name is null terminated.
FString RuntimeName = FString ( InstanceProps . runtimeName ) ;
UE_LOG ( LogPICOOpenXRHMD , Log , TEXT ( " PICO OpenXR PostCreateInstance RuntimeName:%s " ) , * RuntimeName ) ;
}
void FHMDPICO : : PostGetSystem ( XrInstance InInstance , XrSystemId InSystem )
{
System = InSystem ;
bSupportDisplayRefreshRate = IOpenXRHMDModule : : Get ( ) . IsExtensionEnabled ( XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME ) ;
if ( bSupportDisplayRefreshRate )
{
XR_ENSURE ( xrGetInstanceProcAddr ( InInstance , " xrEnumerateDisplayRefreshRatesFB " , ( PFN_xrVoidFunction * ) & xrEnumerateDisplayRefreshRatesFB ) ) ;
XR_ENSURE ( xrGetInstanceProcAddr ( InInstance , " xrGetDisplayRefreshRateFB " , ( PFN_xrVoidFunction * ) & xrGetDisplayRefreshRateFB ) ) ;
XR_ENSURE ( xrGetInstanceProcAddr ( InInstance , " xrRequestDisplayRefreshRateFB " , ( PFN_xrVoidFunction * ) & xrRequestDisplayRefreshRateFB ) ) ;
}
bSupportLocalFloorLevelEXT = IOpenXRHMDModule : : Get ( ) . IsExtensionEnabled ( XR_EXT_LOCAL_FLOOR_EXTENSION_NAME ) ;
bSupportPerformanceSettingsEXT = IOpenXRHMDModule : : Get ( ) . IsExtensionEnabled ( XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME ) ;
if ( bSupportPerformanceSettingsEXT )
{
XR_ENSURE ( xrGetInstanceProcAddr ( InInstance , " xrPerfSettingsSetPerformanceLevelEXT " , ( PFN_xrVoidFunction * ) & xrPerfSettingsSetPerformanceLevelEXT ) ) ;
}
if ( IOpenXRHMDModule : : Get ( ) . IsExtensionEnabled ( XR_EXT_USER_PRESENCE_EXTENSION_NAME ) )
{
XrSystemUserPresencePropertiesEXT SystemUserPresenceProperties = { XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT } ;
XrSystemProperties systemProperties = { XR_TYPE_SYSTEM_PROPERTIES , & SystemUserPresenceProperties } ;
XR_ENSURE ( xrGetSystemProperties ( InInstance , System , & systemProperties ) ) ;
IsSupportsUserPresence = SystemUserPresenceProperties . supportsUserPresence = = XR_TRUE ;
}
}
bool FHMDPICO : : IsStandaloneStereoOnlyDevice ( )
{
# if PLATFORM_ANDROID
FString DeviceMake = FAndroidMisc : : GetDeviceMake ( ) . ToLower ( ) ;
UE_LOG ( LogPICOOpenXRHMD , Verbose , TEXT ( " DeviceMake:%s " ) , * DeviceMake ) ;
return DeviceMake = = FString ( " pico " ) ;
# else
return false ;
# endif
}
const void * FHMDPICO : : OnCreateSession ( XrInstance InInstance , XrSystemId InSystem , const void * InNext )
{
static FName SystemName ( TEXT ( " OpenXR " ) ) ;
if ( GEngine - > XRSystem . IsValid ( ) & & ( GEngine - > XRSystem - > GetSystemName ( ) = = SystemName ) )
{
OpenXRHMD = ( FOpenXRHMD * ) GEngine - > XRSystem . Get ( ) ;
}
EnableContentProtect ( GetMutableDefault < UPICOOpenXRRuntimeSettings > ( ) - > bContentProtectEXT ) ;
return InNext ;
}
float ConvertDisplayRefreshRate ( EDisplayRefreshRatePICO Rate )
{
switch ( Rate )
{
case EDisplayRefreshRatePICO : : Default :
return 0.0f ;
break ;
case EDisplayRefreshRatePICO : : Rate72 :
return 72.0f ;
break ;
case EDisplayRefreshRatePICO : : Rate90 :
return 90.0f ;
break ;
case EDisplayRefreshRatePICO : : Rate120 :
return 120.0f ;
break ;
}
return 0.0f ;
}
void FHMDPICO : : PostCreateSession ( XrSession InSession )
{
FReadScopeLock Lock ( SessionHandleMutex ) ;
Session = InSession ;
if ( bSupportDisplayRefreshRate )
{
uint32_t DisplayRefreshRateCountOutput = 0 ;
XR_ENSURE ( xrEnumerateDisplayRefreshRatesFB ( InSession , 0 , & DisplayRefreshRateCountOutput , nullptr ) ) ;
if ( DisplayRefreshRateCountOutput > 0 )
{
SupportedDisplayRefreshRates . SetNum ( DisplayRefreshRateCountOutput ) ;
XR_ENSURE ( xrEnumerateDisplayRefreshRatesFB ( InSession , SupportedDisplayRefreshRates . Num ( ) , & DisplayRefreshRateCountOutput , SupportedDisplayRefreshRates . GetData ( ) ) ) ;
for ( int i = 0 ; i < SupportedDisplayRefreshRates . Num ( ) ; i + + )
{
UE_LOG ( LogPICOOpenXRHMD , Log , TEXT ( " Supported DisplayRefreshRates[%d]:%f " ) , i , SupportedDisplayRefreshRates [ i ] ) ;
}
}
XR_ENSURE ( xrGetDisplayRefreshRateFB ( InSession , & CurrentDisplayRefreshRate ) ) ;
UE_LOG ( LogPICOOpenXRHMD , Log , TEXT ( " Get current default DisplayRefreshRate:%f " ) , CurrentDisplayRefreshRate ) ;
UPICOOpenXRRuntimeSettings * Settings = GetMutableDefault < UPICOOpenXRRuntimeSettings > ( ) ;
if ( Settings )
{
float RequestRate = ConvertDisplayRefreshRate ( Settings - > DisplayRefreshRate ) ;
SetDisplayRefreshRate ( RequestRate ) ;
}
}
if ( ViewTrackingSpace = = XR_NULL_HANDLE )
{
XrReferenceSpaceCreateInfo SpaceInfo ;
SpaceInfo . type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO ;
SpaceInfo . next = nullptr ;
SpaceInfo . referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW ;
SpaceInfo . poseInReferenceSpace = ToXrPose ( FTransform : : Identity ) ;
XR_ENSURE ( xrCreateReferenceSpace ( InSession , & SpaceInfo , & ViewTrackingSpace ) ) ;
}
}
void FHMDPICO : : OnDestroySession ( XrSession InSession )
{
if ( ViewTrackingSpace )
{
XR_ENSURE ( xrDestroySpace ( ViewTrackingSpace ) ) ;
}
ViewTrackingSpace = XR_NULL_HANDLE ;
if ( MRCSpace )
{
XR_ENSURE ( xrDestroySpace ( MRCSpace ) ) ;
}
MRCSpace = XR_NULL_HANDLE ;
}
bool FHMDPICO : : UseCustomReferenceSpaceType ( XrReferenceSpaceType & OutReferenceSpaceType )
{
if ( bSupportLocalFloorLevelEXT )
{
UPICOOpenXRRuntimeSettings * Settings = GetMutableDefault < UPICOOpenXRRuntimeSettings > ( ) ;
if ( Settings & & Settings - > bLocalFloorLevelEXT )
{
OutReferenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT ;
return true ;
}
}
return false ;
}
bool FHMDPICO : : 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
}
void FHMDPICO : : OnEvent ( XrSession InSession , const XrEventDataBaseHeader * InHeader )
{
const XrEventDataBuffer * EventDataBuffer = reinterpret_cast < const XrEventDataBuffer * > ( InHeader ) ;
if ( EventDataBuffer = = nullptr )
{
return ;
}
switch ( EventDataBuffer - > type )
{
case XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB :
if ( bSupportDisplayRefreshRate )
{
const XrEventDataDisplayRefreshRateChangedFB * DisplayRefreshRate = reinterpret_cast < const XrEventDataDisplayRefreshRateChangedFB * > ( EventDataBuffer ) ;
CurrentDisplayRefreshRate = DisplayRefreshRate - > toDisplayRefreshRate ;
UHMDFunctionLibraryPICO : : GetDelegateManagerPICO ( ) - > OnDeviceDisplayRefreshRateChanged . Broadcast ( DisplayRefreshRate - > fromDisplayRefreshRate , DisplayRefreshRate - > toDisplayRefreshRate ) ;
UE_LOG ( LogPICOOpenXRHMD , Log , TEXT ( " DisplayRefreshRate changed from %f to %f. " ) , DisplayRefreshRate - > fromDisplayRefreshRate , DisplayRefreshRate - > toDisplayRefreshRate ) ;
}
break ;
case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT :
if ( bSupportPerformanceSettingsEXT )
{
const XrEventDataPerfSettingsEXT * PerfSettings = reinterpret_cast < const XrEventDataPerfSettingsEXT * > ( EventDataBuffer ) ;
UHMDFunctionLibraryPICO : : GetDelegateManagerPICO ( ) - > OnDevicePerformanceSettingsChanged . Broadcast ( EPerfSettingsDomainPICO ( PerfSettings - > domain )
, EPerfSettingsSubDomainPICO ( PerfSettings - > subDomain )
, EPerfSettingsNotificationLevelPICO ( PerfSettings - > toLevel )
, EPerfSettingsNotificationLevelPICO ( PerfSettings - > fromLevel ) ) ;
UE_LOG ( LogPICOOpenXRHMD , Log , TEXT ( " PerformanceSettings level changed from %d to %d (domain:%d subdomain:%d " ) , PerfSettings - > fromLevel , PerfSettings - > toLevel , PerfSettings - > domain , PerfSettings - > subDomain ) ;
}
break ;
case XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT :
if ( IsSupportsUserPresence )
{
const XrEventDataUserPresenceChangedEXT * UserPresenceChanged = reinterpret_cast < const XrEventDataUserPresenceChangedEXT * > ( EventDataBuffer ) ;
if ( UserPresenceChanged - > isUserPresent )
{
WornState = EHMDWornState : : Type : : Worn ;
FCoreDelegates : : VRHeadsetPutOnHead . Broadcast ( ) ;
}
else
{
WornState = EHMDWornState : : Type : : NotWorn ;
FCoreDelegates : : VRHeadsetRemovedFromHead . Broadcast ( ) ;
}
}
break ;
default :
break ;
}
}
const void * FHMDPICO : : OnEndProjectionLayer ( XrSession InSession , int32 InLayerIndex , const void * InNext , XrCompositionLayerFlags & OutFlags )
{
OutFlags | = XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT ;
OutFlags | = XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT ;
if ( bContentProtectEnabled )
{
ContentProtect = { XR_TYPE_COMPOSITION_LAYER_SECURE_CONTENT_FB } ;
ContentProtect . next = const_cast < void * > ( InNext ) ;
ContentProtect . flags = XR_COMPOSITION_LAYER_SECURE_CONTENT_REPLACE_LAYER_BIT_FB ;
InNext = & ContentProtect ;
}
return InNext ;
}
void FHMDPICO : : UpdateDeviceLocations ( XrSession InSession , XrTime DisplayTime , XrSpace TrackingSpace )
{
CurrentDisplayTime = DisplayTime ;
CurrentBaseSpace = TrackingSpace ;
if ( bSupportMRCExtension & & IsInGameThread ( ) )
{
if ( bIsMRCRunning & & ! RCSceneCapture2DPICO )
{
if ( ! World & & GEngine )
{
const TIndirectArray < FWorldContext > & WorldContexts = GEngine - > GetWorldContexts ( ) ;
for ( const FWorldContext & Context : WorldContexts )
{
if ( ( ( Context . WorldType = = EWorldType : : PIE ) | | ( Context . WorldType = = EWorldType : : Game ) ) & & ( Context . World ( ) ! = NULL ) )
{
World = Context . World ( ) ;
break ;
}
}
}
if ( World )
{
FActorSpawnParameters SpawnInfo ;
SpawnInfo . SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod : : AlwaysSpawn ;
SpawnInfo . bNoFail = true ;
SpawnInfo . ObjectFlags = RF_Transient ;
RCSceneCapture2DPICO = World - > SpawnActor < AMRCCameraPICO > ( SpawnInfo ) ;
RCSceneCapture2DPICO - > SetActorEnableCollision ( false ) ;
}
}
else if ( ! bIsMRCRunning & & RCSceneCapture2DPICO )
{
RCSceneCapture2DPICO - > Destroy ( ) ;
RCSceneCapture2DPICO = nullptr ;
}
}
}
FIntRect FHMDPICO : : GetViewportSize ( const FOpenXRLayer : : FPerEyeTextureData & EyeData , const IStereoLayers : : FLayerDesc & Desc )
{
FBox2D Viewport ( EyeData . SwapchainSize * Desc . UVRect . Min , EyeData . SwapchainSize * Desc . UVRect . Max ) ;
return FIntRect ( Viewport . Min . IntPoint ( ) , Viewport . Max . IntPoint ( ) ) ;
}
FVector2D FHMDPICO : : GetQuadSize ( const FOpenXRLayer : : FPerEyeTextureData & EyeData , const IStereoLayers : : FLayerDesc & Desc )
{
if ( Desc . Flags & IStereoLayers : : LAYER_FLAG_QUAD_PRESERVE_TEX_RATIO )
{
float AspectRatio = EyeData . SwapchainSize . Y / EyeData . SwapchainSize . X ;
return FVector2D ( Desc . QuadSize . X , Desc . QuadSize . X * AspectRatio ) ;
}
return Desc . QuadSize ;
}
void FHMDPICO : : OnBeginRendering_RenderThread ( XrSession InSession )
{
if ( OpenXRHMD = = nullptr | | InSession = = XR_NULL_HANDLE )
{
return ;
}
FXRRenderBridge * RenderBridge = OpenXRHMD - > GetActiveRenderBridge_GameThread ( OpenXRHMD - > ShouldUseSeparateRenderTarget ( ) ) ;
FOpenXRRenderBridge * Bridge = static_cast < FOpenXRRenderBridge * > ( RenderBridge ) ;
uint8 UnusedActualFormat = 0 ;
ETextureCreateFlags Flags = TexCreate_Dynamic | TexCreate_SRGB | TexCreate_ShaderResource | TexCreate_RenderTargetable ;
FRHICommandListImmediate & RHICmdList = FRHICommandListExecutor : : GetImmediateCommandList ( ) ;
static const int64 OPENXR_SWAPCHAIN_WAIT_TIMEOUT = 100000000ll ; // 100ms in nanoseconds.
if ( bSupportMRCExtension & & bIsMRCRunning & & ! MRCLayer . IsValid ( ) )
{
if ( Bridge & & MRCLayerDesc . Texture . IsValid ( ) & & MRCLayerDesc . LeftTexture . IsValid ( ) )
{
MRCLayer = MakeShareable ( new FOpenXRLayer ( MRCLayerDesc ) ) ;
{
FRHITexture * Texture = MRCLayer - > Desc . Texture - > GetTexture2D ( ) ;
FXRSwapChainPtr SwapChain = Bridge - > CreateSwapchain ( InSession ,
IStereoRenderTargetManager : : GetStereoLayerPixelFormat ( ) ,
UnusedActualFormat ,
Texture - > GetSizeX ( ) ,
Texture - > GetSizeY ( ) ,
# ifdef PICO_CUSTOM_ENGINE
1 ,
# endif
1 ,
Texture - > GetNumMips ( ) ,
Texture - > GetNumSamples ( ) ,
Texture - > GetFlags ( ) | Flags ,
Texture - > GetClearBinding ( ) ) ;
MRCLayer - > RightEye . SetSwapchain ( SwapChain , Texture - > GetSizeXY ( ) ) ;
}
{
FRHITexture * Texture = MRCLayer - > Desc . LeftTexture - > GetTexture2D ( ) ;
FXRSwapChainPtr SwapChain = Bridge - > CreateSwapchain ( InSession ,
IStereoRenderTargetManager : : GetStereoLayerPixelFormat ( ) ,
UnusedActualFormat ,
Texture - > GetSizeX ( ) ,
Texture - > GetSizeY ( ) ,
# ifdef PICO_CUSTOM_ENGINE
1 ,
# endif
1 ,
Texture - > GetNumMips ( ) ,
Texture - > GetNumSamples ( ) ,
Texture - > GetFlags ( ) | Flags ,
Texture - > GetClearBinding ( ) ) ;
MRCLayer - > LeftEye . SetSwapchain ( SwapChain , Texture - > GetSizeXY ( ) ) ;
}
const bool bNoAlpha = MRCLayer - > Desc . Flags & IStereoLayers : : LAYER_FLAG_TEX_NO_ALPHA_CHANNEL ;
const bool bIsStereo = MRCLayer - > Desc . LeftTexture . IsValid ( ) ;
const FTransform PositionTransform = FTransform : : Identity ;
float WorldToMeters = OpenXRHMD - > GetWorldToMetersScale ( ) ;
XrCompositionLayerQuad MRCQuadLayer = { XR_TYPE_COMPOSITION_LAYER_QUAD , nullptr } ;
MRCQuadLayer . layerFlags = bNoAlpha ? 0 : XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT | XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT ;
MRCQuadLayer . space = ViewTrackingSpace ;
MRCQuadLayer . subImage . imageArrayIndex = 0 ;
MRCQuadLayer . pose = ToXrPose ( MRCLayer - > Desc . Transform * PositionTransform , WorldToMeters ) ;
const FVector2D LayerComponentScaler ( MRCLayer - > Desc . Transform . GetScale3D ( ) . Y , MRCLayer - > Desc . Transform . GetScale3D ( ) . Z ) ;
if ( MRCLayer - > RightEye . Swapchain . IsValid ( ) )
{
MRCQuadLayer . eyeVisibility = bIsStereo ? XR_EYE_VISIBILITY_RIGHT : XR_EYE_VISIBILITY_BOTH ;
MRCQuadLayer . subImage . imageRect = ToXrRect ( GetViewportSize ( MRCLayer - > RightEye , MRCLayer - > Desc ) ) ;
MRCQuadLayer . subImage . swapchain = static_cast < FOpenXRSwapchain * > ( MRCLayer - > RightEye . Swapchain . Get ( ) ) - > GetHandle ( ) ;
MRCQuadLayer . size = ToXrExtent2D ( GetQuadSize ( MRCLayer - > RightEye , MRCLayer - > Desc ) * LayerComponentScaler , WorldToMeters ) ;
MRCQuadLayerRight = MRCQuadLayer ;
}
if ( MRCLayer - > LeftEye . Swapchain . IsValid ( ) )
{
MRCQuadLayer . eyeVisibility = XR_EYE_VISIBILITY_LEFT ;
MRCQuadLayer . subImage . imageRect = ToXrRect ( GetViewportSize ( MRCLayer - > LeftEye , MRCLayer - > Desc ) ) ;
MRCQuadLayer . subImage . swapchain = static_cast < FOpenXRSwapchain * > ( MRCLayer - > LeftEye . Swapchain . Get ( ) ) - > GetHandle ( ) ;
MRCQuadLayer . size = ToXrExtent2D ( GetQuadSize ( MRCLayer - > LeftEye , MRCLayer - > Desc ) * LayerComponentScaler , WorldToMeters ) ;
MRCQuadLayerLeft = MRCQuadLayer ;
}
}
else
{
MRCLayer . Reset ( ) ;
}
}
else if ( MRCLayer . IsValid ( ) & & ( ! MRCLayerDesc . Texture . IsValid ( ) | | ! MRCLayerDesc . LeftTexture . IsValid ( ) ) )
{
MRCLayer . Reset ( ) ;
}
if ( MRCLayer . IsValid ( ) & & MRCLayer - > RightEye . Swapchain . IsValid ( ) & & MRCLayer - > LeftEye . Swapchain . IsValid ( ) )
{
//Right SwapChain
const FXRSwapChainPtr & RightDstSwapChain = MRCLayer - > RightEye . Swapchain ;
RHICmdList . EnqueueLambda ( [ RightDstSwapChain ] ( FRHICommandListImmediate & InRHICmdList )
{
RightDstSwapChain - > IncrementSwapChainIndex_RHIThread ( ) ;
RightDstSwapChain - > WaitCurrentImage_RHIThread ( OPENXR_SWAPCHAIN_WAIT_TIMEOUT ) ;
} ) ;
FRHITexture * RightSrcTexture = MRCLayer - > Desc . Texture - > GetTexture2D ( ) ;
const FIntRect RightDstRect ( FIntPoint ( 0 , 0 ) , MRCLayer - > RightEye . SwapchainSize . IntPoint ( ) ) ;
FRHITexture * const RightDstTexture = MRCLayer - > RightEye . Swapchain - > GetTexture2DArray ( ) ? MRCLayer - > RightEye . Swapchain - > GetTexture2DArray ( ) : MRCLayer - > RightEye . Swapchain - > GetTexture2D ( ) ;
CopyTexture_RenderThread ( RHICmdList , RightDstTexture , RightSrcTexture , RightDstRect , FIntRect ( ) , true , true , true , true ) ;
RHICmdList . EnqueueLambda ( [ RightDstSwapChain ] ( FRHICommandListImmediate & InRHICmdList )
{
RightDstSwapChain - > ReleaseCurrentImage_RHIThread ( nullptr ) ;
} ) ;
//Left SwapChain
const FXRSwapChainPtr & LeftDstSwapChain = MRCLayer - > LeftEye . Swapchain ;
RHICmdList . EnqueueLambda ( [ LeftDstSwapChain ] ( FRHICommandListImmediate & InRHICmdList )
{
LeftDstSwapChain - > IncrementSwapChainIndex_RHIThread ( ) ;
LeftDstSwapChain - > WaitCurrentImage_RHIThread ( OPENXR_SWAPCHAIN_WAIT_TIMEOUT ) ;
} ) ;
FRHITexture * LeftSrcTexture = MRCLayer - > Desc . LeftTexture - > GetTexture2D ( ) ;
const FIntRect LeftDstRect ( FIntPoint ( 0 , 0 ) , MRCLayer - > LeftEye . SwapchainSize . IntPoint ( ) ) ;
FRHITexture * const LeftDstTexture = MRCLayer - > LeftEye . Swapchain - > GetTexture2DArray ( ) ? MRCLayer - > LeftEye . Swapchain - > GetTexture2DArray ( ) : MRCLayer - > LeftEye . Swapchain - > GetTexture2D ( ) ;
CopyTexture_RenderThread ( RHICmdList , LeftDstTexture , LeftSrcTexture , LeftDstRect , FIntRect ( ) , true , true , true /*BG*/ , true ) ;
RHICmdList . EnqueueLambda ( [ LeftDstSwapChain ] ( FRHICommandListImmediate & InRHICmdList )
{
LeftDstSwapChain - > ReleaseCurrentImage_RHIThread ( nullptr ) ;
} ) ;
}
}
void FHMDPICO : : UpdateCompositionLayers ( XrSession InSession , TArray < const XrCompositionLayerBaseHeader * > & Headers )
{
if ( bSupportMRCExtension & & MRCLayer . IsValid ( ) )
{
Headers . Add ( reinterpret_cast < const XrCompositionLayerBaseHeader * > ( & MRCQuadLayerLeft ) ) ;
Headers . Add ( reinterpret_cast < const XrCompositionLayerBaseHeader * > ( & MRCQuadLayerRight ) ) ;
}
}
void FHMDPICO : : UpdateCompositionLayers ( XrSession InSession , TArray < XrCompositionLayerBaseHeader * > & Headers )
{
# ifndef PICO_CUSTOM_ENGINE
//Stereo Layer default support depth
for ( int32 i = 0 ; i < Headers . Num ( ) ; i + + )
{
2025-03-11 16:50:06 +08:00
if ( OpenXRHMD & & OpenXRHMD - > GetTrackedDeviceSpace ( IXRTrackingSystem : : HMDDeviceId ) = = Headers [ i ] - > space )
{
continue ; //skip face-lock layer
}
2025-03-10 09:43:27 +08:00
if ( Headers [ i ] - > type = = XR_TYPE_COMPOSITION_LAYER_QUAD
| | Headers [ i ] - > type = = XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR
| | Headers [ i ] - > type = = XR_TYPE_COMPOSITION_LAYER_EQUIRECT_KHR )
{
BlendState . next = const_cast < void * > ( Headers [ i ] - > next ) ;
BlendState . srcFactorColor = XrBlendFactorFB : : XR_BLEND_FACTOR_DST_ALPHA_FB ;
BlendState . dstFactorColor = XrBlendFactorFB : : XR_BLEND_FACTOR_ONE_MINUS_DST_ALPHA_FB ;
BlendState . srcFactorAlpha = XrBlendFactorFB : : XR_BLEND_FACTOR_ZERO_FB ;
BlendState . dstFactorAlpha = XrBlendFactorFB : : XR_BLEND_FACTOR_ONE_FB ;
Headers [ i ] - > next = & BlendState ;
}
}
# endif // PICO_CUSTOM_ENGINE
if ( bSupportMRCExtension & & MRCLayer . IsValid ( ) )
{
Headers . Add ( reinterpret_cast < XrCompositionLayerBaseHeader * > ( & MRCQuadLayerLeft ) ) ;
Headers . Add ( reinterpret_cast < XrCompositionLayerBaseHeader * > ( & MRCQuadLayerRight ) ) ;
}
}
bool FHMDPICO : : GetSupportedDisplayRefreshRates ( TArray < float > & Rates )
{
Rates = SupportedDisplayRefreshRates ;
return bSupportDisplayRefreshRate ;
}
bool FHMDPICO : : GetCurrentDisplayRefreshRate ( float & Rate )
{
Rate = CurrentDisplayRefreshRate ;
return bSupportDisplayRefreshRate ;
}
bool FHMDPICO : : SetDisplayRefreshRate ( float Rate )
{
FReadScopeLock Lock ( SessionHandleMutex ) ;
if ( bSupportDisplayRefreshRate & & Session )
{
if ( SupportedDisplayRefreshRates . Contains ( Rate ) )
{
UE_LOG ( LogPICOOpenXRHMD , Log , TEXT ( " Requesting DisplayRefreshRate:%f " ) , Rate ) ;
XR_ENSURE ( xrRequestDisplayRefreshRateFB ( Session , Rate ) ) ;
return true ;
}
else
{
UE_LOG ( LogPICOOpenXRHMD , Warning , TEXT ( " Requested DisplayRefreshRate:%f is not supported by device! " ) , Rate ) ;
}
}
return false ;
}
void FHMDPICO : : EnableContentProtect ( bool Enable )
{
bool ContentProtectSupportedEXT = IOpenXRHMDModule : : Get ( ) . IsExtensionEnabled ( XR_FB_COMPOSITION_LAYER_SECURE_CONTENT_EXTENSION_NAME ) ;
bContentProtectEnabled = Enable & & ContentProtectSupportedEXT ;
UE_LOG ( LogPICOOpenXRHMD , Log , TEXT ( " :EnableContentProtect EXT Supported:%d, Enabled:%d " ) , ContentProtectSupportedEXT , bContentProtectEnabled ) ;
}
bool FHMDPICO : : SetPerformanceLevel ( int domain , int level )
{
if ( Session & & bSupportPerformanceSettingsEXT )
{
return XR_SUCCEEDED ( xrPerfSettingsSetPerformanceLevelEXT ( Session , XrPerfSettingsDomainEXT ( domain ) , XrPerfSettingsLevelEXT ( level ) ) ) ;
}
return false ;
}
bool FHMDPICO : : GetDevicePoseForTime ( const EControllerHand Hand , bool UseDefaultTime , FTimespan Timespan , bool & OutTimeWasUsed , FRotator & OutOrientation , FVector & OutPosition , bool & OutbProvidedLinearVelocity , FVector & OutLinearVelocity , bool & OutbProvidedAngularVelocity , FVector & OutAngularVelocityRadPerSec , bool & OutbProvidedLinearAcceleration , FVector & OutLinearAcceleration , float InWorldToMetersScale )
{
if ( OpenXRHMD = = nullptr )
{
return false ;
}
int32 DeviceId = - 1 ;
if ( Hand = = EControllerHand : : HMD )
{
DeviceId = IXRTrackingSystem : : HMDDeviceId ;
}
else
{
TArray < int32 > Devices ;
if ( OpenXRHMD - > EnumerateTrackedDevices ( Devices , EXRTrackedDeviceType : : Controller ) )
{
if ( Devices . IsValidIndex ( ( int32 ) Hand ) )
{
DeviceId = Devices [ ( int32 ) Hand ] ;
}
}
}
if ( DeviceId = = - 1 )
{
return false ;
}
XrTime TargetTime = 0 ;
if ( UseDefaultTime )
{
TargetTime = CurrentDisplayTime ;
}
else
{
TargetTime = ToXrTime ( Timespan ) ;
}
if ( TargetTime = = 0 )
{
OutTimeWasUsed = false ;
}
else
{
OutTimeWasUsed = true ;
}
FQuat Orientation ;
bool Success = OpenXRHMD - > GetPoseForTime ( DeviceId , TargetTime , OutTimeWasUsed , Orientation , OutPosition , OutbProvidedLinearVelocity , OutLinearVelocity , OutbProvidedAngularVelocity , OutAngularVelocityRadPerSec , OutbProvidedLinearAcceleration , OutLinearAcceleration , InWorldToMetersScale ) ;
OutOrientation = FRotator ( Orientation ) ;
return Success ;
}
EHMDWornState : : Type FHMDPICO : : GetHMDWornState ( bool & ResultValid )
{
ResultValid = IsSupportsUserPresence ;
return WornState ;
}
bool FHMDPICO : : GetFieldOfView ( float & OutHFOVInDegrees , float & OutVFOVInDegrees )
{
if ( OpenXRHMD )
{
OpenXRHMD - > GetHMDDevice ( ) - > GetFieldOfView ( OutHFOVInDegrees , OutVFOVInDegrees ) ;
return true ;
}
return false ;
}
bool FHMDPICO : : GetInterpupillaryDistance ( float & IPD )
{
if ( OpenXRHMD )
{
IPD = OpenXRHMD - > GetInterpupillaryDistance ( ) * OpenXRHMD - > GetWorldToMetersScale ( ) ;
return true ;
}
return false ;
}
void FHMDPICO : : SetBaseRotationAndBaseOffset ( FRotator Rotation , FVector BaseOffset , EOrientPositionSelector : : Type Options )
{
if ( OpenXRHMD )
{
if ( ( Options = = EOrientPositionSelector : : Orientation ) | | ( Options = = EOrientPositionSelector : : OrientationAndPosition ) )
{
OpenXRHMD - > SetBaseRotation ( Rotation ) ;
}
if ( ( Options = = EOrientPositionSelector : : Position ) | | ( Options = = EOrientPositionSelector : : OrientationAndPosition ) )
{
OpenXRHMD - > SetBasePosition ( BaseOffset ) ;
}
}
}
void FHMDPICO : : GetBaseRotationAndBaseOffset ( FRotator & OutRotation , FVector & OutBaseOffset )
{
if ( OpenXRHMD ! = nullptr )
{
OutRotation = OpenXRHMD - > GetBaseRotation ( ) ;
OutBaseOffset = OpenXRHMD - > GetBasePosition ( ) ;
}
else
{
OutRotation = FRotator : : ZeroRotator ;
OutBaseOffset = FVector : : ZeroVector ;
}
}
FTimespan FHMDPICO : : GetDisplayTime ( )
{
return ToFTimespan ( CurrentDisplayTime ) ;
}
void FHMDPICO : : CopyTexture_RenderThread ( FRHICommandListImmediate & RHICmdList , FRHITexture * DstTexture , FRHITexture * SrcTexture , FIntRect DstRect , FIntRect SrcRect , bool bAlphaPremultiply , bool bNoAlpha , bool bClearGreen , bool bInvertY , bool bInvertAlpha ) const
{
ERenderTargetActions RTAction = ERenderTargetActions : : Clear_Store ;
ERHIAccess FinalDstAccess = ERHIAccess : : SRVMask ;
check ( IsInRenderingThread ( ) ) ;
const uint32 ViewportWidth = DstRect . Width ( ) ;
const uint32 ViewportHeight = DstRect . Height ( ) ;
const FIntPoint TargetSize ( ViewportWidth , ViewportHeight ) ;
const float SrcTextureWidth = SrcTexture - > GetSizeX ( ) ;
const float SrcTextureHeight = SrcTexture - > GetSizeY ( ) ;
float U = 0.f , V = 0.f , USize = 1.f , VSize = 1.f ;
if ( SrcRect . IsEmpty ( ) )
{
SrcRect . Min . X = 0 ;
SrcRect . Min . Y = 0 ;
SrcRect . Max . X = SrcTextureWidth ;
SrcRect . Max . Y = SrcTextureHeight ;
}
else
{
U = SrcRect . Min . X / SrcTextureWidth ;
V = SrcRect . Min . Y / SrcTextureHeight ;
USize = SrcRect . Width ( ) / SrcTextureWidth ;
VSize = SrcRect . Height ( ) / SrcTextureHeight ;
}
if ( bInvertY )
{
V = 1.0f - V ;
VSize = - VSize ;
}
RHICmdList . Transition ( FRHITransitionInfo ( DstTexture , ERHIAccess : : Unknown , ERHIAccess : : RTV ) ) ;
FRHITexture * ColorRT = DstTexture - > GetTexture2DArray ( ) ? DstTexture - > GetTexture2DArray ( ) : DstTexture - > GetTexture2D ( ) ;
FRHIRenderPassInfo RenderPassInfo ( ColorRT , RTAction ) ;
RHICmdList . BeginRenderPass ( RenderPassInfo , TEXT ( " OpenXRHMD_CopyTexture " ) ) ;
{
if ( bClearGreen | | bNoAlpha )
{
const FIntRect ClearRect ( 0 , 0 , DstTexture - > GetSizeX ( ) , DstTexture - > GetSizeY ( ) ) ;
RHICmdList . SetViewport ( ClearRect . Min . X , ClearRect . Min . Y , 0 , ClearRect . Max . X , ClearRect . Max . Y , 1.0f ) ;
if ( bClearGreen )
{
DrawClearQuad ( RHICmdList , FLinearColor : : Green ) ;
}
else
{
// For opaque texture copies, we want to make sure alpha is initialized to 1.0f
DrawClearQuadAlpha ( RHICmdList , 1.0f ) ;
}
}
RHICmdList . SetViewport ( DstRect . Min . X , DstRect . Min . Y , 0 , DstRect . Max . X , DstRect . Max . Y , 1.0f ) ;
FGraphicsPipelineStateInitializer GraphicsPSOInit ;
RHICmdList . ApplyCachedRenderTargets ( GraphicsPSOInit ) ;
if ( bClearGreen )
{
GraphicsPSOInit . BlendState = TStaticBlendState < CW_RGBA , BO_Add , BF_InverseSourceAlpha , BF_SourceAlpha , BO_Add , BF_Zero , BF_InverseSourceAlpha > : : GetRHI ( ) ;
}
else if ( bInvertAlpha )
{
GraphicsPSOInit . BlendState = TStaticBlendState < CW_RGBA , BO_Add , BF_One , BF_Zero , BO_Add , BF_Zero , BF_InverseSourceAlpha > : : GetRHI ( ) ;
}
else if ( bAlphaPremultiply )
{
if ( bNoAlpha )
{
GraphicsPSOInit . BlendState = TStaticBlendState < CW_RGB , BO_Add , BF_One , BF_Zero , BO_Add , BF_One , BF_Zero > : : GetRHI ( ) ;
}
else
{
GraphicsPSOInit . BlendState = TStaticBlendState < CW_RGBA , BO_Add , BF_SourceAlpha , BF_Zero , BO_Add , BF_One , BF_Zero > : : GetRHI ( ) ;
}
}
else
{
if ( bNoAlpha )
{
GraphicsPSOInit . BlendState = TStaticBlendState < CW_RGB > : : GetRHI ( ) ;
}
else
{
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 ;
FGlobalShaderMap * ShaderMap = GetGlobalShaderMap ( OpenXRHMD - > GetConfiguredShaderPlatform ( ) ) ;
TShaderMapRef < FScreenVS > VertexShader ( ShaderMap ) ;
TShaderRef < FGlobalShader > PixelShader ;
TShaderRef < FDisplayMappingPixelShader > DisplayMappingPS ;
TShaderRef < FScreenPS > ScreenPS ;
bool bNeedsDisplayMapping = false ;
bool bIsInputLinear = false ;
EDisplayOutputFormat TVDisplayOutputFormat = EDisplayOutputFormat : : SDR_sRGB ;
EDisplayColorGamut HMDColorGamut = EDisplayColorGamut : : sRGB_D65 ;
EDisplayColorGamut TVColorGamut = EDisplayColorGamut : : sRGB_D65 ;
FOpenXRRenderBridge * RenderBridge = static_cast < FOpenXRRenderBridge * > ( OpenXRHMD - > GetActiveRenderBridge_GameThread ( OpenXRHMD - > ShouldUseSeparateRenderTarget ( ) ) ) ;
if ( FinalDstAccess = = ERHIAccess : : Present & & RenderBridge )
{
EDisplayOutputFormat HMDDisplayFormat ;
bool bHMDSupportHDR ;
if ( RenderBridge - > HDRGetMetaDataForStereo ( HMDDisplayFormat , HMDColorGamut , bHMDSupportHDR ) )
{
bool bTVSupportHDR ;
HDRGetMetaData ( TVDisplayOutputFormat , TVColorGamut , bTVSupportHDR , FVector2D ( 0 , 0 ) , FVector2D ( 0 , 0 ) , nullptr ) ;
if ( TVDisplayOutputFormat ! = HMDDisplayFormat | | HMDColorGamut ! = TVColorGamut | | bTVSupportHDR ! = bHMDSupportHDR )
{
// shader assumes G 2.2 for input / ST2084/sRGB for output right now
ensure ( HMDDisplayFormat = = EDisplayOutputFormat : : SDR_ExplicitGammaMapping ) ;
ensure ( TVDisplayOutputFormat = = EDisplayOutputFormat : : SDR_sRGB | | TVDisplayOutputFormat = = EDisplayOutputFormat : : HDR_ACES_1000nit_ST2084 | | TVDisplayOutputFormat = = EDisplayOutputFormat : : HDR_ACES_2000nit_ST2084 ) ;
bNeedsDisplayMapping = true ;
}
}
// In Android Vulkan preview, when the sRGB swapchain texture is sampled, the data is converted to linear and written to the RGBA10A2_UNORM texture.
// However, D3D interprets integer-valued display formats as containing sRGB data, so we need to convert the linear data back to sRGB.
if ( ! IsMobileHDR ( ) & & IsMobilePlatform ( OpenXRHMD - > GetConfiguredShaderPlatform ( ) ) & & IsSimulatedPlatform ( OpenXRHMD - > GetConfiguredShaderPlatform ( ) ) )
{
bNeedsDisplayMapping = true ;
TVDisplayOutputFormat = EDisplayOutputFormat : : SDR_sRGB ;
bIsInputLinear = true ;
}
}
bNeedsDisplayMapping & = IsFeatureLevelSupported ( OpenXRHMD - > GetConfiguredShaderPlatform ( ) , ERHIFeatureLevel : : ES3_1 ) ;
bool bIsArraySource = SrcTexture - > GetDesc ( ) . IsTextureArray ( ) ;
if ( bNeedsDisplayMapping )
{
FDisplayMappingPixelShader : : FPermutationDomain PermutationVector ;
PermutationVector . Set < FDisplayMappingPixelShader : : FArraySource > ( bIsArraySource ) ;
PermutationVector . Set < FDisplayMappingPixelShader : : FLinearInput > ( bIsInputLinear ) ;
TShaderMapRef < FDisplayMappingPixelShader > DisplayMappingPSRef ( ShaderMap , PermutationVector ) ;
DisplayMappingPS = DisplayMappingPSRef ;
PixelShader = DisplayMappingPSRef ;
}
else
{
if ( LIKELY ( ! bIsArraySource ) )
{
TShaderMapRef < FScreenPS > ScreenPSRef ( ShaderMap ) ;
ScreenPS = ScreenPSRef ;
PixelShader = ScreenPSRef ;
}
else
{
TShaderMapRef < FScreenFromSlice0PS > ScreenPSRef ( ShaderMap ) ;
ScreenPS = ScreenPSRef ;
PixelShader = ScreenPSRef ;
}
}
GraphicsPSOInit . BoundShaderState . VertexDeclarationRHI = GFilterVertexDeclaration . VertexDeclarationRHI ;
GraphicsPSOInit . BoundShaderState . VertexShaderRHI = VertexShader . GetVertexShader ( ) ;
GraphicsPSOInit . BoundShaderState . PixelShaderRHI = PixelShader . GetPixelShader ( ) ;
SetGraphicsPipelineState ( RHICmdList , GraphicsPSOInit , 0 ) ;
RHICmdList . Transition ( FRHITransitionInfo ( SrcTexture , ERHIAccess : : Unknown , ERHIAccess : : SRVMask ) ) ;
const bool bSameSize = DstRect . Size ( ) = = SrcRect . Size ( ) ;
if ( ScreenPS . IsValid ( ) )
{
FRHISamplerState * PixelSampler = bSameSize ? TStaticSamplerState < SF_Point > : : GetRHI ( ) : TStaticSamplerState < SF_Bilinear > : : GetRHI ( ) ;
SetShaderParametersLegacyPS ( RHICmdList , ScreenPS , PixelSampler , SrcTexture ) ;
}
else if ( DisplayMappingPS . IsValid ( ) )
{
SetShaderParametersLegacyPS ( RHICmdList , DisplayMappingPS , TVDisplayOutputFormat , TVColorGamut , HMDColorGamut , SrcTexture , bSameSize ) ;
}
FModuleManager : : GetModulePtr < IRendererModule > ( " Renderer " ) - > DrawRectangle (
RHICmdList ,
0 , 0 ,
ViewportWidth , ViewportHeight ,
U , V ,
USize , VSize ,
TargetSize ,
FIntPoint ( 1 , 1 ) ,
VertexShader ,
EDRF_Default ) ;
}
RHICmdList . EndRenderPass ( ) ;
RHICmdList . Transition ( FRHITransitionInfo ( DstTexture , ERHIAccess : : RTV , FinalDstAccess ) ) ;
}
bool FHMDPICO : : IsEyeTrackerSupported ( bool & Supported )
{
Supported = false ;
if ( Instance ! = XR_NULL_HANDLE & & System ! = XR_NULL_SYSTEM_ID )
{
if ( IOpenXRHMDModule : : Get ( ) . IsExtensionEnabled ( XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME ) )
{
XrSystemEyeGazeInteractionPropertiesEXT EyeGazeInteractionProperties = { XR_TYPE_SYSTEM_EYE_GAZE_INTERACTION_PROPERTIES_EXT } ;
XrSystemProperties systemProperties = { XR_TYPE_SYSTEM_PROPERTIES , & EyeGazeInteractionProperties } ;
XR_ENSURE ( xrGetSystemProperties ( Instance , System , & systemProperties ) ) ;
Supported = EyeGazeInteractionProperties . supportsEyeGazeInteraction = = XR_TRUE ;
}
return true ;
}
return false ;
}
void FHMDPICO : : CreateMRCLayer ( class UTexture * BackgroundRTTexture , class UTexture * ForegroundRTTexture )
{
if ( BackgroundRTTexture & & BackgroundRTTexture - > GetResource ( ) & & ForegroundRTTexture & & ForegroundRTTexture - > GetResource ( ) )
{
MRCLayerDesc . PositionType = IStereoLayers : : ELayerType : : FaceLocked ;
MRCLayerDesc . Transform . SetLocation ( FVector ( 100 , 0 , 0 ) ) ;
MRCLayerDesc . Texture = ForegroundRTTexture - > GetResource ( ) - > TextureRHI ;
MRCLayerDesc . LeftTexture = BackgroundRTTexture - > GetResource ( ) - > TextureRHI ;
MRCLayerDesc . Flags = IStereoLayers : : ELayerFlags : : LAYER_FLAG_TEX_CONTINUOUS_UPDATE | IStereoLayers : : ELayerFlags : : LAYER_FLAG_TEX_NO_ALPHA_CHANNEL ;
MRCLayerDesc . QuadSize = FVector2D ( 100 , 100 ) ;
}
}
void FHMDPICO : : DestroyMRCLayer ( )
{
MRCLayerDesc . Texture = nullptr ;
MRCLayerDesc . LeftTexture = nullptr ;
MRCLayer . Reset ( ) ;
}
bool FHMDPICO : : GetExternalCameraInfo ( int32 & width , int32 & height , float & fov )
{
if ( MRCDebugMode . UseCustomCameraInfo )
{
width = MRCDebugMode . Width ;
height = MRCDebugMode . Height ;
fov = MRCDebugMode . Fov ;
return true ;
}
return false ;
}
bool FHMDPICO : : GetExternalCameraPose ( FTransform & Pose )
{
if ( MRCDebugMode . UseCustomTransform )
{
Pose = MRCDebugMode . Pose ;
return true ;
}
if ( bSupportMRCExtension & & Session & & CurrentBaseSpace & & MRCSpace & & OpenXRHMD )
{
XrSpaceLocation NewLocation = { XR_TYPE_SPACE_LOCATION } ;
const XrResult Result = xrLocateSpace ( MRCSpace , CurrentBaseSpace , CurrentDisplayTime , & NewLocation ) ;
if ( Result ! = XR_SUCCESS )
{
return false ;
}
if ( ! ( NewLocation . locationFlags & ( XR_SPACE_LOCATION_POSITION_VALID_BIT | XR_SPACE_LOCATION_ORIENTATION_VALID_BIT ) ) )
{
return false ;
}
const FQuat Orientation = ToFQuat ( NewLocation . pose . orientation ) ;
const FVector Position = ToFVector ( NewLocation . pose . position , OpenXRHMD - > GetWorldToMetersScale ( ) ) ;
FTransform TrackingToWorld = OpenXRHMD - > GetTrackingToWorldTransform ( ) ;
Pose = FTransform ( Orientation , Position ) * TrackingToWorld ;
return true ;
}
return false ;
}
void FHMDPICO : : EnableMRCDebugMode ( class UWorld * WorldContext , bool Enable , bool ViewInHMD , bool UseCustomTransform , const FTransform & Pose , bool UseCustomCameraInfo , int Width , int Height , float Fov )
{
bSupportMRCExtension = Enable ;
bIsMRCRunning | = Enable ;
World = WorldContext ;
if ( Enable )
{
MRCDebugMode . EnableExtension = Enable ;
MRCDebugMode . ViewInHMD = ViewInHMD ;
MRCDebugMode . UseCustomTransform = UseCustomTransform ;
MRCDebugMode . Pose = Pose ;
MRCDebugMode . UseCustomCameraInfo = UseCustomCameraInfo ;
MRCDebugMode . Width = Width ;
MRCDebugMode . Height = Height ;
MRCDebugMode . Fov = Fov ;
}
else
{
MRCDebugMode = { } ;
}
}
bool FHMDPICO : : NeedReallocateTexture ( bool bLeft , const FOpenXRLayer & Layer )
{
if ( bLeft )
{
if ( ! Layer . Desc . LeftTexture . IsValid ( ) )
{
return false ;
}
FRHITexture * Texture = Layer . Desc . LeftTexture - > GetTexture2D ( ) ;
if ( ! Texture )
{
return false ;
}
if ( ! Layer . LeftEye . Swapchain . IsValid ( ) )
{
return true ;
}
return Layer . LeftEye . SwapchainSize ! = Texture - > GetSizeXY ( ) ;
}
else
{
if ( ! Layer . Desc . Texture . IsValid ( ) )
{
return false ;
}
FRHITexture * Texture = Layer . Desc . Texture - > GetTexture2D ( ) ;
if ( ! Texture )
{
return false ;
}
if ( ! Layer . RightEye . Swapchain . IsValid ( ) )
{
return true ;
}
return Layer . RightEye . SwapchainSize ! = Texture - > GetSizeXY ( ) ;
}
}
void FHMDPICO : : UpdateLayerSwapchainTexture ( int64 WaitTimeOut , const FOpenXRLayer & Layer , FRHICommandListImmediate & RHICmdList )
{
const bool bNoAlpha = Layer . Desc . Flags & IStereoLayers : : LAYER_FLAG_TEX_NO_ALPHA_CHANNEL ;
// We need to copy each layer into an OpenXR swapchain so they can be displayed by the compositor.
if ( Layer . RightEye . Swapchain . IsValid ( ) & & Layer . Desc . Texture . IsValid ( ) )
{
if ( Layer . RightEye . bUpdateTexture )
{
const FXRSwapChainPtr & DstSwapChain = Layer . RightEye . Swapchain ;
RHICmdList . EnqueueLambda ( [ WaitTimeOut , DstSwapChain ] ( FRHICommandListImmediate & InRHICmdList )
{
DstSwapChain - > IncrementSwapChainIndex_RHIThread ( ) ;
DstSwapChain - > WaitCurrentImage_RHIThread ( WaitTimeOut ) ;
} ) ;
FRHITexture * SrcTexture = Layer . Desc . Texture - > GetTexture2D ( ) ;
FIntRect DstRect ( FIntPoint ( 0 , 0 ) , Layer . RightEye . SwapchainSize . IntPoint ( ) ) ;
OpenXRHMD - > CopyTexture_RenderThread ( RHICmdList , SrcTexture , FIntRect ( ) , Layer . RightEye . Swapchain - > GetTexture2D ( ) , DstRect , false , bNoAlpha ) ;
// Enqueue a command to release the image after the copy is done
RHICmdList . EnqueueLambda ( [ DstSwapChain ] ( FRHICommandListImmediate & InRHICmdList )
{
DstSwapChain - > ReleaseCurrentImage_RHIThread ( nullptr ) ;
} ) ;
}
}
if ( Layer . LeftEye . Swapchain . IsValid ( ) & & Layer . Desc . LeftTexture . IsValid ( ) )
{
if ( Layer . LeftEye . bUpdateTexture )
{
const FXRSwapChainPtr & DstSwapChain = Layer . LeftEye . Swapchain ;
RHICmdList . EnqueueLambda ( [ WaitTimeOut , DstSwapChain ] ( FRHICommandListImmediate & InRHICmdList )
{
DstSwapChain - > IncrementSwapChainIndex_RHIThread ( ) ;
DstSwapChain - > WaitCurrentImage_RHIThread ( WaitTimeOut ) ;
} ) ;
FRHITexture * SrcTexture = Layer . Desc . LeftTexture - > GetTexture2D ( ) ;
FIntRect DstRect ( FIntPoint ( 0 , 0 ) , Layer . LeftEye . SwapchainSize . IntPoint ( ) ) ;
OpenXRHMD - > CopyTexture_RenderThread ( RHICmdList , SrcTexture , FIntRect ( ) , Layer . LeftEye . Swapchain - > GetTexture2D ( ) , DstRect , false , bNoAlpha ) ;
// Enqueue a command to release the image after the copy is done
RHICmdList . EnqueueLambda ( [ DstSwapChain ] ( FRHICommandListImmediate & InRHICmdList )
{
DstSwapChain - > ReleaseCurrentImage_RHIThread ( nullptr ) ;
} ) ;
}
}
}
FIntPoint FHMDPICO : : GetDefaultRenderTargetSize ( )
{
if ( OpenXRHMD )
{
return OpenXRHMD - > GetIdealRenderTargetSize ( ) ;
}
return FIntPoint ( ) ;
}
void FHMDPICO : : GetCurrentRenderTargetSize ( uint32 & InOutSizeX , uint32 & InOutSizeY )
{
InOutSizeX = InOutSizeY = 0 ;
if ( OpenXRHMD & & GEngine )
{
check ( GEngine - > GameViewport - > Viewport ) ;
OpenXRHMD - > CalculateRenderTargetSize ( * GEngine - > GameViewport - > Viewport , InOutSizeX , InOutSizeY ) ;
}
}
uint32 FHMDPICO : : CreateLayer ( const IStereoLayers : : FLayerDesc & InLayerDesc )
{
FScopeLock LockLayers ( & LayerCritSect ) ;
uint32 LayerId = + + StereoLayerID ;
check ( LayerId ! = IStereoLayers : : FLayerDesc : : INVALID_LAYER_ID ) ;
FOpenXRLayer & NewLayer = StereoLayersPICO . Emplace ( LayerId , FOpenXRLayer ( InLayerDesc ) ) ;
NewLayer . SetLayerId ( LayerId ) ;
return LayerId ;
}
void FHMDPICO : : SetLayerDesc ( uint32 ID , const IStereoLayers : : FLayerDesc & Desc )
{
FScopeLock LockLayers ( & LayerCritSect ) ;
FOpenXRLayer * LayerFound = StereoLayersPICO . Find ( ID ) ;
if ( LayerFound )
{
LayerFound - > Desc = Desc ;
}
}
void FHMDPICO : : DestroyLayer ( uint32 ID )
{
FScopeLock LockLayers ( & LayerCritSect ) ;
StereoLayersPICO . Remove ( ID ) ;
}
void FHMDPICO : : MarkTextureForUpdate ( uint32 ID )
{
FScopeLock LockLayers ( & LayerCritSect ) ;
FOpenXRLayer * LayerFound = StereoLayersPICO . Find ( ID ) ;
if ( LayerFound )
{
// If the swapchain is static we need to re-allocate it before it can be updated
if ( ! ( LayerFound - > Desc . Flags & IStereoLayers : : LAYER_FLAG_TEX_CONTINUOUS_UPDATE ) )
{
LayerFound - > RightEye . Swapchain . Reset ( ) ;
LayerFound - > LeftEye . Swapchain . Reset ( ) ;
}
LayerFound - > RightEye . bUpdateTexture = true ;
LayerFound - > LeftEye . bUpdateTexture = true ;
}
}