perf: Arc-cache paper_canvas_wires() to eliminate O(N) clone on navigation

paper_canvas_wires() now returns Arc<Vec<WireModel>> backed by
paper_canvas_cache (epoch-keyed). Pan/zoom frames hit the cache and
return a pointer copy; only geometry changes rebuild the wire list.
Completes the paper-canvas caching chain (L+M): sheet cache →
projected-per-viewport cache → full canvas Arc cache.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-04-26 16:30:21 +03:00
commit 52c830e477
2 changed files with 18 additions and 3 deletions

View file

@ -98,6 +98,10 @@ pub struct Scene {
/// Stores projected + clipped wires in paper-space coordinates.
/// Maps vp_handle → (geometry_epoch, Vec<WireModel>).
paper_projected_cache: RefCell<HashMap<Handle, (u64, Vec<WireModel>)>>,
/// Full paper-canvas wire list (sheet + inactive-viewport projections + interim/preview).
/// Keyed by geometry_epoch — valid for all navigation frames (pan/zoom do not bump epoch).
/// Returns Arc so paper_canvas_wires() is O(1) on cache hits.
paper_canvas_cache: RefCell<Option<(u64, Arc<Vec<WireModel>>)>>,
/// Active layout name — "Model" or a paper space layout name.
pub current_layout: String,
/// GPU render data for hatch fills, keyed by the DXF entity Handle.
@ -137,6 +141,7 @@ impl Scene {
viewport_wire_cache: RefCell::new(HashMap::new()),
paper_sheet_cache: RefCell::new(None),
paper_projected_cache: RefCell::new(HashMap::new()),
paper_canvas_cache: RefCell::new(None),
current_layout: "Model".to_string(),
hatches: HashMap::new(),
meshes: HashMap::new(),
@ -2271,7 +2276,15 @@ impl Scene {
/// All wires needed to render the paper-space canvas (2D widget path).
/// Includes paper entities, paper boundary, inactive viewport projections
/// (excluding the active MSPACE viewport), plus interim/preview wires.
pub fn paper_canvas_wires(&self) -> Vec<WireModel> {
pub fn paper_canvas_wires(&self) -> Arc<Vec<WireModel>> {
{
let cache = self.paper_canvas_cache.borrow();
if let Some((cached_epoch, ref arc)) = *cache {
if cached_epoch == self.geometry_epoch {
return Arc::clone(arc);
}
}
}
let layout_block = self.current_layout_block_handle();
let mut wires = self.paper_sheet_wires();
wires.extend(self.viewport_content_wires(layout_block, None, self.active_viewport));
@ -2279,7 +2292,9 @@ impl Scene {
wires.push(iw.clone());
}
wires.extend(self.preview_wires.iter().cloned());
wires
let arc = Arc::new(wires);
*self.paper_canvas_cache.borrow_mut() = Some((self.geometry_epoch, Arc::clone(&arc)));
arc
}
/// Hatch fills for the paper-space canvas.

View file

@ -100,7 +100,7 @@ impl<'a> canvas::Program<Message> for PaperCanvas<'a> {
};
// ── Wires (entity lines + inactive viewport projections) ──────────────
for wire in &self.scene.paper_canvas_wires() {
for wire in self.scene.paper_canvas_wires().iter() {
let [r, g, b, a] = wire.color;
let color = Color { r, g, b, a };