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

@ -752,6 +752,7 @@ impl H7CAD {
if let Some(dl) = self.tabs[i].scene.document.layers.get_mut(&name) {
if frozen { dl.freeze(); } else { dl.thaw(); }
}
self.tabs[i].scene.bump_geometry();
self.tabs[i].dirty = true;
}
Task::none()
@ -788,6 +789,7 @@ impl H7CAD {
let vp_info = self.tabs[i].scene.viewport_list();
let doc_layers = self.tabs[i].scene.document.layers.clone();
self.tabs[i].layers.sync_with_viewports(&doc_layers, vp_info);
self.tabs[i].scene.bump_geometry();
self.tabs[i].dirty = true;
}
}
@ -2470,6 +2472,7 @@ impl H7CAD {
// Switch to Model if active layout was deleted.
if self.tabs[i].scene.current_layout == name {
self.tabs[i].scene.current_layout = "Model".to_string();
self.tabs[i].scene.bump_geometry();
}
self.layout_manager_selected = "Model".to_string();
self.layout_manager_rename_buf = String::new();
@ -2516,6 +2519,7 @@ impl H7CAD {
let i = self.active_tab;
let name = self.layout_manager_selected.clone();
self.tabs[i].scene.current_layout = name.clone();
self.tabs[i].scene.bump_geometry();
self.command_line.push_output(&format!("Switched to layout '{name}'."));
Task::none()
}