Skip to content

WebXR over WebGPU: XRGPUBinding session glue and two-pass stereo - #5

Open
octopusburrow wants to merge 3 commits into
dwalter:webgpu-4.6.2from
octopusburrow:pr3-webxr-webgpu-combined
Open

WebXR over WebGPU: XRGPUBinding session glue and two-pass stereo#5
octopusburrow wants to merge 3 commits into
dwalter:webgpu-4.6.2from
octopusburrow:pr3-webxr-webgpu-combined

Conversation

@octopusburrow

Copy link
Copy Markdown

Adds working immersive-vr on the WebGPU driver:

  • config: allow building the webxr module without opengl3 (gated to webgpu=yes; the
    old opengl3 gate predates WebGPU XR and matches stock Godot).
  • JS glue: XRGPUBinding session setup (requires the webgpu session feature). The
    projection layer is created ONCE as a texture-array for immersive-vr: the previous
    build-mono-then-rebuild-on-first-pose path cost a measured ~10s of session startup on
    desktop Chrome (swapchain allocate/teardown/allocate, and a mid-session layer swap the
    WebXR-WebGPU spec currently leaves undefined).
  • renderer: WebGPU has no multiview, so each eye renders as an ordinary single-view
    pass into its layer of the XR texture array.

Behavior on WebGL sessions is functionally unchanged — the stereo-from-start default is
gated to WebGPU immersive-vr, and handheld AR keeps the upstream 1-view fallback on both
backends. (Two diagnostics added during development fired spuriously on WebGL paths;
both are now gated to the WebGPU binding.)

Dependencies and disclosures:

  • Requires an emdawnwebgpu recent enough to provide WebGPU.importJsTexture.
  • engine.js now requests xrCompatible: true adapters for WebGPU builds; WebIDL
    ignores unknown dictionary members so this is inert on non-XR browsers, but on
    multi-GPU systems it can influence adapter selection (caller-provided adapters are
    honored unchanged). Newer Chrome enforces the spec's xrCompatible requirement
    (XRGPUBinding throws InvalidStateError without it), so this is load-bearing, not
    hygiene.
  • Per-view passes are capped at 2 views (view_slice_cache); >2-view devices would need
    a small extension.
  • On this PR alone, directional shadow cascades render once per eye (view-independent
    work duplicated); a follow-up PR shares them across the two passes.

Verified end-to-end on desktop Chrome + SteamVR: session entry (instant, vs ~10s before
the single-allocation fix), correct per-eye stereo projection, exit and re-entry. Known
limitation: no depth submission (the layer declares none; Chromium currently submits no
depth on any path). immersive-ar alpha handling is future work, noted in-code.

Comfort note: desktop-PCVR WebGPU sessions on current release Chrome exhibit a
compositor-side pose-drift artifact unrelated to this PR (Chromium missed a swap-chain
sync token; tracked at mrdoob/three.js#34343, affects all WebGPU-XR content). Fixed
upstream in CL 8275843 (merged 2026-08-25, ships in M154 — Canary/Dev today); we
verified the fix resolves it on this code path.

octopusburrow and others added 3 commits August 27, 2026 02:33
…acros

Newer emdawnwebgpu packages add a WGPUStringView message parameter to
WGPUQueueWorkDoneCallback (4 params: status, message, userdata1,
userdata2); earlier packages use 3 (no message). The signature is a
property of the Dawn package each emsdk release pins, so no clean
version macro exists — and macro PRESENCE does not discriminate: both
generations define WGPU_STRLEN/WGPUStringView (verified against Dawn
v20260423.175430 [4-param] and the earlier package in this fork's
deploy toolchain [3-param]).

So: provide both signatures as overloads of _fence_work_done_callback
and let the assignment to the port's own WGPUQueueWorkDoneCallback
typedef select the matching one. The 4-param overload is additionally
guarded on WGPU_STRING_VIEW_INIT for hypothetical pre-StringView
headers. The dispatch pattern compiles against both package
generations (verified standalone on both toolchains); a full engine
build is verified on the 3-param toolchain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…check

Vendored as patches/0007 per the thirdparty/tint/patches/ convention.
This is a Group B (Specialization Constants) patch, completing the
capability this fork introduced in 0002 and extended in 0003/0005:
Godot's specialization constants change effective struct and array sizes
at runtime, so a spec-constant-sized array leaves the containing struct's
Size() at its unfolded value and the validator's TOTAL-size check
(str->Size() < cur_offset) fires on IR that the fork's own 0002 rationale
already classifies as valid-by-construction. 0002 relaxed the per-MEMBER
size check for this exact producer; this patch applies the same
capability to the struct-total check a few lines below it.

Without this, the Mobile renderer's scene shader (depth shadow atlas +
sampler UBO with spec-constant-sized arrays) fails Tint validation and
every lit 3D scene renders black on Dawn/Windows, including this fork's
own sample projects. Unshaded scenes are unaffected, which is why the
gap survived: the fork's Windows TODO note and this failure are the same
bug.

Layer choice: an alternative would be changing Godot's shader generation
so emitted SPIR-V never carries the mismatch, touching no vendored code.
This fork already faced that choice for the member-size case and chose
validator relaxation, three times (0002, 0003, 0005, documented as
Group B in patches/README.md); this patch follows the established
architecture rather than opening a second front. If Group B is ever
re-architected toward shader-gen fixes, this check belongs in that
migration with the rest of the group.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds immersive WebXR support to the WebGPU backend on the web platform.

Build: the WebXR module was hard-gated on opengl3; this allows it to build
in webgpu=yes/opengl3=no configurations (config.py). The gate is the only
thing that changes for non-WebGPU builds.

Runtime: binds the XR session via XRGPUBinding (not XRWebGLBinding) when the
engine runs on the WebGPU driver, and renders stereo as two single-view
passes into the layers of the XR texture array, since WebGPU has no multiview
extension. The per-view path is opt-in via XRInterface::needs_per_view_passes()
(defaults false), so every existing XR backend — WebGL/WebXR included — takes
the unchanged single/multiview path.

WebGL-XR behavior is unchanged: it continues to use XRWebGLBinding and the
operator-supplied WebXR Layers polyfill exactly as before.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant