perf(text): cache SDF text vertices across frames

The SDF text collector re-laid out every glyph of every text entity on
each frame (pan/zoom included). Cache the vertex list on the Scene keyed
by (geometry_epoch, annotation-scale bits) — it only changes when geometry
or the scale changes — so pan/zoom reuses it. Glyph baking happens on the
cache-miss path only; the atlas is a persistent global and the texture
upload is already gated on the geometry epoch, so cache hits skip both the
layout and any re-upload. No behaviour change.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-07 08:29:18 +03:00
1 parent 75ddab6da7
commit fd0b8fc1d4
2 files changed
+32 -3

No files matched your search

+6
View File
@@ -710,6 +710,11 @@ pub struct Scene {
pub(crate) model_tiles: RefCell<Vec<ModelTile>>,
/// Index of the active model tile (camera input + overlays target it).
pub(crate) active_model_tile: std::cell::Cell<usize>,
/// Cache of the SDF text vertex list, keyed on `(geometry_epoch,
/// anno_scale.to_bits())`, so the per-frame collector is not re-laid-out on
/// pan / zoom. See [`Scene::sdf_text_vertices`].
sdf_text_cache:
RefCell<Option<(u64, u32, std::sync::Arc<Vec<crate::scene::pipeline::text_gpu::TextVertex>>)>>,
/// pane_grid layout tree for the Model tab — the source of truth for the
/// tile split layout, resize and focus. `model_tiles` (the renderer's
/// per-pane data: camera / render-mode / grid) is kept in lock-step with
@@ -959,6 +964,7 @@ impl Scene {
snap_on: false,
}]),
active_model_tile: std::cell::Cell::new(0),
sdf_text_cache: RefCell::new(None),
// One pane mapped to tile 0 — matches the single default tile above.
model_panes: iced::widget::pane_grid::State::new(0).0,
selection: Rc::new(RefCell::new(SelectionState::default())),
+26 -3
View File
@@ -783,8 +783,31 @@ impl Scene {
#[allow(dead_code)] // consumed by the text render pass in the GPU integration step
pub(in crate::scene) fn sdf_text_vertices(
&self,
) -> Vec<crate::scene::pipeline::text_gpu::TextVertex> {
self.sdf_text_vertices_enabled(crate::scene::text::sdf_atlas::sdf_text_enabled())
) -> std::sync::Arc<Vec<crate::scene::pipeline::text_gpu::TextVertex>> {
use std::sync::Arc;
if !crate::scene::text::sdf_atlas::sdf_text_enabled() {
return Arc::new(Vec::new());
}
// Cache keyed on (geometry_epoch, annotation scale): the run layout only
// changes when geometry or the scale changes, so pan / zoom reuses it
// instead of re-laying-out every glyph each frame.
let anno = if self.current_layout == "Model" {
self.annotation_scale
} else {
1.0
};
let key = (self.geometry_epoch, anno.to_bits());
{
let cache = self.sdf_text_cache.borrow();
if let Some((epoch, anno_bits, verts)) = cache.as_ref() {
if *epoch == key.0 && *anno_bits == key.1 {
return verts.clone();
}
}
}
let verts = Arc::new(self.sdf_text_vertices_enabled(true));
*self.sdf_text_cache.borrow_mut() = Some((key.0, key.1, verts.clone()));
verts
}
#[allow(dead_code)]
@@ -1152,7 +1175,7 @@ impl Scene {
let text_verts = if inst.paper_sheet {
Arc::new(Vec::new())
} else {
Arc::new(self.sdf_text_vertices())
self.sdf_text_vertices()
};
Some(ViewportData {
wires: all_wires,