From 0d18e0d1bfef98ed829ca8f4c3500a3fc2867bc3 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Fri, 5 Jun 2026 09:44:12 +0300 Subject: [PATCH] perf(scene): batch the selection-overlay wire pass too Follow-up to 3.3. The selection xray pass still built one buffer + one draw call per selected wire, so select-all over a large drawing issued a draw per entity. That pass applies neither scissor nor mesh-edge skip, so all selected wires merge into one order-preserving run via from_run. Removes the now-dead WireGpu::new / build per-wire constructors. Co-Authored-By: Claude Opus 4.8 --- src/scene/pipeline/mod.rs | 20 ++++++-------------- src/scene/pipeline/wire_gpu.rs | 19 ------------------- 2 files changed, 6 insertions(+), 33 deletions(-) diff --git a/src/scene/pipeline/mod.rs b/src/scene/pipeline/mod.rs index 2dfeb79a8..a8da45618 100644 --- a/src/scene/pipeline/mod.rs +++ b/src/scene/pipeline/mod.rs @@ -900,13 +900,6 @@ impl Pipeline { wires: &[WireModel], depth_map: &rustc_hash::FxHashMap, ) { - let depth_of = |w: &WireModel| -> f32 { - w.name - .parse::() - .ok() - .and_then(|h| depth_map.get(&h).copied()) - .unwrap_or(0.0) - }; // Batch the wire pass: instead of one GPU buffer + one draw call per // WireModel (tens of thousands on a large drawing), merge maximal runs // of *consecutive* wires that share scissor + mesh-edge state into one @@ -934,13 +927,12 @@ impl Pipeline { self.gpu_wires = batches; // Full-brightness copies of selected wires, drawn on top of everything - // in the selection overlay pass so they're always visible. Kept - // per-wire — the selection set is small and this overlay is separate. - self.gpu_selected_wires = wires - .iter() - .filter(|w| w.selected) - .map(|w| WireGpu::new(device, w, depth_of(w))) - .collect(); + // in the selection overlay pass so they're always visible. The xray + // pass applies neither scissor nor mesh-edge skip, so all selected + // wires batch into one run (None / false) — select-all over a large + // drawing no longer costs one draw call per entity. Order preserved. + let selected: Vec = wires.iter().filter(|w| w.selected).cloned().collect(); + self.gpu_selected_wires = WireGpu::from_run(device, &selected, depth_map, None, false); } /// Recompute pixel scissor rects for viewport-clipped wires from the current view_proj. diff --git a/src/scene/pipeline/wire_gpu.rs b/src/scene/pipeline/wire_gpu.rs index 4bdb1c79d..0aba6ba0e 100644 --- a/src/scene/pipeline/wire_gpu.rs +++ b/src/scene/pipeline/wire_gpu.rs @@ -277,12 +277,6 @@ fn wire_draw_depth(wire: &WireModel, depth_map: &rustc_hash::FxHashMap } impl WireGpu { - pub fn new(device: &wgpu::Device, wire: &WireModel, draw_depth: f32) -> Self { - let mut g = Self::build(device, wire, wire.color, draw_depth); - g.vp_scissor = wire.vp_scissor; - g.is_3d_mesh_edge = !wire.fill_tris.is_empty(); - g - } /// Merge a run of WireModels that share scissor + mesh-edge state into one /// (or, past the 256 MB GPU limit, a few) instance buffer(s), then stamp @@ -382,17 +376,4 @@ impl WireGpu { }) .collect() } - - fn build(device: &wgpu::Device, wire: &WireModel, color: [f32; 4], draw_depth: f32) -> Self { - let instances = emit_wire_instances(wire, color, draw_depth); - let label = format!("wire.ibuf.{}", wire.name); - let instance_buffer = instance_buffer_mapped(device, &label, &instances); - - Self { - instance_buffer, - instance_count: instances.len() as u32, - vp_scissor: None, - is_3d_mesh_edge: false, - } - } }