Share source buffers across block placements and render nested content with GPU instance transforms.\n\nChunk silhouettes and compact mesh material data so large drawings stay under device buffer limits and remain responsive.
The wipeout pass ran after the wire pass with no draw-order bias, so
the mask erased its own boundary frame (and everything else at its
plane regardless of draw order). The mask uniform now carries the
entity's draw-order depth (uniform grows 80 → 96 bytes) and biases its
clip-z like every other pipeline, and the frame wire rides a
+half-rank override — the later mask pass fails the depth test at the
frame's pixels, so the boundary stays visible, while entities drawn
above the wipeout are no longer erased.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
Rectangular paper viewports now go through the same GPU stencil clip as
non-rectangular ones: viewport_clip_boundary_ndc returns the four rect
corners (a full-target mask) instead of nothing, so the render path has
no rect/non-rect special case — every content viewport carries a clip
boundary and draws with stencil reference 0xFF.
With a single clip path, the old per-wire / -image / -wipeout scissor
mechanism is fully dead and removed: compute_*_scissors, the
*_pixel_scissors buffers, the project_scissor helper, the per-pass
set_scissor_rect blocks, the vp_scissor field threaded through
WireModel / ImageModel / HatchModel and their GPU mirrors (WireGpu /
ImageGpu / WipeoutGpu / HatchWebGpu), from_run's scissor parameter, the
scissor grouping in build_wire_buffers, and the is_arena_eligible gate.
Net -245 lines across 54 files.
Rectangular viewports still clip exactly via their own render rectangle;
the stencil mask is a redundant full-target no-op there, kept only so
the clip code path stays uniform. XCLIP keeps its CPU wire clip.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A gradient hatch was drawn linear from the entity colour to its second
stop, so a red->yellow spherical fill came out as a pale linear wash. Two
fixes:
- Colour: start from the gradient's first stop, not the entity colour.
hatch_model_from_dxf already baked stop-0 in; stop synced_hatch_models
from clobbering it with the entity's resolved colour for gradient fills.
- Shape: spherical / hemispherical / curved gradient names now render as a
radial fill (new shader mode 3, centred on the boundary with radius to the
farthest vertex) instead of linear. Native and web shaders both updated.
Co-Authored-By: Claude Opus 4.8 <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>