From e9488f99dd3018521860c58fedfc322ab8c7998e Mon Sep 17 00:00:00 2001 From: Hakan Date: Sat, 23 May 2026 09:45:24 +0300 Subject: [PATCH] perf(wire): move LWDISPLAY toggle from CPU rebake to wire shader uniform LWDISPLAY (Show Lineweight) flipping the toggle no longer triggers a full retessellate. `render_style_for` always bakes the entity's resolved (layer- inherited) lineweight, and the wire shader reads `Uniforms.lwdisplay_enable` to either honour the per-vertex half_width or collapse the quad to a 1 px line. Side effect: PDF export now uses the real lineweight regardless of LWDISPLAY, matching AutoCAD's plot semantics (LWDISPLAY only affects screen render). Co-Authored-By: Claude Opus 4.7 --- src/app/commands.rs | 2 +- src/app/update.rs | 2 +- src/scene/pipeline/uniforms.rs | 8 ++++--- src/scene/render.rs | 43 ++++++++++++++++------------------ src/shaders/wire.wgsl | 16 +++++++++---- 5 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/app/commands.rs b/src/app/commands.rs index ed347b5ee..41e3e1b27 100644 --- a/src/app/commands.rs +++ b/src/app/commands.rs @@ -4522,7 +4522,7 @@ impl H7CAD { Ok(Some(v)) => { self.push_undo_snapshot(i, "LWDISPLAY"); self.tabs[i].scene.document.header.lineweight_display = v; - self.tabs[i].scene.bump_geometry(); + // No retessellate — the wire shader honours the flag via uniforms. self.tabs[i].dirty = true; self.command_line.push_output(&format!( "LWDISPLAY {}", diff --git a/src/app/update.rs b/src/app/update.rs index 229b3d79e..50407af07 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -2514,7 +2514,7 @@ impl H7CAD { if i < self.tabs.len() { let h = &mut self.tabs[i].scene.document.header; h.lineweight_display = !h.lineweight_display; - self.tabs[i].scene.bump_geometry(); + // No retessellate — the wire shader reads the flag from uniforms. self.tabs[i].dirty = true; } Task::none() diff --git a/src/scene/pipeline/uniforms.rs b/src/scene/pipeline/uniforms.rs index 5b4a0bb69..3f6f1b577 100644 --- a/src/scene/pipeline/uniforms.rs +++ b/src/scene/pipeline/uniforms.rs @@ -11,11 +11,13 @@ pub struct Uniforms { /// hatch shader to substitute solid fill when pattern line spacing /// falls below ~2 px (Phase 3.3 LOD). pub world_per_pixel: f32, - pub _pad: f32, + /// LWDISPLAY toggle (1.0 = show lineweights, 0.0 = force 1 px). + /// Read by the wire shader so the toggle does not require a retessellate. + pub lwdisplay_enable: f32, } impl Uniforms { - pub fn new(camera: &Camera, bounds: Rectangle) -> Self { + pub fn new(camera: &Camera, bounds: Rectangle, lwdisplay_enable: bool) -> Self { let half_h = camera.ortho_size(); let world_per_pixel = if bounds.height > 0.0 { (2.0 * half_h) / bounds.height @@ -27,7 +29,7 @@ impl Uniforms { camera_pos: camera.position_vec4(), viewport_size: [bounds.width, bounds.height], world_per_pixel, - _pad: 0.0, + lwdisplay_enable: if lwdisplay_enable { 1.0 } else { 0.0 }, } } } diff --git a/src/scene/render.rs b/src/scene/render.rs index 2673fe793..a76a6ff7c 100644 --- a/src/scene/render.rs +++ b/src/scene/render.rs @@ -253,27 +253,24 @@ pub(super) fn render_style_for( let (pattern_length, pattern) = resolve_pattern(&document.line_types, lt_name, lt_scale); let line_weight_px = { - // LWDISPLAY (header.lineweight_display): when false, every entity - // draws at the 1-pixel base width regardless of its assigned weight. - // AutoCAD's "Show Lineweight" toggle maps onto this header var. - if !document.header.lineweight_display { - 1.0 - } else { - let ew = &e.common().line_weight; - let resolved = match ew { - LineWeight::ByLayer | LineWeight::ByBlock | LineWeight::Default => document - .layers - .get(layer_name) - .map(|l| &l.line_weight) - .unwrap_or(&LineWeight::Default), - _ => ew, - }; - const MM_TO_PX: f32 = 96.0 / 25.4; - resolved - .millimeters() - .map(|mm| (mm as f32 * MM_TO_PX).max(1.0)) - .unwrap_or(1.0) - } + // LWDISPLAY is no longer evaluated here — the toggle is now applied in + // the wire shader via `Uniforms.lwdisplay_enable`, so we always bake the + // entity's resolved (layer-inherited) weight. Toggling lineweight + // visibility costs only a uniform write, not a retessellate. + let ew = &e.common().line_weight; + let resolved = match ew { + LineWeight::ByLayer | LineWeight::ByBlock | LineWeight::Default => document + .layers + .get(layer_name) + .map(|l| &l.line_weight) + .unwrap_or(&LineWeight::Default), + _ => ew, + }; + const MM_TO_PX: f32 = 96.0 / 25.4; + resolved + .millimeters() + .map(|mm| (mm as f32 * MM_TO_PX).max(1.0)) + .unwrap_or(1.0) }; (entity_color, pattern_length, pattern, line_weight_px, aci) @@ -376,7 +373,7 @@ impl Scene { wipeout_hatches: self.wipeout_models_arc(), images: self.images_arc(), meshes: self.meshes_arc(), - uniforms: Uniforms::new(&cam, bounds), + uniforms: Uniforms::new(&cam, bounds, self.document.header.lineweight_display), cam_rotation: cam.view_rotation_mat(), hover_region, bg_color, @@ -420,7 +417,7 @@ impl Scene { wipeout_hatches: self.wipeout_models_arc(), images: self.images_arc(), meshes: self.meshes_arc(), - uniforms: Uniforms::new(&cam, bounds), + uniforms: Uniforms::new(&cam, bounds, self.document.header.lineweight_display), cam_rotation: cam.view_rotation_mat(), hover_region, bg_color: self.bg_color, diff --git a/src/shaders/wire.wgsl b/src/shaders/wire.wgsl index 9d420997a..d5d29fa17 100644 --- a/src/shaders/wire.wgsl +++ b/src/shaders/wire.wgsl @@ -11,10 +11,13 @@ // • pat0/pat1 encode up to 8 elements: positive=dash, negative=gap. struct Uniforms { - view_proj: mat4x4, - camera_pos: vec4, - viewport_size: vec2, - _pad: vec2, + view_proj: mat4x4, + camera_pos: vec4, + viewport_size: vec2, + world_per_pixel: f32, + // LWDISPLAY toggle: 0.0 = force 1 px (half_width 0.5), 1.0 = use per-vertex + // baked half_width. Lets the LWT button switch without retessellating. + lwdisplay_enable: f32, } @group(0) @binding(0) var u: Uniforms; @@ -70,8 +73,11 @@ struct VertexOut { // Select the clip-space position for this vertex's endpoint. let clip_pos = mix(clip_a, clip_b, in.which_end); + // LWDISPLAY off → collapse to a 1-pixel-wide line (half_width = 0.5). + let hw = select(0.5, in.half_width, u.lwdisplay_enable > 0.5); + // Offset in clip space (multiply by w to un-apply perspective division). - let ndc_offset = perp_ndc * in.half_width * in.side; + let ndc_offset = perp_ndc * hw * in.side; let final_clip = clip_pos + vec4(ndc_offset * clip_pos.w, 0.0, 0.0); var out: VertexOut;