perf(scene): keep block cache across non-block edits

The block-definition tessellation cache (BlockCache::build — every block
defn) was keyed on geometry_epoch, so every edit rebuilt all block defns.
On a block-heavy drawing that turned the ~30ms visible-wire re-tess into a
~400ms spike at line-commit, grip first-move and grip release.

Key the block cache on a separate block_epoch. bump_geometry bumps both
geometry_epoch and block_epoch (safe default — xref, block create,
explode, REFCLOSE, undo, open all keep rebuilding blocks). Operations that
provably can't change a block definition now call bump_geometry_no_blocks,
which bumps only geometry_epoch so the visible wires re-tessellate against
the reused block cache:

- add_entity for a non-Insert/Block entity (line, arc, text, hatch, …) —
  scene.add_entity only ever routes to model/paper space, never block
  content;
- grip drag hide / commit / Esc-cancel — moving a top-level entity or an
  insert never edits a block defn.

Line-commit and grip start/end drop from ~400ms to the ~30ms baseline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-06-05 15:24:02 +03:00
commit 3b2f8431a3
3 changed files with 51 additions and 6 deletions

View file

@ -1325,7 +1325,7 @@ impl OpenCADStudio {
}
self.tabs[i].scene.hidden.remove(&h);
self.tabs[i].scene.clear_preview_wire();
self.tabs[i].scene.bump_geometry();
self.tabs[i].scene.bump_geometry_no_blocks();
self.refresh_selected_grips();
}
self.tabs[self.active_tab].snap_result = None;
@ -2127,7 +2127,9 @@ impl OpenCADStudio {
self.grip_original =
self.tabs[i].scene.document.get_entity(grip.handle).cloned();
self.tabs[i].scene.hidden.insert(grip.handle);
self.tabs[i].scene.bump_geometry();
// Grip drag never changes a block definition — keep the
// block cache so the hide doesn't re-tessellate blocks.
self.tabs[i].scene.bump_geometry_no_blocks();
self.grip_preview_handle = Some(grip.handle);
}
@ -2650,7 +2652,7 @@ impl OpenCADStudio {
self.grip_original = None;
self.tabs[i].scene.hidden.remove(&h);
self.tabs[i].scene.clear_preview_wire();
self.tabs[i].scene.bump_geometry();
self.tabs[i].scene.bump_geometry_no_blocks();
}
// Placement confirmed — keep the just-added leader.
self.grip_add_provisional = None;