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

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