From 626031de00c88634ffbe43dcddd330820ad44d25 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Sun, 5 Jul 2026 03:26:07 +0300 Subject: [PATCH] perf(render): reuse static wire set for model-space pick/snap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every pan/rotate on a large drawing jerked once at the start of the gesture. Instrumentation traced it to `paper_sheet_wires_arc` re-running a full ~300 ms tessellation of the whole model block on the first hit-test after each camera move. `entity_wires_arc` (the pick/snap source in Model layout) delegated to the camera_generation-keyed, view-culled `paper_sheet_wires_arc`. A camera move bumps `camera_generation`, so the next hit-test cold-missed that cache and re-tessellated every visible entity — O(visible), which is why the jump grew with on-screen object count and shrank when zoomed in. The GPU render was unaffected (it holds the separate camera-independent `model_static_wires`), so only that one CPU frame stalled and the accumulated mouse motion applied at once, reading as a jump. Point the Model pick path at the same resident static set the render holds (keyed on geometry_epoch only). Pick wants every entity, not a view-culled subset, so this is also more correct — off-screen entities stay pickable — and it never re-tessellates on a camera move. Co-Authored-By: Claude Opus 4.8 --- src/scene/mod.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 212cbdc3d..86b1e7b04 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -2117,12 +2117,18 @@ impl Scene { } } let layout_block = self.current_layout_block_handle(); - // Model space: paper_sheet_wires_arc IS the full entity wire set — share the Arc, - // no Vec clone needed. + // Model space: reuse the resident, camera-independent static wire set the + // render already holds (keyed on geometry_epoch only). This is the FULL + // entity set — pick / snap want every entity, not a view-culled subset — + // and it does NOT re-tessellate on a camera move. The old path went + // through the camera_generation-keyed, view-culled paper_sheet set, so + // every pan/rotate cold-missed it and paid a full O(visible) re-tess + // (~300 ms on large drawings) the first time hit-testing ran after the + // move — the "jump" at the start of each gesture. The tile args are + // unused by `model_tile_wires_arc`. if self.current_layout == "Model" { - let arc = self.paper_sheet_wires_arc(); - *self.wire_cache.borrow_mut() = Some((key, Arc::clone(&arc))); - return arc; + let cam = self.camera.borrow().clone(); + return self.model_tile_wires_arc(0, &cam, 1.0, 1.0); } // Paper space: extend sheet wires with projected viewport content. let mut wires = (*self.paper_sheet_wires_arc()).clone();