fix(webgpu): reshape wavefront TRACE/SHADE to 2-D to survive >4.19M rays

A 1-D indirect dispatch of ceil(W*H/64) workgroups for the wavefront
TRACE/SHADE stages overflows maxComputeWorkgroupsPerDimension (65535 on
Dawn/Firefox) once the surface exceeds ~4.19M rays (~2560x1640). Per the
WebGPU spec such a dispatch is silently dropped — no validation error —
so at 4K the world is never traced and the accumulator stays black while
non-RT passes survive.

_wfPrep now spreads the workgroups across a 2-D grid (x clamped to 65535,
y = ceil(wg/65535)), and the wfTrace/wfShade entry points rebuild the
linear ray index from (global_invocation_id, num_workgroups). The existing
`i >= _wfCurCount()` guard absorbs the grid overshoot. GENERATE/RESOLVE
already use a 2-D tile dispatch and are unchanged.

Verified in Firefox/WebGPU with RTStress at a 3449x1739 surface (5.99M
rays, 93716 workgroups — well over the 65535 cap): renders the full cube
grid where master shows a black screen.

Resolves #11

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
catbot 2026-06-01 11:09:15 +00:00
commit 1e749818ef
2 changed files with 14 additions and 5 deletions

View file

@ -1834,9 +1834,18 @@ fn _wfReadRay(i: u32) -> WfRay {
// PREP — publish indirect args for the upcoming TRACE/SHADE; zero the next
// buffer's emit counter.
fn _wfPrep() {
let n = _wfCurCount();
wfIndirect[0] = (n + 63u) / 64u;
wfIndirect[1] = 1u;
let n = _wfCurCount();
let wg = (n + 63u) / 64u;
// maxComputeWorkgroupsPerDimension is 65535 on Dawn/Firefox; a 1-D
// dispatch of ceil(W*H/64) overflows it past ~4.19M rays (~2560x1640)
// and WebGPU silently drops the indirect dispatch -> black screen. Spread
// across a 2-D grid; wfTrace/wfShade rebuild the linear index from
// num_workgroups.
let MAXDIM = 65535u;
let gx = min(wg, MAXDIM);
let gy = (wg + MAXDIM - 1u) / MAXDIM; // = 1 when wg <= MAXDIM
wfIndirect[0] = gx;
wfIndirect[1] = gy;
wfIndirect[2] = 1u;
if (wfParams.curIsA == 1u) { atomicStore(&wfCounters[1], 0u); }
else { atomicStore(&wfCounters[0], 0u); }