Skip to content

XR: render directional shadow cascades once per frame in per-view-pass mode - #7

Open
octopusburrow wants to merge 4 commits into
dwalter:webgpu-4.6.2from
octopusburrow:pr5-xr-shadow-share
Open

XR: render directional shadow cascades once per frame in per-view-pass mode#7
octopusburrow wants to merge 4 commits into
dwalter:webgpu-4.6.2from
octopusburrow:pr5-xr-shadow-share

Conversation

@octopusburrow

Copy link
Copy Markdown

In the per-view-pass path (no multiview on WebGPU), each eye runs a full _render_scene,
and the directional shadow block has no dirty/version guard — cascades were fully
re-rendered per eye. This adds a set_xr_repeat_view_pass() hint (RenderingMethod,
default no-op) set by the two-pass loop for view passes ≥ 1; RendererSceneCull then
skips directional-shadow collection/setup on repeat passes (shadow_count stays 0 → no
cascade cull, no re-render) while the lighting list and the first pass's atlas + cascade
transforms persist untouched. The skip is guarded to actual camera passes (a reflection
probe rendering inside the window — none does today — keeps its own shadow state), and
the flag's bracketing invariant is documented at the set site.

Honest caveat: the shared cascades are fit to the FIRST view's frustum (in per-view-pass
mode the interface reports the current eye only), so the second eye samples a fit made
for the first. The eyes' frusta nearly coincide and the pre-change behavior — each eye
fitting its own cascades — made shadows differ between the eyes, which is worse for
stereo; but a combined-frustum fit is the proper follow-up and is noted in-code.

Positional (omni/spot) shadows already behave: the shadow atlas has a version guard, and
detect_light_intersects_multiple_cameras handles the two-camera case.

Measured with the driver's perf counters on a shadowed test scene (12 cubes + one
shadow-casting DirectionalLight, emulated-XR on a real GPU): XR draws/frame 30 → 22,
render passes/frame 8 → 6 — exactly the directional-shadow delta that was doubling.
Desktop is unchanged (the hint is never set outside the per-view loop).

octopusburrow and others added 4 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.
…s mode

(see build-tree commit message — measured 30->22 draws/f, 8->6 RP/f in XR,
desktop unchanged)
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