forked from 1b-agent/opencadstudio-upstream
perf: hand the loader thread's tessellation memo to the scene
`prepare_open_geometry` builds the first Model wire set on a throwaway
`Scene`, filling its per-entity tessellation memo with an entry for every
one of the drawing's 186 455 entities. `install_prepared_open_geometry`
then transferred the wire set and the interaction index but not the memo,
so the temporary scene took it to the grave.
The first geometry edit therefore rebuilt from cold:
wires-build 2723.3ms memo_hit=0 memo_miss=186455
against 760 ms once the memo is warm -- 1.3 s of work the loader thread
had already done, repeated on the user's first action.
`PreparedOpenGeometry` now carries the memo, moved rather than cloned
since the scene is dropped immediately afterwards. The guard hash travels
with it: the memo is only usable under the state it was built for, and a
mismatch on the receiving side clears it, which is what happened
unconditionally before.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2d2fdacdfd
commit
d5d9ceec45
1 file changed
+23
@@ -432,6 +432,17 @@ struct ResidentWireLayout {
|
||||
pub struct PreparedOpenGeometry {
|
||||
pub wires: Arc<Vec<WireModel>>,
|
||||
pub interaction_index: Option<Arc<crate::scene::pick::interaction_index::InteractionIndex>>,
|
||||
/// The per-entity tessellation memo the loader thread filled while building
|
||||
/// `wires`, with the guard hash it was valid under.
|
||||
///
|
||||
/// `prepare_open_geometry` works on a throwaway `Scene`, so this used to be
|
||||
/// dropped with it and the first geometry edit re-tessellated all 186 455
|
||||
/// entities from cold — 1.3 s of work the loader had already done. The
|
||||
/// guard travels with it because the memo is only usable under the state it
|
||||
/// was built for; a mismatch on the receiving side clears it, which is
|
||||
/// exactly the old behaviour.
|
||||
pub tess_memo: HashMap<Handle, Arc<Vec<WireModel>>>,
|
||||
pub tess_guard: u64,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PreparedOpenGeometry {
|
||||
@@ -439,6 +450,7 @@ impl std::fmt::Debug for PreparedOpenGeometry {
|
||||
f.debug_struct("PreparedOpenGeometry")
|
||||
.field("wires", &self.wires.len())
|
||||
.field("interaction_index", &self.interaction_index.is_some())
|
||||
.field("tess_memo", &self.tess_memo.len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1016,12 +1028,17 @@ pub fn prepare_open_geometry(
|
||||
wires.len(),
|
||||
);
|
||||
}
|
||||
// Taken, not cloned: the scene is discarded on the next line.
|
||||
let tess_memo = std::mem::take(&mut *scene.resident_tess_memo.borrow_mut());
|
||||
let tess_guard = scene.resident_tess_guard.get();
|
||||
let doc = std::mem::replace(&mut scene.document, CadDocument::new());
|
||||
(
|
||||
doc,
|
||||
PreparedOpenGeometry {
|
||||
wires,
|
||||
interaction_index,
|
||||
tess_memo,
|
||||
tess_guard,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -2206,6 +2223,12 @@ impl Scene {
|
||||
layout: None,
|
||||
},
|
||||
);
|
||||
// Adopt the loader thread's tessellation memo so the first edit patches
|
||||
// the entities it touched instead of rebuilding every one of them.
|
||||
if !prepared.tess_memo.is_empty() {
|
||||
*self.resident_tess_memo.borrow_mut() = prepared.tess_memo;
|
||||
self.resident_tess_guard.set(prepared.tess_guard);
|
||||
}
|
||||
if let Some(index) = prepared.interaction_index {
|
||||
self.cache_interaction_index(self.geometry_epoch, prepared.wires, index);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user