forked from 1b-agent/opencadstudio-upstream
perf(scene): batch the wire pass into per-run draw calls
Phase 3.3. upload_wires built one GPU instance buffer + one draw call per WireModel — tens of thousands on a large drawing, a major per-frame CPU / driver cost. Merge maximal runs of consecutive wires that share scissor + mesh-edge state into one concatenated instance buffer each (WireGpu:: from_run); the existing draw loop then issues a single draw per run. A 2D model (uniform None scissor, no mesh edges) collapses to one buffer + one draw. Runs are consecutive, never globally regrouped, so the sorted wire order is preserved exactly — depth-biased overlap and alpha blending both resolve in submission order, so the image is unchanged. from_run guarantees instance order (index-ordered collect + sequential flatten). The WireInstance layout, shader, scissor projection and draw loop are untouched; only buffer packing changed. Selection overlay stays per-wire. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
65eba3887a
commit
c6e17deb7e
3 files changed
+83
-6
No files matched your search
@@ -284,6 +284,50 @@ impl WireGpu {
|
||||
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
|
||||
/// the shared `scissor` / `mesh_edge` onto each so the draw loop treats the
|
||||
/// whole run as a single batch.
|
||||
///
|
||||
/// Unlike [`from_batch`], instance order is **guaranteed** to follow wire
|
||||
/// order (parallel `collect` is index-ordered; the flatten is sequential).
|
||||
/// The main wire pass depends on that — depth-biased overlap *and* alpha
|
||||
/// blending both resolve in submission order, so a reorder would change the
|
||||
/// image for transparent / coincident wires.
|
||||
pub fn from_run(
|
||||
device: &wgpu::Device,
|
||||
wires: &[WireModel],
|
||||
depth_map: &rustc_hash::FxHashMap<u64, f32>,
|
||||
scissor: Option<[f32; 4]>,
|
||||
mesh_edge: bool,
|
||||
) -> Vec<Self> {
|
||||
use rayon::prelude::*;
|
||||
const MAX_INSTANCES: usize = 268_435_456 / std::mem::size_of::<WireInstance>();
|
||||
let per: Vec<Vec<WireInstance>> = wires
|
||||
.par_iter()
|
||||
.map(|w| emit_wire_instances(w, w.color, wire_draw_depth(w, depth_map)))
|
||||
.collect();
|
||||
let mut instances: Vec<WireInstance> = Vec::with_capacity(per.iter().map(Vec::len).sum());
|
||||
for mut v in per {
|
||||
instances.append(&mut v);
|
||||
}
|
||||
if instances.is_empty() {
|
||||
return vec![];
|
||||
}
|
||||
instances
|
||||
.chunks(MAX_INSTANCES)
|
||||
.map(|chunk| {
|
||||
let buf = instance_buffer_mapped(device, "wire.run.ibuf", chunk);
|
||||
Self {
|
||||
instance_buffer: buf,
|
||||
instance_count: chunk.len() as u32,
|
||||
vp_scissor: scissor,
|
||||
is_3d_mesh_edge: mesh_edge,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Merge multiple WireModels into GPU instance buffers, chunked to fit the
|
||||
/// 256 MB GPU limit. Each wire keeps its own color and pattern — they live
|
||||
/// per-instance.
|
||||
|
||||
Reference in new issue
Block a user