Hatch/wipeout boundary sub-loops were separated by NaN vertices that the
fill shaders detected with `p.x == p.x`. Intel Mesa folds that NaN
self-compare to `true` under fast math, so separators read as real
vertices, the ray-cast crossing count broke, and fills bled outside
their boundary — only on Intel iGPUs. GPU uploads now rewrite the
separators to a finite 1e30 sentinel (GPU_BOUNDARY_SEP) and the shaders
test `abs(x) < 1e29`, which every driver evaluates identically. The
CPU-side NaN representation is unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tint (Chrome/Edge WebGPU) rejects derivative builtins reached after the
family loop's non-uniform early return, invalidating hatch_web/wipeout
shaders on Windows. Take dpdx/dpdy(xz) once at fs_main top and rotate
into the family frame in check_family — same math, uniform flow.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The batched path is the sole hatch renderer and has no family cap, yet it
was named hatch_batched_gpu while the old capped per-primitive renderer
kept the hatch_gpu / MAX_FAMILIES name — which reads as if visible hatch
fills are truncated at 16 families (they are not). Swap the names so the
active renderer is hatch_gpu.rs / hatch.wgsl and the legacy one, now used
only for wipeout masks, is wipeout_gpu.rs / wipeout.wgsl. MAX_FAMILIES now
lives only in the wipeout module. Pure rename; no behaviour change.
Refs #323.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>