Tonemap Subpass breaks when Passthrough is enabled - UE 5.6 Oculus Fork

Hey everyone,

I’ve been running into a strange issue with Tonemap Subpass on Quest (UE 5.6, Meta fork).
Everything works perfectly as long as Passthrough is disabled in the Meta XR settings.

But the moment I enable the “Passthrough Enabled” checkbox — even if I never actually start passthrough at runtime — the image in the headset changes completely.
It becomes much darker, and things like color tint or post-process adjustments stop having any visible effect.

What’s weird is that this happens even when passthrough is set as an underlay, and even though I never call any passthrough functions (so the camera feed never shows up).
It’s enough to just enable it in the project settings.

I captured the frame with RenderDoc, and you can clearly see that part of the frame still goes through the Tonemap pass, but something happens afterward — it looks like the final composition changes or the tonemap result is being blended incorrectly.
Almost as if enabling passthrough changes the way the swapchain or alpha blending is handled internally, even when it’s not used.

Repro steps:

  1. Set r.Mobile.TonemapSubpass=1

  2. Keep HDR off

  3. Run on Quest → Tonemap looks fine

  4. Enable Insight Passthrough in Meta XR settings

  5. Run again (without initiating passthrough at runtime)

  6. The image becomes darker, tint/post-process doesn’t work anymore

Tested on:

Device: Quest 3

Engine: UE 5.6 (Meta fork)

MSAA: 2x

HDR: Off

r.PostProcessing.PropagateAlpha: tested both 0 and 1


It looks like just having Passthrough enabled in project settings changes how the Tonemap Subpass behaves — even when the passthrough feed isn’t being used at all.
Anyone else seeing this? Or any idea what might be happening under the hood?

Do you know if the incorrect blending passes are done on the engine or runtime side? RenderDoc is possibly capturing both, since the runtime can insert render passes in the app process during frame submission.

I’ve seen people having trouble with similar issues where the alpha channel content of submitted frames are affecting the render, and I suspect it because Meta’s runtime has been updated to handle alpha premultiplication correctly. The OpenXR spec states that any frame with alpha enabled will be alpha blended, but most runtimes don’t seem to do this for opaque VR frames. This shouldn’t usually be a problem, unless the app is telling the runtime that the frame is not premultiplied, since the runtime has to now multiply the input color with the alpha.

I’m guessing that enabling passthrough is setting the source alpha bit and unpremultiplied bit on every submitted frame, making the runtime change the blending to match.

Hey! Thank you for the reply!

Yeah, I think it’s on the engine side.

When I preview with Vulkan on PC, I get exactly the same behavior as on Quest, so it’s not just a runtime issue i guess.

In RenderDoc, the alpha looks fine before the Tonemap pass, but once Tonemap starts, it seems to fetch or rebuild the alpha differently. After that, the alpha turns into a full-frame mask instead of following the scene geometry, basically the scene’s alpha gets lost.

I’ve also tried enabling r.Mobile.PropagateAlpha=1, but it doesn’t seem to pass anything through to the Tonemap Subpass.

So maybe it’s just a limitation or something missing in how the subpass handles alpha with Vulkan. :frowning:

Yeah, it sounds like it’s a bug in the engine fork then. It might be worth reporting the bug to Meta.

Alright so I think I have it fixed.

After a lot of debugging, I finally fixed the issue with the Tonemap Subpass on Quest (Vulkan).

Even when passthrough wasn’t active at runtime, just enabling “Insight Passthrough Enabled” in the Meta XR plugin made the image darker and completely ignored color grading or post-process adjustments.

Root cause:

The alpha channel wasn’t being preserved through the tonemap pass.

Two key problems were causing it:

1. The shader was explicitly forcing SceneColor.a = 1.0, which overwrote the real alpha data.

2. The tonemap pipeline sometimes downgraded the render target format to RGB-only (PF_R8G8B8), dropping the alpha entirely.

Because of that, even enabling r.PostProcessing.PropagateAlpha=1 did nothing, the data was already gone before composition.

Fix:

Removed the forced SceneColor.a = 1.0 assignment so the true alpha is kept.

Commented out the code that changed the color format from RGBA to RGB.

Forced the shader permutation to compile with FTonemapperAlphaChannelDim = true.

Ensured the output always writes RGBA (bForceWriteAlpha = true) in PostProcessTonemap.cpp.

Finally, enabled

r.Postprocessing.PropagateAlpha=1

After recompiling shaders, everything started working perfectly passthrough, HDR, tone mapping, and color grading now all behave correctly with preserved alpha.

I’ll share a clean patch file soon so others can drop it into their Meta fork and test the fix.

1 Like