m4b_gpu.py moves the per-draw rasterization to a GPU compute shader (nearest-z
winner per pixel) with the verified M5 texel decode as a vectorized numpy
post-pass. Output matches the M5-verified CPU render (frame_*.png) bit-for-bit
across all 12 frames (differ>24 = 0.000%), at 1.4s/12 frames vs 69.9s (~50x;
the firmware's 3.7s now dominates -- the render keeps up with real-time).
Reaching bit-identity took finding two real bugs (honest trail in M4B-RESULTS.md):
1. 4-edge clip: the shader tested only edges[:4] while the CPU clips with ALL
edges (up to 6); 5-6-edge polys bled past their boundary and overwrote
neighbours -- the 18% region divergence on the receding walls/floor. Fixed
with 6 edge slots.
2. float32 planes: A*x+B*y+C in float32 flipped the z-test winner vs the CPU's
float64 at contested depths. Fixed with fp64 in the shader.
exp_precision.py is the diagnostic that REJECTED int-truncation as a reconciler
(it worsens float32/64 sensitivity to ~60%; true fixed-point width is a separate
spec item). Verified honestly by measuring, not eyeballing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>