perf(glyphs): memoize constraint-glyph placements behind constraints_epoch

Placements (entity lookups, intersections, String labels, projections)
recomputed per view() and per mouse-move hit-test, with overlay re-clones
and 2-3x offset recomputes. New constraints_epoch bumped on every set
mutation/visibility/undo/DWG-load (plus new set_constraint_enabled API);
single-slot Arc<[GlyphEntry]> memo on an 11-field key (hover/selection
excluded); overlay de-cloned/de-duped to the single geometry source;
hit paths reuse the cache.

Tests:
- cached_glyph_placements_hit_returns_same_arc: same-key Arc identity
- cached_glyph_placements_invalidated_by_metadata_edit: edit + selection change yield new Arc
- glyph_hit_test_entries_matches_precomputed_boxes: shared hit-test helper

Bench (cargo bench glyph, 60 constraints): 8.18us -> 0.05-0.15us (~55x).
A/B reference deleted.
This commit is contained in:
Karim Jerbi committed 2026-09-21 22:52:47 +01:00
1 parent 2d49204c50
commit 4432f633e7
12 files changed
+767 -239

No files matched your search

+18 -30
View File
@@ -863,39 +863,26 @@ bg={bg_ms:.1}ms n={view_count}"
.map(|constraint| constraint.kind.glyph_symbol().to_string())
})
};
let constraint_glyphs: Vec<(
iced::Point,
[f32; 2],
String,
bool,
bool,
Vec<iced::Point>,
)> = if is_paper {
Vec::new()
let constraint_glyphs: std::sync::Arc<
[crate::scene::parametric_constraints::GlyphEntry],
> = if is_paper {
std::sync::Arc::from([])
} else {
let scope = tab.current_parametric_scope();
tab.scene
.constraint_glyph_placements_screen(
scope,
sel_ref.vp_size,
self.show_constraint_values,
self.constraint_bar_display,
self.constraint_bar_mode,
)
.into_iter()
.map(|(id, point, direction, label, is_conflicting, hover_points)| {
let selected = tab.scene.selected_constraint == Some(id);
(
point,
direction,
label,
is_conflicting,
selected,
hover_points,
)
})
.collect()
tab.scene.cached_glyph_placements(
scope,
sel_ref.vp_size,
self.show_constraint_values,
self.constraint_bar_display,
self.constraint_bar_mode,
)
};
// Selection stays OUTSIDE the cache (applied post-hoc): a selection
// change must not invalidate the placement memo.
let constraint_glyph_selected: std::sync::Arc<[bool]> = constraint_glyphs
.iter()
.map(|entry| tab.scene.selected_constraint == Some(entry.id))
.collect();
crate::ui::overlay::selection_overlay(
std::sync::Arc::clone(&tab.scene.selection),
snap_info,
@@ -939,6 +926,7 @@ bg={bg_ms:.1}ms n={view_count}"
grip_hover: self.model_space.grip_hover,
},
constraint_glyphs,
constraint_glyph_selected,
self.constraint_glyph_tooltip
.map(|kind| crate::t!(kind.label()).into_owned()),
constraint_cursor_badge,