Skip to content

Quest Browser: immersive WebXR session hangs when MSAA + per-frame shadow map pass + SplatMesh are combined #400

Description

@sawa-zen

Describe the bug

On Meta Quest 3 (Meta Quest Browser), an immersive WebXR session hangs within ~2 seconds and never recovers (the page's rAF loop stops permanently; the OS itself stays responsive) when all three of the following are active:

  1. new THREE.WebGLRenderer({ antialias: true }) (MSAA)
  2. renderer.shadowMap.enabled = true with a shadow-casting light rendering a per-frame shadow depth pass
  3. A Spark SplatMesh being rendered

Removing any one of the three avoids the hang. Verified matrix (each condition tested in isolation on device):

antialias shadow pass SplatMesh result
on off on OK
off on on OK
on on off OK
on on on hangs within seconds
on on (baked once via shadowMap.autoUpdate = false) on OK

Additional observations:

  • Adding the SplatMesh mid-session vs. before entering VR makes no difference.
  • Not reproducible on desktop, PICO 4, or Apple Vision Pro — Quest Browser only.
  • WASM + SIMD are available on the device (verified in-page); sorting runs in the worker normally, so this is not a WASM-fallback issue.
  • Throttling with minSortIntervalMs was not required for the workaround; the deciding factor is whether the shadow depth pass runs every frame.

Environment

Reproduction

Single-file repro (CDN imports only, all variables toggled via URL params):
https://github.com/sawa-zen/xrift-world-3dgs-musium/tree/7fab1f8/tools/quest-msaa-repro

Serve the folder over HTTPS, put any .spz next to it as model.spz, open on Quest, enter VR (a spinning cube + frame counter make the hang obvious):

  • /?aa=1&shadow=0 → OK
  • /?aa=0&shadow=1 → OK
  • /?aa=1&shadow=1&splat=0 → OK
  • /?aa=1&shadow=1hangs
  • /?aa=1&shadow=1&shadowstatic=1 (shadow map baked once) → OK

Suspected mechanism

The per-frame shadow depth pass (render-target switch to a depth texture), the MSAA resolve of the XR framebuffer, and Spark's out-of-band GPU work (update pass scheduled via setTimeout + fence-based async depth readback for sorting) appear to deadlock the Adreno driver when interleaved every frame. We could not pinpoint the exact GL interaction without a GPU debugger.

Workaround for other users

renderer.shadowMap.autoUpdate = false (bake shadows once / on demand) allows MSAA + shadows + Spark to coexist on Quest.

Happy to test patches or provide more traces.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions