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:
parent
65eba3887a
commit
c6e17deb7e
3 changed files with 83 additions and 6 deletions
|
|
@ -907,13 +907,35 @@ impl Pipeline {
|
|||
.and_then(|h| depth_map.get(&h).copied())
|
||||
.unwrap_or(0.0)
|
||||
};
|
||||
self.gpu_wires = wires
|
||||
.iter()
|
||||
.map(|w| WireGpu::new(device, w, depth_of(w)))
|
||||
.collect();
|
||||
// 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
|
||||
// concatenated instance buffer each. The draw loop then issues one draw
|
||||
// per run. Runs must be consecutive (not regrouped) so the original
|
||||
// wire order — already sorted by draw order — is preserved; depth bias
|
||||
// and alpha blending both depend on it. Scissor and mesh-edge stay
|
||||
// grouping keys because the draw loop sets one scissor per batch and
|
||||
// skips whole mesh-edge batches in shaded modes.
|
||||
let mut batches: Vec<WireGpu> = Vec::new();
|
||||
let mut i = 0;
|
||||
while i < wires.len() {
|
||||
let scissor = wires[i].vp_scissor;
|
||||
let mesh_edge = !wires[i].fill_tris.is_empty();
|
||||
let mut j = i + 1;
|
||||
while j < wires.len()
|
||||
&& wires[j].vp_scissor == scissor
|
||||
&& (!wires[j].fill_tris.is_empty()) == mesh_edge
|
||||
{
|
||||
j += 1;
|
||||
}
|
||||
batches.extend(WireGpu::from_run(device, &wires[i..j], depth_map, scissor, mesh_edge));
|
||||
i = j;
|
||||
}
|
||||
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.
|
||||
// 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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue