Wavefront RT: atomic pixel accumulator to allow >1 ray per pixel per bounce #30
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Catcrafts/Crafter.Graphics#30
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
The WebGPU wavefront ray tracer's per-pixel accumulator (
wfAccum) isnon-atomic by design, which caps the renderer at one ray per pixel per
bounce. This blocks multi-light shadowing — where a SHADE invocation wants to
emit one shadow ray per light and have each resolve independently — because
the multiple shadow rays for a single pixel resolve in the same SHADE pass and
race on the non-atomic read-modify-write.
Request: an atomic accumulate path so a closest-hit shader can safely emit
N shadow rays per pixel in a single bounce, matching what the hardware (Vulkan)
RT backend already does trivially.
Where the limitation lives
additional/dom-webgpu.js:1677—@group(1) @binding(13) var<storage,read_write> wfAccum : array<vec4<f32>>;additional/dom-webgpu.js:1701-1703—rtAccumulate:implementations/Crafter.Graphics-PipelineRTWebGPU.cpp— codegen sites thattouch
wfAccumdirectly:wfAccum[pixel] = vec4<f32>(0.0, 0.0, 0.0, 0.0);runResolve(gid.xy, wfAccum[pixel])Why it matters (downstream)
3DForts issue #153: a nuclear blast should be a real shadow-casting light, and
every dynamic light up to the
maxLightsbudget should cast a shadow ray(today only the single brightest light per surface is shadowed; the rest leak
in as unshadowed fill, so blast light passes straight through walls). On the
Vulkan backend this is a trivial loop of synchronous
traceRayEXTcallsreturning a
shadowValue. On the WebGPU wavefront backend it requires emittingone shadow ray per light from the same closest-hit invocation — i.e. several
rays for the same pixel in one bounce, whose miss-stage
rtAccumulate(payload.color)calls then race.
Proposed change
Make the accumulator atomic (float add via CAS), so >1 ray/pixel/bounce is safe:
wfAccumtoarray<atomic<u32>>with 4 u32 slots per pixel (RGBA;same 16 B/pixel footprint, no host-side allocation change).
rtAccumulateto CAS each channel:PipelineRTWebGPU.cpp:atomicStore0 to the 4 slots (bitcast(0.0) == 0u).atomicLoadthe 4 slots,bitcast<f32>, assemble thevec4<f32>passed to
runResolve.All existing RT consumers (Sponza, RTStress, …) emit ≤1 ray/pixel/bounce, so
their output is unchanged — this is strictly a correctness-preserving widening.
Tradeoffs / open questions
rtAccumulatebecomes a CAS loop (3 channels). Under lowcontention the CAS succeeds first try; only the multi-shadow-ray bounce sees
real contention. If the per-accumulate cost is a concern for simple scenes,
the atomic path could be gated behind a pipeline flag (e.g. an
RTPass/pipeline option) so single-ray consumers keep the plain add.ray emits the next on resolve), which needs no accumulator change but raises
the required max ray-recursion depth to ~N and serializes N barriered passes.
Heavier at runtime and more shader complexity; the atomic accumulator is the
cleaner primitive.
Filed from 3DForts (#153). Happy to send the PR if the approach (and the
flag-gating question) looks right.