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

@ -132,6 +132,18 @@ handles; the render path re-tessellates only those, reusing the rest.
Also useful on open: any partial cache (e.g. from block defns) can be
re-used.
**Partial (landed): block cache survives non-block edits.** The
block-definition tessellation cache (`BlockCache::build`, every block defn)
was keyed on `geometry_epoch`, so *any* edit rebuilt all block defns — a
~30 ms baseline wire re-tess turned into a ~400 ms spike on block-heavy
drawings at line-commit / grip-start / grip-release. The cache now keys on a
separate `block_epoch`. `bump_geometry` bumps both (safe default); the
operations that provably can't change a block defn — adding a top-level
entity (`add_entity` for non-Insert/Block) and grip-moving an entity/insert —
call `bump_geometry_no_blocks`, which re-tessellates only the visible wires
(block cache reused). Block-content edits (REFCLOSE, block create, xref,
explode) still go through full `bump_geometry`.
**Partial (landed): grip drag.** Dragging an entity's grip called
`scene.apply_grip` every move, which `bump_geometry`'d → a full model
re-tessellation per move (plus an O(N) clone of all wires for snapping). Now