perf(render): arena on by default; trim per-edit resident + text work

Three cuts to the per-edit cost, on top of the incremental wire arena:

- Enable OCS_WIRE_GPU_PATCH by default (kill-switch OCS_WIRE_GPU_PATCH=0), so the
  in-place / append-tombstone arena — validated on the reference drawing (wire
  block ~300ms + 265MB upload -> ~2ms) — is on for everyone; a bail always falls
  back to the identical batched path.

- Resident patch: an all-Modified edit never reorders, so reassemble by walking
  the recorded assembled order (~10k handles) instead of scanning the whole
  document (~800k) to re-derive draw order. Add/Remove still scan (positions
  change). ~14ms -> <1ms on a move.

- SDF text: a new content id misses the glyph cache every edit, but if no
  text-bearing entity (Text/MText/Dimension/MLeader/Leader/Table/attribute/INSERT)
  changed since the last build the glyphs are identical, so reuse them. ~23ms -> 0
  on a plain-geometry edit.

A move/edit on this 800k-entity drawing now spends ~50ms (dominated by the undo
document clone) instead of ~85ms, and the wire GPU upload is gone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-15 10:14:44 +03:00
1 parent f12100d783
commit aea1fd2caa
2 files changed
+104 -19

No files matched your search

+27 -5
View File
@@ -990,6 +990,25 @@ impl Scene {
return verts.clone();
}
}
// A new content id (every geometry edit) misses the cache — but if no
// text-bearing entity changed since the last build, the glyphs are
// identical, so reuse them instead of re-walking every wire.
{
let reuse = {
let last = self.last_sdf_text.borrow();
match &*last {
Some((epoch, arc)) if self.text_unchanged(*epoch) => Some(arc.clone()),
_ => None,
}
};
if let Some(arc) = reuse {
self.sdf_text_cache
.borrow_mut()
.insert(wire_content_id, arc.clone());
*self.last_sdf_text.borrow_mut() = Some((self.geometry_epoch, arc.clone()));
return arc;
}
}
let mut out: Vec<crate::scene::pipeline::text_gpu::TextVertex> = Vec::new();
for w in wires {
if !w.text_verts.is_empty() {
@@ -997,12 +1016,15 @@ impl Scene {
}
}
let verts = Arc::new(out);
let mut cache = self.sdf_text_cache.borrow_mut();
// Ids change on rebuild, so old keys die naturally; cap bounds churn.
if cache.len() > 8 {
cache.clear();
{
let mut cache = self.sdf_text_cache.borrow_mut();
// Ids change on rebuild, so old keys die naturally; cap bounds churn.
if cache.len() > 8 {
cache.clear();
}
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()));
verts
}