@@ -251,6 +251,10 @@ UnitySubsystemErrorCode OpenVRDisplayProvider::GfxThread_Start( UnityXRRendering
251251 m_eActiveTextureType = vr::TextureType_DirectX;
252252 break ;
253253
254+ case kUnityGfxRendererD3D12 :
255+ m_eActiveTextureType = vr::TextureType_DirectX12;
256+ break ;
257+
254258 case kUnityGfxRendererVulkan :
255259 m_eActiveTextureType = vr::TextureType_Vulkan;
256260 break ;
@@ -261,7 +265,7 @@ UnitySubsystemErrorCode OpenVRDisplayProvider::GfxThread_Start( UnityXRRendering
261265 break ;
262266
263267 default :
264- XR_TRACE ( " [OpenVR] [Error] Unsupported graphics api. Only DirectX, OpenGL and Vulkan are supported at this time." );
268+ XR_TRACE ( " [OpenVR] [Error] Unsupported graphics api. Only DirectX 11/12 , OpenGL and Vulkan are supported at this time." );
265269 return kUnitySubsystemErrorCodeFailure ;
266270 break ;
267271 }
@@ -459,7 +463,7 @@ UnitySubsystemErrorCode OpenVRDisplayProvider::GfxThread_SubmitCurrentFrame()
459463UnitySubsystemErrorCode OpenVRDisplayProvider::GfxThread_BlitToMirrorViewRenderTarget ( const UnityXRMirrorViewBlitInfo *mirrorBlitInfo )
460464{
461465 #ifndef __linux__
462- // Set RTV
466+ // Set RTV for D3D11
463467 if ( XR_WIN && m_eActiveTextureType == vr::TextureType_DirectX )
464468 {
465469 ID3D11RenderTargetView *pRTV = s_pProviderContext->interfaces ->Get <IUnityGraphicsD3D11>()->RTVFromRenderBuffer ( mirrorBlitInfo->mirrorRtDesc ->rtNative );
@@ -471,6 +475,7 @@ UnitySubsystemErrorCode OpenVRDisplayProvider::GfxThread_BlitToMirrorViewRenderT
471475 const FLOAT clrColor[4 ] = { 0 , 0 , 0 , 0 };
472476 pImmediateContext->ClearRenderTargetView ( pRTV, clrColor );
473477 }
478+ // D3D12 doesn't need special handling here - Unity handles the render target setup
474479 #endif
475480
476481 return kUnitySubsystemErrorCodeSuccess ;
@@ -748,15 +753,70 @@ bool OpenVRDisplayProvider::SubmitToCompositor( vr::EVREye eEye, int nStage )
748753 return false ;
749754 }
750755 }
756+ #ifndef __linux__
757+ else if ( m_eActiveTextureType == vr::TextureType_DirectX12 )
758+ {
759+ ID3D12Resource *pD3D12Resource = (ID3D12Resource * )GetNativeEyeTexture ( nStage, nTexIndex );
760+ if ( !pD3D12Resource )
761+ {
762+ XR_TRACE ( " [OpenVR] [Error] Unable to get D3D12 resource for stage %i and eye %i\n " , nStage, eEye );
763+ return false ;
764+ }
765+
766+ IUnityGraphicsD3D12v5 *pD3D12v5 = s_pProviderContext->interfaces ->Get <IUnityGraphicsD3D12v5>();
767+ IUnityGraphicsD3D12v4 *pD3D12v4 = s_pProviderContext->interfaces ->Get <IUnityGraphicsD3D12v4>();
768+ IUnityGraphicsD3D12 *pD3D12 = s_pProviderContext->interfaces ->Get <IUnityGraphicsD3D12>();
769+
770+ ID3D12CommandQueue *pCommandQueue = nullptr ;
771+ if ( pD3D12v5 )
772+ {
773+ pCommandQueue = pD3D12v5->GetCommandQueue ();
774+ }
775+ else if ( pD3D12v4 )
776+ {
777+ pCommandQueue = pD3D12v4->GetCommandQueue ();
778+ }
779+ else if ( pD3D12 )
780+ {
781+ pCommandQueue = pD3D12->GetCommandQueue ();
782+ }
783+
784+ if ( !pCommandQueue )
785+ {
786+ XR_TRACE ( " [OpenVR] [Error] Unable to get D3D12 command queue for stage %i and eye %i\n " , nStage, eEye );
787+ return false ;
788+ }
789+
790+ m_vrD3D12Texture.m_pResource = pD3D12Resource;
791+ m_vrD3D12Texture.m_pCommandQueue = pCommandQueue;
792+ m_vrD3D12Texture.m_nNodeMask = 1 ;
793+
794+ XR_TRACE ( " [OpenVR] D3D12 Resource: %p, CommandQueue: %p for stage %i eye %i\n " ,
795+ pD3D12Resource, pCommandQueue, nStage, eEye );
796+ }
797+ #endif
751798
752799 // Grab the correct texture for this stage
753800 vr::VRTextureWithDepth_t tex;
754- tex.handle = m_eActiveTextureType == vr::TextureType_Vulkan ? &m_vrVulkanTexture : GetNativeEyeTexture ( nStage, nTexIndex );
801+ if ( m_eActiveTextureType == vr::TextureType_Vulkan )
802+ {
803+ tex.handle = &m_vrVulkanTexture;
804+ }
805+ #ifndef __linux__
806+ else if ( m_eActiveTextureType == vr::TextureType_DirectX12 )
807+ {
808+ tex.handle = &m_vrD3D12Texture;
809+ }
810+ #endif
811+ else
812+ {
813+ tex.handle = GetNativeEyeTexture ( nStage, nTexIndex );
814+ }
755815 tex.eType = m_eActiveTextureType;
756816 tex.eColorSpace = vr::ColorSpace_Auto;
757817
758818 // Check if we have a valid depth buffer
759- if (m_pNativeDepthTextures[nStage][nTexIndex])
819+ if ( m_pNativeDepthTextures[nStage][nTexIndex] )
760820 {
761821 tex.depth .handle = m_pNativeDepthTextures[nStage][nTexIndex];
762822 }
@@ -767,12 +827,27 @@ bool OpenVRDisplayProvider::SubmitToCompositor( vr::EVREye eEye, int nStage )
767827 vr::EVRSubmitFlags nFlags = m_eActiveTextureType == vr::TextureType_Vulkan ? vr::Submit_VulkanTextureWithArrayData : vr::Submit_Default;
768828
769829 // Submit the texture to the Compositor
770- vr::EVRCompositorError res = vr::VRCompositorError_None;
771- res = vr::VRCompositor ()->Submit ( eEye, &tex, &m_textureBounds, nFlags );
830+ vr::EVRCompositorError res = vr::VRCompositor ()->Submit ( eEye, &tex, &m_textureBounds, nFlags );
772831
773832 if ( res != vr::VRCompositorError_None )
774833 {
775- XR_TRACE ( " [OpenVR] [Error] Unable to submit eye texture: [%i] [%x]\n " , res, tex.handle );
834+ const char *errorName = " Unknown" ;
835+ switch ( res )
836+ {
837+ case vr::VRCompositorError_RequestFailed: errorName = " RequestFailed" ; break ;
838+ case vr::VRCompositorError_IncompatibleVersion: errorName = " IncompatibleVersion" ; break ;
839+ case vr::VRCompositorError_DoNotHaveFocus: errorName = " DoNotHaveFocus" ; break ;
840+ case vr::VRCompositorError_InvalidTexture: errorName = " InvalidTexture" ; break ;
841+ case vr::VRCompositorError_IsNotSceneApplication: errorName = " IsNotSceneApplication" ; break ;
842+ case vr::VRCompositorError_TextureIsOnWrongDevice: errorName = " TextureIsOnWrongDevice" ; break ;
843+ case vr::VRCompositorError_TextureUsesUnsupportedFormat: errorName = " TextureUsesUnsupportedFormat" ; break ;
844+ case vr::VRCompositorError_SharedTexturesNotSupported: errorName = " SharedTexturesNotSupported" ; break ;
845+ case vr::VRCompositorError_IndexOutOfRange: errorName = " IndexOutOfRange" ; break ;
846+ case vr::VRCompositorError_AlreadySubmitted: errorName = " AlreadySubmitted" ; break ;
847+ default : break ;
848+ }
849+ XR_TRACE ( " [OpenVR] [Error] Unable to submit eye %i texture for stage %i: [%i - %s] handle: %p, type: %i\n " ,
850+ eEye, nStage, res, errorName, tex.handle , tex.eType );
776851 return false ;
777852 }
778853 }
@@ -790,6 +865,7 @@ void OpenVRDisplayProvider::SetupRenderPass( const vr::EVREye eEye, const UnityX
790865 int32_t nRenderPassesCount;
791866 int32_t nRenderParamsCount;
792867 int32_t nTextureArraySlice;
868+ int32_t nTextureIndex;
793869
794870 if ( m_bUseSinglePass )
795871 {
@@ -800,6 +876,7 @@ void OpenVRDisplayProvider::SetupRenderPass( const vr::EVREye eEye, const UnityX
800876 nRenderPassesCount = 1 ;
801877 nRenderParamsCount = 2 ;
802878 nTextureArraySlice = eEye;
879+ nTextureIndex = 0 ;
803880 }
804881 else
805882 {
@@ -810,14 +887,15 @@ void OpenVRDisplayProvider::SetupRenderPass( const vr::EVREye eEye, const UnityX
810887 nRenderPassesCount = 2 ;
811888 nRenderParamsCount = 1 ;
812889 nTextureArraySlice = 0 ;
890+ nTextureIndex = eEye;
813891 }
814892
815893 // Set the number of render passes for this frame
816894 pTargetFrame->renderPassesCount = nRenderPassesCount;
817895
818896 // Setup base render pass properties
819897 UnityXRNextFrameDesc::UnityXRRenderPass &renderPass = pTargetFrame->renderPasses [nRenderPasses];
820- renderPass.textureId = m_UnityTextures[m_nCurFrame % m_nNumStages][nRenderPasses ];
898+ renderPass.textureId = m_UnityTextures[m_nCurFrame % m_nNumStages][nTextureIndex ];
821899 renderPass.renderParamsCount = nRenderParamsCount;
822900 renderPass.cullingPassIndex = nRenderPasses;
823901
@@ -1089,10 +1167,19 @@ UnitySubsystemErrorCode OpenVRDisplayProvider::CreateEyeTextures( const UnityXRF
10891167 eyeWidth = (uint32_t )eyeWidthScaled;
10901168 eyeHeight = (uint32_t )eyeHeightScaled;
10911169
1170+ bool bUseTextureArrays = m_bUseSinglePass;
1171+
1172+ if ( m_bUseSinglePass )
1173+ {
1174+ XR_TRACE ( " [OpenVR] Single-Pass mode: Creating texture array with 2 slices\n " );
1175+ }
1176+
10921177 // Create textures
10931178 for ( int stage = 0 ; stage < m_nNumStages; ++stage )
10941179 {
1095- for ( int eye = 0 ; eye < nNumTextures; ++eye )
1180+ int nTextureCount = bUseTextureArrays ? 1 : nNumTextures;
1181+
1182+ for ( int eye = 0 ; eye < nTextureCount; ++eye )
10961183 {
10971184 UnityXRRenderTextureDesc unityDesc;
10981185 memset ( &unityDesc, 0 , sizeof ( UnityXRRenderTextureDesc ) );
@@ -1108,11 +1195,22 @@ UnitySubsystemErrorCode OpenVRDisplayProvider::CreateEyeTextures( const UnityXRF
11081195 unityDesc.flags |= kUnityXRRenderTextureFlagsSRGB ;
11091196 }
11101197
1111- if ( m_bUseSinglePass )
1198+ if ( bUseTextureArrays )
11121199 {
11131200 unityDesc.textureArrayLength = 2 ;
11141201 }
11151202
1203+ // Log texture creation details
1204+ const char *apiName = " Unknown" ;
1205+ if ( m_eActiveTextureType == vr::TextureType_DirectX ) apiName = " D3D11" ;
1206+ else if ( m_eActiveTextureType == vr::TextureType_DirectX12 ) apiName = " D3D12" ;
1207+ else if ( m_eActiveTextureType == vr::TextureType_Vulkan ) apiName = " Vulkan" ;
1208+ else if ( m_eActiveTextureType == vr::TextureType_OpenGL ) apiName = " OpenGL" ;
1209+
1210+ XR_TRACE ( " [OpenVR] Creating %s texture (stage %i, eye %i): %ux%u, arrayLen: %u, sRGB: %i, depth: %i\n " ,
1211+ apiName, stage, eye, eyeWidth, eyeHeight, unityDesc.textureArrayLength ,
1212+ (unityDesc.flags & kUnityXRRenderTextureFlagsSRGB ) ? 1 : 0 , unityDesc.depthFormat );
1213+
11161214 // Create an UnityXRRenderTextureId for the native texture so we can tell unity to render to it later.
11171215 UnityXRRenderTextureId unityTexId;
11181216 UnitySubsystemErrorCode res = s_pXRDisplay->CreateTexture ( s_DisplayHandle, &unityDesc, &unityTexId );
@@ -1122,9 +1220,23 @@ UnitySubsystemErrorCode OpenVRDisplayProvider::CreateEyeTextures( const UnityXRF
11221220 return res;
11231221 }
11241222
1125- m_UnityTextures[stage][eye] = unityTexId;
1126- m_pNativeColorTextures[stage][eye] = nullptr ; // this is just registering a creation request, we'll grab the native textures later
1127- m_pNativeDepthTextures[stage][eye] = nullptr ;
1223+ if ( bUseTextureArrays )
1224+ {
1225+ // Single-pass: one texture array for both eyes
1226+ m_UnityTextures[stage][0 ] = unityTexId;
1227+ m_UnityTextures[stage][1 ] = unityTexId;
1228+ m_pNativeColorTextures[stage][0 ] = nullptr ;
1229+ m_pNativeColorTextures[stage][1 ] = nullptr ;
1230+ m_pNativeDepthTextures[stage][0 ] = nullptr ;
1231+ m_pNativeDepthTextures[stage][1 ] = nullptr ;
1232+ }
1233+ else
1234+ {
1235+ // Multi-pass: separate texture per eye
1236+ m_UnityTextures[stage][eye] = unityTexId;
1237+ m_pNativeColorTextures[stage][eye] = nullptr ;
1238+ m_pNativeDepthTextures[stage][eye] = nullptr ;
1239+ }
11281240 }
11291241 }
11301242
@@ -1162,15 +1274,28 @@ void *OpenVRDisplayProvider::GetNativeEyeTexture( int stage, int eye )
11621274 UnitySubsystemErrorCode res = s_pXRDisplay->QueryTextureDesc ( s_DisplayHandle, unityTexId, &unityDesc );
11631275 if ( res != kUnitySubsystemErrorCodeSuccess )
11641276 {
1165- XR_TRACE ( " [OpenVR] Error querying texture: [%i]\n " , res );
1277+ XR_TRACE ( " [OpenVR] Error querying texture for stage %i eye %i : [%i]\n " , stage, eye , res );
11661278 return nullptr ;
11671279 }
11681280
11691281 m_pNativeColorTextures[stage][eye] = unityDesc.color .nativePtr ;
1170- XR_TRACE ( " [OpenVR] Created Native Color: %x\n " , unityDesc.color .nativePtr );
1171-
11721282 m_pNativeDepthTextures[stage][eye] = unityDesc.depth .nativePtr ;
1173- XR_TRACE ( " [OpenVR] Created Native Depth: %x\n " , unityDesc.depth .nativePtr );
1283+
1284+ // Log texture information with API type
1285+ const char *apiName = " Unknown" ;
1286+ if ( m_eActiveTextureType == vr::TextureType_DirectX ) apiName = " D3D11" ;
1287+ else if ( m_eActiveTextureType == vr::TextureType_DirectX12 ) apiName = " D3D12" ;
1288+ else if ( m_eActiveTextureType == vr::TextureType_Vulkan ) apiName = " Vulkan" ;
1289+ else if ( m_eActiveTextureType == vr::TextureType_OpenGL ) apiName = " OpenGL" ;
1290+
1291+ XR_TRACE ( " [OpenVR] %s Native Color Texture (stage %i, eye %i): %p (width: %u, height: %u, arrayLen: %u)\n " ,
1292+ apiName, stage, eye, unityDesc.color .nativePtr , unityDesc.width , unityDesc.height , unityDesc.textureArrayLength );
1293+
1294+ if ( unityDesc.depth .nativePtr )
1295+ {
1296+ XR_TRACE ( " [OpenVR] %s Native Depth Texture (stage %i, eye %i): %p\n " ,
1297+ apiName, stage, eye, unityDesc.depth .nativePtr );
1298+ }
11741299 }
11751300
11761301 return m_pNativeColorTextures[stage][eye];
@@ -1212,25 +1337,28 @@ void OpenVRDisplayProvider::SetupOverlayMirror()
12121337 }
12131338 }
12141339
1215- ID3D11Device *m_pD3D11Device;
1340+ ID3D11Device *m_pD3D11Device = nullptr ;
12161341 if ( m_eActiveTextureType == vr::TextureType_DirectX )
12171342 {
1218- // Grab the active render device
1343+ // Grab the active render device for D3D11
12191344 m_pD3D11Device = s_pProviderContext->interfaces ->Get < IUnityGraphicsD3D11 >()->GetDevice ();
12201345
12211346 // Create a native device handle for OpenVR
12221347 m_nativeDevice.eType = vr::DeviceType_DirectX11;
12231348 m_nativeDevice.handle = m_pD3D11Device;
12241349 }
1350+ else if ( m_eActiveTextureType == vr::TextureType_DirectX12 )
1351+ {
1352+ // OpenVR overlay mirror only supports DirectX11 (no DeviceType_DirectX12 in API as of 2025)
1353+ // D3D12 will fall back to standard left/right eye mirror mode
1354+ m_nativeDevice.handle = nullptr ;
1355+ m_pD3D11Device = nullptr ;
1356+ }
12251357 else
12261358 {
12271359 m_nativeDevice.handle = nullptr ;
12281360 m_pD3D11Device = nullptr ;
12291361 }
1230-
1231- // Create a native device handle for OpenVR
1232- m_nativeDevice.eType = vr::DeviceType_DirectX11;
1233- m_nativeDevice.handle = m_pD3D11Device;
12341362
12351363 // Get the active overlay view - should be our scene application now at this stage
12361364 if ( m_nMirrorMode == kUnityXRMirrorBlitDistort && !m_bOverlayFallback && m_hOverlay != k_ulInvalidOverlayHandle && m_bIsUsingRGB && !m_bIsUsingCustomMirrorMode && vr::VRSystem () && vr::VROverlayView () )
@@ -1343,6 +1471,11 @@ void OpenVRDisplayProvider::SetupOverlayMirror()
13431471 m_bOverlayFallback = true ;
13441472 XR_TRACE ( " [OpenVR] [Error] [Mirror] OpenVR View falling back to left eye texture. Project not using sRGB.\n " );
13451473 }
1474+ else if ( m_eActiveTextureType == vr::TextureType_DirectX12 )
1475+ {
1476+ m_bOverlayFallback = true ;
1477+ XR_TRACE ( " [OpenVR] [Mirror] OpenVR View falling back to left eye texture. DirectX 12 uses standard mirror mode.\n " );
1478+ }
13461479 else if ( m_eActiveTextureType != vr::TextureType_DirectX )
13471480 {
13481481 m_bOverlayFallback = true ;
0 commit comments