perf: eliminate per-frame re-tessellation and GPU re-upload in large files

Add two complementary render caches that make mouse navigation essentially
free regardless of file size:

Option A – Wire tessellation cache: entity_wires_arc() caches the
tessellated Vec<WireModel> keyed by geometry_epoch and returns an Arc,
so navigation frames (no preview wires) incur zero Vec clones and zero
tessellation work. Primitive::wires changed to Arc<Vec<WireModel>>.

Option B – GPU buffer cache: Pipeline stores cached_epoch; prepare()
skips all upload_wires/hatches/images/meshes calls when the epoch is
unchanged, writing only the 192-byte camera uniform instead.

geometry_epoch is a process-wide AtomicU64 incremented by every Scene
mutation (add/erase entity, selection, layer visibility, layout switch,
populate_*, grip, transform, copy, clear, preview wires). Using a global
counter rather than a per-scene counter prevents two tabs that happen to
have the same local bump count from sharing a cached_epoch value, which
was causing the first file's geometry to remain on screen after opening a
second file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-04-26 02:13:31 +03:00
commit d1ecaf4695
5 changed files with 225 additions and 20 deletions

View file

@ -52,6 +52,9 @@ pub struct Pipeline {
gpu_images: Vec<ImageGpu>,
gpu_meshes: Vec<MeshGpu>,
pub viewcube: ViewCubePipeline,
/// Last geometry epoch for which GPU buffers were uploaded.
/// Initialized to u64::MAX so the first frame always uploads.
pub cached_epoch: u64,
}
impl Pipeline {
@ -476,6 +479,7 @@ impl Pipeline {
gpu_images: vec![],
gpu_meshes: vec![],
viewcube,
cached_epoch: u64::MAX,
}
}