From ccb9bb832db96ca0c21cc779607e25cbdf626d5e Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Fri, 5 Jun 2026 10:21:32 +0300 Subject: [PATCH] perf(scene): stop re-tessellating on preview/interim overlay updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 2.2 (partial). set_preview_wires / clear_preview_wire / set_interim_wire called bump_geometry, so every rubber-band frame of a drawing command invalidated the wire cache and re-tessellated the whole model — the same 30ms->400ms spike as selection, but while drawing. Preview / interim wires are an overlay appended to the cached base wire set in build_primitive; they are not part of the tessellation cache, so they no longer bump geometry. The base model-tile tessellation stays a cache hit (static camera, no geometry bump), and the overlay still forces a GPU wire re-upload through the has_overlay content-id path. Real commits (commit_entity / transform / copy) bump geometry on their own, so finished edits and preview-clear still render correctly. Snap/hit-test via entity_wires_arc also stops re-tessellating per preview frame. Co-Authored-By: Claude Opus 4.8 --- ROADMAP.md | 10 ++++++++++ src/scene/mod.rs | 14 +++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 9441d8a5e..48f8923cb 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -132,6 +132,16 @@ 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): preview / interim overlays.** `set_preview_wires`, +`clear_preview_wire` and `set_interim_wire` used to `bump_geometry`, so every +rubber-band frame of a drawing command re-tessellated the whole model (the +same 30 ms → 400 ms spike, but while drawing). Preview / interim wires are an +overlay appended to the cached base wire set in `build_primitive`, not part of +the tessellation cache — so they no longer bump geometry. The base stays a +cache hit (no re-tess), and the overlay forces only a GPU wire re-upload via +the `has_overlay` content-id path. Snap/hit-test (`entity_wires_arc`) also +stops re-tessellating per preview frame. + ### 2.3 Progressive first render On the first frame emit a **coarse**-tol wire pass (e.g. 4× the normal diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 2cad01ec9..5706d8f58 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -4251,14 +4251,21 @@ impl Scene { // ── Preview wire ────────────────────────────────────────────────────── pub fn set_preview_wires(&mut self, wires: Vec) { + // Preview wires are an overlay appended to the cached base wire set in + // `build_primitive`; they are NOT part of the tessellation cache. So a + // preview update must NOT bump `geometry_epoch` — that would re- + // tessellate the whole model on every rubber-band frame. The overlay + // forces a GPU wire re-upload on its own (the `has_overlay` content-id + // path), and iced redraws after the message that set the preview. self.preview_wires = wires; - self.bump_geometry(); } pub fn clear_preview_wire(&mut self) { + // No geometry bump — see `set_preview_wires`. Dropping the overlay + // flips the wire content id back to the base tessellation id, which + // re-uploads the base wires (without the preview) on the next frame. self.preview_wires = vec![]; self.interim_wire = None; - self.bump_geometry(); } pub fn wire_models_for(&self, handles: &[acadrust::Handle]) -> Vec { @@ -4284,8 +4291,9 @@ impl Scene { } pub fn set_interim_wire(&mut self, w: WireModel) { + // Overlay wire — same reasoning as `set_preview_wires`: no geometry + // bump, so the model isn't re-tessellated on every interim update. self.interim_wire = Some(w); - self.bump_geometry(); } // ── Selection ─────────────────────────────────────────────────────────