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 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-06-05 09:23:52 +03:00
commit 65eba3887a
3 changed files with 43 additions and 5 deletions

View file

@ -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,