We are using Passthrough in our Unreal Engine project with the Meta Quest 3. Since updating both the headset and Quest Link to the latest version, the camera passthrough image always appears in the foreground, covering all other scene content.
This issue does not occur when using a headset with firmware version v74 — in that case, the Passthrough Layer component works correctly and is rendered as an underlay as intended.
Current Project and Passthrough Settings:
Project Settings > Rendering
Enable alpha channel support in post processing: “Allow through tonemapper”
Are you calling the MetaXR APIs – especially the initialization of the Persistent Passthrough – via Blueprint or via C++?
The C++ API for (???, v81, v83) is currently completely broken (if you trace through it) as it completely ignores the “Shape” and Overlay/Underlay settings passed in.
I’ve had to patch the C++ code myself, and it appears Meta has not noticed nor fixed it themselves. And with Meta now abandoning VR, I’m not confident the UE port team is still going to be around.
Meanwhile, the Blueprint path works well as demonstrated by their “Unreal-PassthroughSample”.
If anybody is interested, I can post my patch for their broken C++ code.
Hi NuShrike, I’m running into the exact same issue described here — “Unable to retrieve OculusXRHMD, cannot add event polling delegate” — using UE5.7.4 with the latest Meta XR Plugin (v201.0). Confirmed the same failure even in Meta’s official Unreal-PassthroughSample. Would really appreciate it if you could share your patch for the broken C++ passthrough code. Thank you!
Here’s my patch for the still broken C++ API for Passthrough Underlay. Should compile even in UE 5.8.0.
Author: NuShrike <73401213+NuShrike@users.noreply.github.com> 2025-11-29 16:05:34
[MetaXR] Fix critical bug in UOculusXRPersistentPassthroughInstance::UpdateParameters
Meta''s stock implementation corrupts the Passthrough Shape config on every call:
1) In editor it unconditionally nulled InParameters.Shape, discarding the caller''s settings.
2) It then built a new default UOculusXRStereoLayerShapeReconstructed to fix ownership.
Right after, the struct-copy Parameters = InParameters overwrote that new Shape with the (possibly nulled) incoming one — negating #2 and inheriting #1's data loss.
Net result: Passthrough ends up with no Shape configured.
Fix: keep the incoming Shape''s settings via ApplyShape(), struct-copy first, only null+recreate Parameters.Shape when it isn''t actually owned by this, then restore/reconfigure it via LoadShape(this) before UpdateLayer().
diff --git a/Private/OculusXRPersistentPassthroughInstance.cpp b/Private/OculusXRPersistentPassthroughInstance.cpp
--- a/Private/OculusXRPersistentPassthroughInstance.cpp
+++ b/Private/OculusXRPersistentPassthroughInstance.cpp
@@ -32,18 +32,21 @@ void UOculusXRPersistentPassthroughInstance::InitLayer(FOculusXRPersistentPassth
void UOculusXRPersistentPassthroughInstance::UpdateParameters(FOculusXRPersistentPassthroughParameters InParameters)
{
-#if WITH_EDITOR
- // We need the instance to be the owner of the shape, but in editor the shape still exist and its owner is the BP calling the method.
- // So we clear it to force a new one to be created with LoadShape(this)
- InParameters.Shape = nullptr;
-#endif
+ // make sure incoming Shape settings are set
+ InParameters.ApplyShape();
+
+ // struct-copy InParameters to Parameters
+ Parameters = InParameters;
- if (InParameters.LoadShape(this) == nullptr)
+ if (nullptr != Parameters.Shape && this != Parameters.Shape.GetOuter())
{
- Parameters.Shape = NewObject<UOculusXRStereoLayerShapeReconstructed>(this, NAME_None, RF_Public);
+ // We need the instance to be the owner of the Shape, so clear it if it''s not the correct owner
+ // to force a new one to be created with LoadShape(this)
+ Parameters.Shape = nullptr;
}
- Parameters = InParameters;
+ // FIX: restore/reconfigure Parameters.Shape after struct-copy from InParameters & REQUIRED for UpdateLayer()
+ Parameters.LoadShape(this);
UpdateLayer();
}
That error message basically says your headset isn’t plugged in, wrong XR system-runtime.
Seems easier to happen with the Epic OpenXR plugin because it checks for the plugged-in headset at editor start-up time, instead of “VR Preview” time like the original OVRPlugin.
Try replugging in the headset, or before you start the editor.