perf(scene): decouple selection from wire tessellation
Picking an entity called bump_geometry, which invalidated the wire cache and re-tessellated the entire model just to repaint one entity — 30ms to 400ms on a large drawing (visible as a tess-ms spike on the PERF HUD). Selection highlight is no longer baked into tessellation. wires_for_block_ culled now feeds tessellate_entity an empty selection set, so wires are always base-coloured and the wire cache is selection-independent. The highlight is applied in the GPU xray overlay: prepare filters the cached wires by the live `selected ∪ hover` handle set, recolours them to WireModel::SELECTED, and batches them — rebuilt only when a new selection_generation (or the underlying wire content) changes. Pure select / deselect / hover paths now call bump_selection instead of bump_geometry, so a pick refreshes just the overlay — no re-tessellation, no main wire re-upload (geometry_epoch and wire_content_id are unchanged). Geometry edits, undo and file-open still bump_geometry and rebuild everything. Grips/properties refresh through their existing app paths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
0d18e0d1bf
commit
ff9f49511b
4 changed files with 140 additions and 34 deletions
|
|
@ -122,6 +122,11 @@ pub struct Pipeline {
|
|||
/// Model-tile tessellation) and `upload_wires` is skipped. `u64::MAX` =
|
||||
/// nothing uploaded yet.
|
||||
pub cached_wire_id: u64,
|
||||
/// `(wire_content_id, selection_generation)` the selection xray overlay
|
||||
/// (`gpu_selected_wires`) was last built for. Rebuilt when either changes —
|
||||
/// a pick bumps only `selection_generation`, refreshing the overlay without
|
||||
/// touching the main wire buffers.
|
||||
pub cached_selection: (u64, u64),
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
|
|
@ -891,6 +896,7 @@ impl Pipeline {
|
|||
viewcube,
|
||||
cached_epoch: (u64::MAX, u64::MAX),
|
||||
cached_wire_id: u64::MAX,
|
||||
cached_selection: (u64::MAX, u64::MAX),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -925,13 +931,46 @@ impl Pipeline {
|
|||
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. 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<WireModel> = wires.iter().filter(|w| w.selected).cloned().collect();
|
||||
/// Build the selection xray overlay: full-brightness copies of the wires
|
||||
/// whose entity handle is in `highlight`, drawn on top so the selection is
|
||||
/// always visible. Selection is no longer baked into the wire tessellation,
|
||||
/// so this is driven by the live highlight set and rebuilt only when the
|
||||
/// selection (or the underlying wire content) changes — picking an entity
|
||||
/// refreshes just this overlay instead of re-tessellating the model. The
|
||||
/// xray pass applies neither scissor nor mesh-edge skip, so everything
|
||||
/// merges into one order-preserving run.
|
||||
pub fn upload_selected_wires(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
wires: &[WireModel],
|
||||
highlight: &rustc_hash::FxHashSet<acadrust::Handle>,
|
||||
depth_map: &rustc_hash::FxHashMap<u64, f32>,
|
||||
) {
|
||||
if highlight.is_empty() {
|
||||
self.gpu_selected_wires = vec![];
|
||||
return;
|
||||
}
|
||||
// Recolor to the selection highlight: the xray pass uses the normal
|
||||
// wire shader (no forced colour), so the highlight now lives here
|
||||
// instead of being baked into the tessellation. Drawn on top with
|
||||
// depth-compare Always, so it overrides the base-coloured main pass.
|
||||
let selected: Vec<WireModel> = wires
|
||||
.iter()
|
||||
.filter(|w| {
|
||||
w.name
|
||||
.parse::<u64>()
|
||||
.ok()
|
||||
.map(acadrust::Handle::new)
|
||||
.is_some_and(|h| highlight.contains(&h))
|
||||
})
|
||||
.map(|w| {
|
||||
let mut c = w.clone();
|
||||
c.color = WireModel::SELECTED;
|
||||
c
|
||||
})
|
||||
.collect();
|
||||
self.gpu_selected_wires = WireGpu::from_run(device, &selected, depth_map, None, false);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue