## Summary
CloudXR.js 6.1.0 renders decoded video frames correctly in WebXR inline/2D mode but displays a black screen in immersive VR mode (`immersive-vr`) on Meta Quest 3. The streaming connection is stable, frames are sent and received, but the video content is not rendered to the XRWebGLLayer framebuffer.
## Environment
**Client:**
- Device: Meta Quest 3 (latest OS, auto-updated May 2026)
- Browser: Meta Quest Browser (Chromium-based)
- CloudXR.js: 6.1.0 (from NGC)
- Samples tested: Simple WebGL sample AND React Three Fiber sample — both exhibit the same issue
**Server:**
- Instance: AWS EC2 g6e.xlarge (NVIDIA L40S, 48 GB)
- OS: Windows Server 2022
- Driver: NVIDIA GRID 596.36 (WDDM mode)
- CloudXR Runtime: 6.0.5
- Device profile: `quest3`
- Application: CloudXR LÖVR sample (from GitHub - NVIDIA/cloudxr-lovr-sample: Stream your LÖVR VR applications wirelessly to supported headsets using NVIDIA CloudXR technology. This sample integrates CloudXR Runtime into LÖVR, enabling untethered VR experiences without physical cables. · GitHub )
**Network:**
- Signaling: WSS via nginx reverse proxy (port 48322 → localhost:49100)
- Media: Direct UDP via `mediaAddress` (ICE disabled per docs)
- Web client hosted on HTTPS (AWS CloudFront)
## Symptoms
### What works (inline/2D mode):
- Connecting via HTTP directly to port 49100 (no immersive session)
- Video frames decode and render correctly to the HTML canvas
- LÖVR scene content visible: controller models, cubes with XYZ axes, hand tracking data
- Connection stable for extended periods
### What fails (immersive VR mode):
- App enters `immersive-vr` session successfully (WebXR permission granted)
- Connection to server establishes (signaling + media)
- Server confirms frames are being sent (logs show 80+ frames delivered)
- Quest displays **completely black** in the headset
- In React sample: the opaque data channel UI panel (Action 1/Action 2 buttons) renders correctly as a floating 2D panel in VR space — proving the XR session is active and local rendering works
- Connection remains stable (no timeout, no crash) — data is flowing bidirectionally
- Issue persists regardless of codec (H.264, H.265, AV1), resolution settings, or foveation configuration
## Key Observation
The **same server, same connection, same frames** render correctly in inline mode but not in immersive mode. The only difference is the rendering target:
- Inline: frames rendered to HTML `` element ✅
- Immersive: frames should render to `XRWebGLLayer` framebuffer ❌
This points to a client-side issue in how CloudXR.js renders decoded frames to the XR compositor’s framebuffer in immersive mode.
## Reproduction Steps
1. Set up CloudXR Runtime 6.0.5 on a GPU server with the LÖVR sample running in `–webrtc` mode
2. Host either the Simple WebGL or React Three Fiber sample on HTTPS
3. On Quest 3, navigate to the sample and configure connection settings
4. Connect — observe “stream started” / stable connection
5. App enters immersive VR — observe black screen (no rendered content)
6. For comparison: connect via HTTP in non-immersive mode — observe rendered content (cubes, controllers)
## What We’ve Ruled Out
- **Server encoding:** NVENC is working (confirmed via logs: “Server sent the first video frame to client”, 80+ frames delivered)
- **Network delivery:** Frames arrive at the client (connection stable, no timeouts)
- **Client decoding:** Frames decode correctly (visible in 2D inline mode)
- **Codec compatibility:** Tested H.264, H.265, AV1 — all show same black screen
- **Resolution:** Tested various resolutions (1024x1024 through 4096x4032) — same result
- **WebXR session:** XR session is active (React sample’s local UI renders in VR space)
- **Quest OS version:** Latest (auto-updated)
- **Both samples:** Simple WebGL and React Three Fiber samples both exhibit identical behavior
## Questions for NVIDIA
1. Is there a known issue with CloudXR.js 6.1.0 rendering to XRWebGLLayer on Quest 3?
2. Are there specific WebGL context creation flags or XRWebGLLayer configuration required for CloudXR frame rendering in immersive mode?
3. Is there a working reference deployment of CloudXR.js 6.1.0 streaming to Quest 3 in immersive VR mode that we can compare against?
4. Does the `session.render(timestamp, frame, xrWebGLLayer)` call have specific requirements for the WebGL state or framebuffer binding that the samples might not be satisfying?
## Browser Console Errors (via chrome://inspect remote debugging)
The following errors appear in the Quest 3 browser console when entering immersive VR mode:
```
⚠ “No WebGL binding found”
c @ bundle.js:2
await in c
dispatchEvent @ bundle.js:2
Nt.setSession @ bundle.js:2
await in Nt.setSession
He @ bundle.js:2
Ve @ bundle.js:2
Qe @ bundle.js:2
await in Qe
enterVR @ bundle.js:2
Running on HTTPS protocol - using secure WebSocket (WSS)
No proxy URL - using direct WSS connection
Using user-provided server IP: 18.191.19.82
Using user-provided port: 48322
⚠ [WebGLStateApply] Cannot bind UNIFORM_BUFFER at index 0: buffer has been deleted. Skipping.
CloudXR session connect initiated
❌ WebGL context is already wrapped with state tracking. Returning existing BoundWebGLState.
⚠ WebGL context is already wrapped with state tracking. Returning existing BoundWebGLState.
⚠ WebGL context is already wrapped with state tracking. Returning existing BoundWebGLState.
(repeated 14+ times)
Requested frame rate 90 is supported; requested it.
❌ InvalidStateError: OfferSession promise was cancelled because it was called more than once.
```
### Analysis of Console Errors
1. **“No WebGL binding found”** — This appears to be the root cause. When entering immersive VR, CloudXR.js cannot find the WebGL shader/program binding needed to render decoded video frames to the XR layer’s framebuffer.
2. **“Cannot bind UNIFORM_BUFFER at index 0: buffer has been deleted”** — Indicates WebGL state corruption during the transition to immersive mode.
3. **“WebGL context is already wrapped with state tracking”** — CloudXR.js’s internal `BoundWebGLState` tracker is conflicting with the WebGL context. This occurs in both the Simple WebGL sample and the React Three Fiber sample, suggesting it’s internal to CloudXR.js rather than sample-specific.
4. **“OfferSession promise was cancelled because it was called more than once”** — Suggests a race condition in the WebRTC session establishment during XR mode entry.
### Hypothesis
CloudXR.js 6.1.0’s WebGL state management (`BoundWebGLState`) appears to lose track of the WebGL context when the browser transitions to an immersive XR session. The XR session may create or reconfigure the WebGL context in a way that invalidates CloudXR’s internal state tracking, causing the “No WebGL binding found” error and preventing frame rendering to the XRWebGLLayer.