Description

Reported via UDN (case No107). Verified against //UE5/Release-5.6 sources; same code present in 5.5, 5.7, 5.8 and Main. Licensee reproduces on 5.3 and 5.6 with the Canon MREAL runtime.

Symptom

Frame-level offset between the color image and the submitted depth (XR_KHR_composition_layer_depth) in OpenXR apps. Intermittent; occurrence depends on RHI, GPU driver and scene load.

Root cause analysis (Release-5.6 line refs)

  • xr.OpenXRAcquireMode defaults to 2, so bIsAcquireOnAnyThreadSupported is false on all RHIs (OpenXRHMD.cpp [Link Removed], L1607). The per-RHI auto branch is dead code by default.
  • Color: FSceneViewport::EnqueueBeginRenderFrame (SceneViewport.cpp [Link Removed]) selects the frame's render target from AcquireColorTexture(). Under mode 2 this performs no acquire and just reads SwapChainIndex_RHIThread (OpenXRHMD.cpp L2617), i.e. whatever the RHI thread last acquired at that instant. Correct pairing with the image waited/released that frame relies on the game thread being exactly one acquire behind the RHI thread. Nothing enforces this; pipeline depth variation (load, driver, RHI) breaks it.
  • Depth: acquired on the RHI thread in OnBeginRendering_RHIThread ([Link Removed]), while the renderer's depth target is captured on the render thread via GetTextureRef() in AllocateDepthTexture (L2860). That pointer is rewritten by every acquire (FOpenXRSwapchain::IncrementSwapChainIndex_RHIThread). Unsynchronized read, sampled at a different time than color, so color and depth slip independently.

Steps to reproduce

  1. Build an OpenXR app following [Link Removed] and add moderate scene load. Plugins: OpenXR, OpenXRHandTracking.
  2. Run on the Canon MREAL OpenXR runtime with default settings (xr.OpenXRAcquireMode=2), D3D12 or Vulkan RHI.
  3. Compare submitted color vs depth (XR_KHR_composition_layer_depth) per frame.

Result: color and depth are intermittently offset by one frame. Occurrence varies with RHI and driver. With xr.OpenXRAcquireMode=1: greatly reduced but not zero on D3D12; immediate crash on Vulkan.

Workaround status

xr.OpenXRAcquireMode=1 drastically reduces but does not eliminate mismatches on D3D12. Expected residuals: (a) when the game thread runs 2+ frames ahead of the RHI-thread Wait, the ImageAcquired guard turns the acquire into a no-op ("Attempted to redundantly acquire image") and two consecutive frames target the same image; (b) the render-thread GetTextureRef() capture for depth can race the next game-thread acquire.

Mode 1 is not viable on Vulkan: the game-thread acquire path has no queue marshalling while xrAcquireSwapchainImage may access the VkQueue (FIXME in OpenXRHMD_Swapchain.cpp; cf. the 5.6.1 RunOnGraphicsQueue wrapping on all RHI-side call sites). Licensee confirms an immediate crash on Vulkan with mode 1.

Suggested direction

Propagate the acquired image index/texture explicitly per frame through the game/render/RHI pipeline instead of racy reads of SwapChainIndex_RHIThread/RHITexture, and keep Vulkan acquires marshalled via RunOnGraphicsQueue.

Licensee environment

Win11 23H2, i9-14900KF, RTX 4080, Canon MREAL runtime. Mode 2 matrix: driver 581.08: mismatch on Vulkan and D3D12, none on D3D11. Driver 566.36: mismatch on D3D12, none on D3D11 (Vulkan fails the engine driver check on this version, unrelated).

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-388831 in the post.

0
Login to Vote

Unresolved
CreatedJul 29, 2026
UpdatedAug 4, 2026
View Jira Issue