perf(statusbar): cache layout/scale derived data behind epochs (major)

Status bar rescanned the whole object map twice per frame (layout_names +
scale_picker_list), deep-cloned an already-cached Arc, and built two
undo/redo label vecs consumed ~never. Cache layout names (epoch key) and
scale picker (epoch + unit-factor + imperial-family + current-scale key)
as Arc behind choke-point invalidation (layout/scale impl methods,
undo/redo swaps, file-open, doc-reset); borrow across the widget boundary
(owned-Arc params; E0515 forbids &'a slices over call-site temporaries);
build undo/redo labels only when their dropdown is open.

Tests:
- cached_layout_names_returns_same_arc_until_bump: hit-path Arc identity
- cached_layout_names_invalidated_by_add_layout: bump yields new Arc + names
- cached_scale_picker_returns_same_arc_until_bump: hit-path Arc identity
- cached_scale_picker_invalidated_by_add_scale: bump yields new Arc + scale
- cached_scale_picker_invalidated_by_current_scale_change: key completeness

Bench (cargo bench ui_statusbar_derived_data, 3k entities): cached hit below
timer granularity vs ~1us uncached scans, PASS (50us threshold).
This commit is contained in:
Karim Jerbi committed 2026-09-21 22:31:16 +01:00
1 parent baba5bccd3
commit dcc05f310d
16 files changed
+327 -43

No files matched your search

+27
View File
@@ -850,6 +850,8 @@ impl OpenCADStudio {
.swap_structure(std::mem::replace(stored, acadrust::CadDocument::new()));
*stored = inverse;
self.tabs[i].scene.invalidate_dependency_index();
self.tabs[i].scene.bump_layout_epoch();
self.tabs[i].scene.bump_scale_epoch();
}
StructureSnapshot::Layers(entries) => {
let names: Vec<String> =
@@ -911,12 +913,31 @@ impl OpenCADStudio {
}
}
StructureSnapshot::Objects(entries) => {
let mut layout_touched = false;
let mut scale_touched = false;
for entry in entries {
let value = if undo {
entry.before.clone()
} else {
entry.after.clone()
};
// Either direction can add/remove a layout: undoing
// an insert removes it (`before` is `None`), redoing
// re-inserts it — so both sides must be inspected.
layout_touched |= matches!(
entry.before,
Some(acadrust::objects::ObjectType::Layout(_))
) || matches!(
entry.after,
Some(acadrust::objects::ObjectType::Layout(_))
);
scale_touched |= matches!(
entry.before,
Some(acadrust::objects::ObjectType::Scale(_))
) || matches!(
entry.after,
Some(acadrust::objects::ObjectType::Scale(_))
);
if let Some(object) = value {
self.tabs[i]
.scene
@@ -927,6 +948,12 @@ impl OpenCADStudio {
self.tabs[i].scene.document.objects.remove(&entry.handle);
}
}
if layout_touched {
self.tabs[i].scene.bump_layout_epoch();
}
if scale_touched {
self.tabs[i].scene.bump_scale_epoch();
}
}
StructureSnapshot::Styles {
before,