fix(render): SDF text reuse is per wire source (#403)
Paper layouts gather glyphs for the sheet and each content viewport in
one frame; the single-slot last_sdf_text handed the first source's
glyphs to the rest, hiding every model-space text entity inside
viewports (regression in aea1fd2). Key the reuse by render source, and
invalidate on Tolerance/ATTDEF edits and complex-linetype hosts too.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
1cf64161d3
commit
c0499a1d3c
2 changed files with 71 additions and 28 deletions
|
|
@ -789,12 +789,16 @@ pub struct Scene {
|
||||||
sdf_text_cache: RefCell<
|
sdf_text_cache: RefCell<
|
||||||
HashMap<u64, std::sync::Arc<Vec<crate::scene::pipeline::text_gpu::TextVertex>>>,
|
HashMap<u64, std::sync::Arc<Vec<crate::scene::pipeline::text_gpu::TextVertex>>>,
|
||||||
>,
|
>,
|
||||||
/// `(geometry_epoch, gathered text)` of the most recent SDF-text build. When a
|
/// Per wire-source `(geometry_epoch, gathered text)` of the most recent
|
||||||
/// new content id misses the cache but the journal shows no text-bearing
|
/// SDF-text build. When a new content id misses the cache but the journal
|
||||||
/// entity changed since this epoch, the same glyphs are reused instead of
|
/// shows no text-bearing entity changed since that epoch, the same glyphs
|
||||||
/// re-walking every wire — an edit on plain geometry never rebuilds the text.
|
/// are reused instead of re-walking every wire — an edit on plain geometry
|
||||||
|
/// never rebuilds the text. Keyed by the render source (sheet / tile /
|
||||||
|
/// content viewport / implicit view): a paper frame gathers several wire
|
||||||
|
/// sets in one frame, and a single slot handed the sheet's glyphs to the
|
||||||
|
/// content viewports, hiding every model-space text entity (#403).
|
||||||
last_sdf_text: RefCell<
|
last_sdf_text: RefCell<
|
||||||
Option<(u64, std::sync::Arc<Vec<crate::scene::pipeline::text_gpu::TextVertex>>)>,
|
HashMap<u64, (u64, std::sync::Arc<Vec<crate::scene::pipeline::text_gpu::TextVertex>>)>,
|
||||||
>,
|
>,
|
||||||
/// pane_grid layout tree for the Model tab — the source of truth for the
|
/// 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
|
/// tile split layout, resize and focus. `model_tiles` (the renderer's
|
||||||
|
|
@ -1115,7 +1119,7 @@ impl Scene {
|
||||||
}]),
|
}]),
|
||||||
active_model_tile: std::cell::Cell::new(0),
|
active_model_tile: std::cell::Cell::new(0),
|
||||||
sdf_text_cache: RefCell::new(HashMap::default()),
|
sdf_text_cache: RefCell::new(HashMap::default()),
|
||||||
last_sdf_text: RefCell::new(None),
|
last_sdf_text: RefCell::new(HashMap::default()),
|
||||||
// One pane mapped to tile 0 — matches the single default tile above.
|
// One pane mapped to tile 0 — matches the single default tile above.
|
||||||
model_panes: iced::widget::pane_grid::State::new(0).0,
|
model_panes: iced::widget::pane_grid::State::new(0).0,
|
||||||
selection: Rc::new(RefCell::new(SelectionState::default())),
|
selection: Rc::new(RefCell::new(SelectionState::default())),
|
||||||
|
|
@ -1394,26 +1398,39 @@ impl Scene {
|
||||||
|
|
||||||
/// True when no text-bearing entity changed since `last_epoch`, so cached SDF
|
/// True when no text-bearing entity changed since `last_epoch`, so cached SDF
|
||||||
/// glyphs stay valid. Text comes from Text / MText / Dimension / MultiLeader /
|
/// glyphs stay valid. Text comes from Text / MText / Dimension / MultiLeader /
|
||||||
/// Leader / Table / attributes and from block references (their baked text
|
/// Leader / Table / Tolerance / attributes (incl. ATTDEF) and from block
|
||||||
/// moves with the instance) — an edit to any of those, or any removal,
|
/// references (their baked text moves with the instance) — an edit to any of
|
||||||
/// invalidates it; a plain line / arc / polyline edit does not.
|
/// those, or any removal, invalidates it; a plain line / arc / polyline edit
|
||||||
|
/// does not.
|
||||||
fn text_unchanged(&self, last_epoch: u64) -> bool {
|
fn text_unchanged(&self, last_epoch: u64) -> bool {
|
||||||
match self.replay_since(last_epoch) {
|
match self.replay_since(last_epoch) {
|
||||||
Some(deltas) => deltas.iter().all(|&(h, k)| {
|
Some(deltas) => deltas.iter().all(|&(h, k)| {
|
||||||
k != ChangeKind::Removed
|
if k == ChangeKind::Removed {
|
||||||
&& !matches!(
|
return false;
|
||||||
self.document.get_entity(h),
|
}
|
||||||
Some(
|
let Some(e) = self.document.get_entity(h) else {
|
||||||
EntityType::Text(_)
|
return true;
|
||||||
| EntityType::MText(_)
|
};
|
||||||
| EntityType::Dimension(_)
|
if matches!(
|
||||||
| EntityType::MultiLeader(_)
|
e,
|
||||||
| EntityType::Leader(_)
|
EntityType::Text(_)
|
||||||
| EntityType::Table(_)
|
| EntityType::MText(_)
|
||||||
| EntityType::AttributeEntity(_)
|
| EntityType::Dimension(_)
|
||||||
| EntityType::Insert(_)
|
| EntityType::MultiLeader(_)
|
||||||
)
|
| EntityType::Leader(_)
|
||||||
)
|
| EntityType::Table(_)
|
||||||
|
| EntityType::Tolerance(_)
|
||||||
|
| EntityType::AttributeEntity(_)
|
||||||
|
| EntityType::AttributeDefinition(_)
|
||||||
|
| EntityType::Insert(_)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Complex-linetype glyphs ride the host entity's own wire, so a
|
||||||
|
// plain line/polyline edit still moves text when its linetype
|
||||||
|
// embeds text or shapes.
|
||||||
|
let lt = crate::scene::view::render::linetype_name_for(&self.document, e);
|
||||||
|
crate::io::linetypes::resolve_complex_lt(&self.document, <).is_none()
|
||||||
}),
|
}),
|
||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -997,6 +997,7 @@ impl Scene {
|
||||||
&self,
|
&self,
|
||||||
wires: &[WireModel],
|
wires: &[WireModel],
|
||||||
wire_content_id: u64,
|
wire_content_id: u64,
|
||||||
|
source_key: u64,
|
||||||
) -> std::sync::Arc<Vec<crate::scene::pipeline::text_gpu::TextVertex>> {
|
) -> std::sync::Arc<Vec<crate::scene::pipeline::text_gpu::TextVertex>> {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
{
|
{
|
||||||
|
|
@ -1007,11 +1008,13 @@ impl Scene {
|
||||||
}
|
}
|
||||||
// A new content id (every geometry edit) misses the cache — but if no
|
// A new content id (every geometry edit) misses the cache — but if no
|
||||||
// text-bearing entity changed since the last build, the glyphs are
|
// text-bearing entity changed since the last build, the glyphs are
|
||||||
// identical, so reuse them instead of re-walking every wire.
|
// identical, so reuse them instead of re-walking every wire. Reuse is
|
||||||
|
// per `source_key`: another source's glyphs are a different wire set,
|
||||||
|
// not an older build of this one (#403).
|
||||||
{
|
{
|
||||||
let reuse = {
|
let reuse = {
|
||||||
let last = self.last_sdf_text.borrow();
|
let last = self.last_sdf_text.borrow();
|
||||||
match &*last {
|
match last.get(&source_key) {
|
||||||
Some((epoch, arc)) if self.text_unchanged(*epoch) => Some(arc.clone()),
|
Some((epoch, arc)) if self.text_unchanged(*epoch) => Some(arc.clone()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|
@ -1020,7 +1023,9 @@ impl Scene {
|
||||||
self.sdf_text_cache
|
self.sdf_text_cache
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(wire_content_id, arc.clone());
|
.insert(wire_content_id, arc.clone());
|
||||||
*self.last_sdf_text.borrow_mut() = Some((self.geometry_epoch, arc.clone()));
|
self.last_sdf_text
|
||||||
|
.borrow_mut()
|
||||||
|
.insert(source_key, (self.geometry_epoch, arc.clone()));
|
||||||
return arc;
|
return arc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1039,7 +1044,13 @@ impl Scene {
|
||||||
}
|
}
|
||||||
cache.insert(wire_content_id, verts.clone());
|
cache.insert(wire_content_id, verts.clone());
|
||||||
}
|
}
|
||||||
*self.last_sdf_text.borrow_mut() = Some((self.geometry_epoch, verts.clone()));
|
{
|
||||||
|
let mut last = self.last_sdf_text.borrow_mut();
|
||||||
|
if last.len() > 8 {
|
||||||
|
last.clear();
|
||||||
|
}
|
||||||
|
last.insert(source_key, (self.geometry_epoch, verts.clone()));
|
||||||
|
}
|
||||||
verts
|
verts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1344,7 +1355,22 @@ impl Scene {
|
||||||
// internal text and the paper sheet's own annotation alike — each set
|
// internal text and the paper sheet's own annotation alike — each set
|
||||||
// draws only the text that belongs to it. Cached on the wire content
|
// draws only the text that belongs to it. Cached on the wire content
|
||||||
// id so an unchanged wire set is not re-walked every frame.
|
// id so an unchanged wire set is not re-walked every frame.
|
||||||
let text_verts = self.gather_text_verts(&all_wires, wire_content_id);
|
// The reuse fallback inside the gather is keyed per wire SOURCE — the
|
||||||
|
// sheet, a Model tile, a content viewport and the implicit view carry
|
||||||
|
// different glyph sets even at the same geometry epoch (#403). Tiles
|
||||||
|
// share the resident Model set, so they share one key; the implicit
|
||||||
|
// view mixes in the current layout block (BEDIT swaps sets without a
|
||||||
|
// geometry delta).
|
||||||
|
let text_source_key: u64 = if inst.tile_idx.is_some() {
|
||||||
|
0x1000_0000_0000_0000
|
||||||
|
} else if inst.paper_sheet {
|
||||||
|
0x2000_0000_0000_0000
|
||||||
|
} else if inst.handle == acadrust::Handle::NULL {
|
||||||
|
0x4000_0000_0000_0000 | self.current_layout_block_handle().value()
|
||||||
|
} else {
|
||||||
|
0x3000_0000_0000_0000 | inst.handle.value()
|
||||||
|
};
|
||||||
|
let text_verts = self.gather_text_verts(&all_wires, wire_content_id, text_source_key);
|
||||||
// Grip-drag / command-preview glyphs, excluded from the epoch-cached base
|
// Grip-drag / command-preview glyphs, excluded from the epoch-cached base
|
||||||
// gather above. Two sources, both tiny (one operation's worth) and walked
|
// gather above. Two sources, both tiny (one operation's worth) and walked
|
||||||
// per frame: the overlay wires' own glyphs (MOVE / COPY / ROTATE / SCALE /
|
// per frame: the overlay wires' own glyphs (MOVE / COPY / ROTATE / SCALE /
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue