From 14a981dc878899ab8b90286a7d58252273f9defc Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Sat, 30 May 2026 02:02:43 +0300 Subject: [PATCH] perf(wire): collapse sub-pixel dash patterns to solid line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the smallest dash / gap element of a linetype pattern would project below one pixel on screen, the gaps either shimmer as aliasing or vanish entirely — the user reads the line as solid anyway. Compute the min non-zero element of `pat0 / pat1` once in the vertex shader, flat- interpolate it across the instance, and let the fragment stage skip the arc-length dash test (and the `discard` for gap fragments) when `min_elem >= u.world_per_pixel` fails. Mirrors the existing hatch-LOD branch in `hatch.wgsl`; primary win is visual quality at extreme zoom-out, with a small fragment-shader op saving as a side effect. Co-Authored-By: Claude Opus 4.7 --- src/shaders/wire.wgsl | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/shaders/wire.wgsl b/src/shaders/wire.wgsl index 277b5e436..84e7b6482 100644 --- a/src/shaders/wire.wgsl +++ b/src/shaders/wire.wgsl @@ -38,12 +38,18 @@ struct InstanceIn { } struct VertexOut { - @builtin(position) clip_pos: vec4, - @location(0) color: vec4, - @location(1) distance: f32, - @location(2) pattern_length: f32, - @location(3) pat0: vec4, - @location(4) pat1: vec4, + @builtin(position) clip_pos: vec4, + @location(0) color: vec4, + @location(1) distance: f32, + @location(2) pattern_length: f32, + @location(3) pat0: vec4, + @location(4) pat1: vec4, + // World length of the smallest non-zero dash / gap element of this + // instance. Flat-interpolated (constant per instance) so the + // fragment stage can short-circuit the dash test when every gap + // projects below one pixel on screen. See the LOD branch in + // `fs_main`. + @location(5) @interpolate(flat) min_elem: f32, } @vertex fn vs_main(@builtin(vertex_index) vid: u32, in: InstanceIn) -> VertexOut { @@ -90,6 +96,19 @@ struct VertexOut { let ndc_offset = perp_ndc * hw * side; let final_clip = clip_pos + vec4(ndc_offset * clip_pos.w, 0.0, 0.0); + // Smallest non-zero dash / gap element, in world units. Used by + // the fragment stage to decide when the pattern's finest feature + // would render below one pixel and should collapse to a solid line. + var min_elem: f32 = in.pattern_length; + let elems = array( + in.pat0.x, in.pat0.y, in.pat0.z, in.pat0.w, + in.pat1.x, in.pat1.y, in.pat1.z, in.pat1.w, + ); + for (var i = 0u; i < 8u; i++) { + let e = abs(elems[i]); + if e > 0.0 && e < min_elem { min_elem = e; } + } + var out: VertexOut; out.clip_pos = final_clip; out.color = in.color; @@ -97,6 +116,7 @@ struct VertexOut { out.pattern_length = in.pattern_length; out.pat0 = in.pat0; out.pat1 = in.pat1; + out.min_elem = min_elem; return out; } @@ -117,8 +137,15 @@ fn in_dash(dist: f32, pat_len: f32, p0: vec4, p1: vec4) -> bool { @fragment fn fs_main(in: VertexOut) -> @location(0) vec4 { if in.pattern_length > 0.0 { - if !in_dash(in.distance, in.pattern_length, in.pat0, in.pat1) { - discard; + // LOD: once the pattern's smallest feature drops below ~1 px + // on screen, dash gaps alias / shimmer (or vanish completely) + // and the user reads the line as solid anyway. Skip the dash + // test and return solid colour — also saves the per-fragment + // arc-length math + `discard`. + if in.min_elem >= u.world_per_pixel { + if !in_dash(in.distance, in.pattern_length, in.pat0, in.pat1) { + discard; + } } } return in.color;