perf(scene): decouple selection from wire tessellation

Picking an entity called bump_geometry, which invalidated the wire cache
and re-tessellated the entire model just to repaint one entity — 30ms to
400ms on a large drawing (visible as a tess-ms spike on the PERF HUD).

Selection highlight is no longer baked into tessellation. wires_for_block_
culled now feeds tessellate_entity an empty selection set, so wires are
always base-coloured and the wire cache is selection-independent. The
highlight is applied in the GPU xray overlay: prepare filters the cached
wires by the live `selected ∪ hover` handle set, recolours them to
WireModel::SELECTED, and batches them — rebuilt only when a new
selection_generation (or the underlying wire content) changes.

Pure select / deselect / hover paths now call bump_selection instead of
bump_geometry, so a pick refreshes just the overlay — no re-tessellation,
no main wire re-upload (geometry_epoch and wire_content_id are unchanged).
Geometry edits, undo and file-open still bump_geometry and rebuild
everything. Grips/properties refresh through their existing app paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-06-05 10:04:22 +03:00
commit ff9f49511b
4 changed files with 140 additions and 34 deletions

View file

@ -176,6 +176,17 @@ outright — no re-tessellation, `tess ms` drops to ~0 on the PERF HUD. Zoom
exactly as before; the cull margin is unchanged, so miss cost is identical
(no zoom regression).
**Partial (landed): selection decoupled.** Picking an entity used to call
`bump_geometry`, invalidating the wire cache and re-tessellating the WHOLE
model just to repaint one entity (30 ms → 400 ms on a large drawing). The
highlight is no longer baked into tessellation: wires are always base-coloured
(`sel` is empty in `wires_for_block_culled`), and the selection highlight is
applied in the GPU xray overlay from the live `selected ∪ hover` set,
recoloured to `WireModel::SELECTED`. Selection / hover now bump a cheap
`selection_generation` instead of `geometry_epoch`, so the overlay refreshes
without any re-tessellation or main-buffer re-upload. `tess ms` stays flat
when selecting.
**Partial (landed): per-frame split.** `build_primitive` ran
`split_face3d_wires` every frame — an O(N) per-wire handle lookup + clone to
separate Face3D wires — even on a pan that reused the tessellation. It's now