forked from 1b-agent/opencadstudio-upstream
perf(scene): memoize Face3D wire split across frames
build_primitive ran split_face3d_wires every frame — for each wire a string-parse + document handle lookup + clone, to separate Face3D wires from the rest — even when a pan had just reused the tessellation. The split is content-only, so memoize it by the Model-tile wire content id: a pan-reuse (same id) reuses the split, and the common no-overlay frame returns the cached `other` Arc with zero clone. Preview/interim frames clone once to append. Non-tile paths split inline as before. Completes the cheap-pan path: a pure pan now reuses the CPU tessellation, the Face3D split, and the GPU wire upload — only the view_proj uniform and per-frame scissors recompute. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
0f40cfa054
commit
65eba3887a
3 files changed
+43
-5
No files matched your search
@@ -176,6 +176,13 @@ outright — no re-tessellation, `tess ms` drops to ~0 on the PERF HUD. Zoom
|
||||
exactly as before; the cull margin is unchanged, so miss cost is identical
|
||||
(no zoom regression).
|
||||
|
||||
**Partial (landed): per-frame split.** `build_primitive` ran
|
||||
`split_face3d_wires` every frame — an O(N) per-wire handle lookup + clone to
|
||||
separate Face3D wires — even on a pan that reused the tessellation. It's now
|
||||
memoized by the tile's wire content id, and the non-overlay frame reuses the
|
||||
`other` Arc with no clone at all. So a pan reuses tessellation, the split, and
|
||||
the GPU upload; only the uniform + scissors update per frame.
|
||||
|
||||
**Partial (landed): hover.** `set_hover_highlight` no longer bumps the geometry
|
||||
epoch (a full re-tessellation) when the hovered entity is already selected —
|
||||
the effective highlight set `selected ∪ {hover}` is then unchanged, so the
|
||||
|
||||
@@ -752,6 +752,13 @@ pub struct Scene {
|
||||
/// never collide with a real [`WIRE_CONTENT_GEN`] id; incremented every
|
||||
/// use so the GPU always sees a fresh id and re-uploads.
|
||||
pub(crate) wire_force_nonce: std::cell::Cell<u64>,
|
||||
/// Memoized `(face3d, other)` split of the Model-tile wire set, keyed by
|
||||
/// its [`WIRE_CONTENT_GEN`] id. `split_face3d_wires` is an O(N) per-wire
|
||||
/// handle lookup + clone that otherwise re-runs every frame; a pan that
|
||||
/// reuses the tessellation (same id) reuses this split too.
|
||||
#[allow(clippy::type_complexity)]
|
||||
split_cache:
|
||||
RefCell<Option<(u64, Arc<Vec<WireModel>>, Arc<Vec<WireModel>>)>>,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
@@ -813,6 +820,7 @@ impl Scene {
|
||||
last_tess_wires: std::cell::Cell::new(0),
|
||||
last_model_wire_gen: std::cell::Cell::new(0),
|
||||
wire_force_nonce: std::cell::Cell::new(0),
|
||||
split_cache: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+28
-5
@@ -633,11 +633,34 @@ impl Scene {
|
||||
n | (1u64 << 63)
|
||||
}
|
||||
};
|
||||
let (face3d_wires, other_wires) = split_face3d_wires(&base_arc, &self.document);
|
||||
let all_wires = if self.interim_wire.is_none() && self.preview_wires.is_empty() {
|
||||
Arc::new(other_wires)
|
||||
// Split Face3D wires from the rest. The split is content-only (keyed
|
||||
// by the tile's wire id), so on a pan-reuse it's memoized rather than
|
||||
// re-walking every wire (handle lookup + clone) each frame. Non-tile
|
||||
// paths have no stable id and split inline as before.
|
||||
let (face3d_wires, other_arc) = match tile_wire_gen {
|
||||
Some(gen) => {
|
||||
let cached = {
|
||||
let c = self.split_cache.borrow();
|
||||
c.as_ref().filter(|(g, ..)| *g == gen).map(|(_, f, o)| (f.clone(), o.clone()))
|
||||
};
|
||||
cached.unwrap_or_else(|| {
|
||||
let (f, o) = split_face3d_wires(&base_arc, &self.document);
|
||||
let (fa, oa) = (Arc::new(f), Arc::new(o));
|
||||
*self.split_cache.borrow_mut() = Some((gen, fa.clone(), oa.clone()));
|
||||
(fa, oa)
|
||||
})
|
||||
}
|
||||
None => {
|
||||
let (f, o) = split_face3d_wires(&base_arc, &self.document);
|
||||
(Arc::new(f), Arc::new(o))
|
||||
}
|
||||
};
|
||||
// Reuse the cached `other` Arc directly when no live preview/interim
|
||||
// wires need appending; otherwise clone once to extend.
|
||||
let all_wires = if !has_overlay {
|
||||
other_arc
|
||||
} else {
|
||||
let mut v = other_wires;
|
||||
let mut v = (*other_arc).clone();
|
||||
if let Some(iw) = &self.interim_wire {
|
||||
v.push(iw.clone());
|
||||
}
|
||||
@@ -705,7 +728,7 @@ impl Scene {
|
||||
|
||||
Some(ViewportData {
|
||||
wires: all_wires,
|
||||
face3d_wires: Arc::new(face3d_wires),
|
||||
face3d_wires,
|
||||
draw_depths: self.draw_depth_map(),
|
||||
hatches,
|
||||
wipeout_hatches,
|
||||
|
||||
Reference in new issue
Block a user