forked from 1b-agent/opencadstudio-upstream
perf: hand the loader thread's block definitions to the scene
The first edit on a large drawing spent 1147 ms rebuilding the block
definition cache:
wires-build 2723.3ms ... blk_cache=1147.2 ... memo_miss=186455
Not because the edit invalidated it -- `add_entity_internal` already
routes a non-block entity through `bump_entities`, so drawing a line
leaves `block_epoch` alone. The cache was empty because
`prepare_open_geometry` built it on a throwaway `Scene`, exactly as it
did the tessellation memo, and it went the same way.
`PreparedOpenGeometry` now carries the definitions across, moved rather
than cloned, and `install_prepared_open_geometry` stamps them with the
receiving scene's `block_epoch`. That stamp is sound: the document is not
touched between the loader finishing and this call, so the definitions
describe it exactly. A key that does not match what the scene later asks
for simply misses, which is the old behaviour.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
38f576c296
commit
909b7f3379
1 file changed
+25
@@ -443,6 +443,14 @@ pub struct PreparedOpenGeometry {
|
|||||||
/// exactly the old behaviour.
|
/// exactly the old behaviour.
|
||||||
pub tess_memo: HashMap<Handle, Arc<Vec<WireModel>>>,
|
pub tess_memo: HashMap<Handle, Arc<Vec<WireModel>>>,
|
||||||
pub tess_guard: u64,
|
pub tess_guard: u64,
|
||||||
|
/// Block definitions the loader thread built while tessellating, keyed as
|
||||||
|
/// `block_defn_cache` keys them but without an epoch — the receiving scene
|
||||||
|
/// stamps its own.
|
||||||
|
///
|
||||||
|
/// Dropped with the throwaway scene before, which is why the first edit
|
||||||
|
/// spent 1147 ms rebuilding definitions the loader had just built from the
|
||||||
|
/// same, unmodified document.
|
||||||
|
pub block_defns: Vec<(u64, Arc<cache::block_cache::BlockCache>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for PreparedOpenGeometry {
|
impl std::fmt::Debug for PreparedOpenGeometry {
|
||||||
@@ -451,6 +459,7 @@ impl std::fmt::Debug for PreparedOpenGeometry {
|
|||||||
.field("wires", &self.wires.len())
|
.field("wires", &self.wires.len())
|
||||||
.field("interaction_index", &self.interaction_index.is_some())
|
.field("interaction_index", &self.interaction_index.is_some())
|
||||||
.field("tess_memo", &self.tess_memo.len())
|
.field("tess_memo", &self.tess_memo.len())
|
||||||
|
.field("block_defns", &self.block_defns.len())
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1031,6 +1040,11 @@ pub fn prepare_open_geometry(
|
|||||||
// Taken, not cloned: the scene is discarded on the next line.
|
// 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_memo = std::mem::take(&mut *scene.resident_tess_memo.borrow_mut());
|
||||||
let tess_guard = scene.resident_tess_guard.get();
|
let tess_guard = scene.resident_tess_guard.get();
|
||||||
|
let block_defns: Vec<(u64, Arc<cache::block_cache::BlockCache>)> =
|
||||||
|
std::mem::take(&mut *scene.block_defn_cache.borrow_mut())
|
||||||
|
.into_iter()
|
||||||
|
.map(|(key, (_epoch, cache))| (key, cache))
|
||||||
|
.collect();
|
||||||
let doc = std::mem::replace(&mut scene.document, CadDocument::new());
|
let doc = std::mem::replace(&mut scene.document, CadDocument::new());
|
||||||
(
|
(
|
||||||
doc,
|
doc,
|
||||||
@@ -1039,6 +1053,7 @@ pub fn prepare_open_geometry(
|
|||||||
interaction_index,
|
interaction_index,
|
||||||
tess_memo,
|
tess_memo,
|
||||||
tess_guard,
|
tess_guard,
|
||||||
|
block_defns,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -2229,6 +2244,16 @@ impl Scene {
|
|||||||
*self.resident_tess_memo.borrow_mut() = prepared.tess_memo;
|
*self.resident_tess_memo.borrow_mut() = prepared.tess_memo;
|
||||||
self.resident_tess_guard.set(prepared.tess_guard);
|
self.resident_tess_guard.set(prepared.tess_guard);
|
||||||
}
|
}
|
||||||
|
// Same for the block definitions, stamped with this scene's block
|
||||||
|
// epoch: the document has not been touched between the loader
|
||||||
|
// finishing and this call, so they describe it exactly.
|
||||||
|
if !prepared.block_defns.is_empty() {
|
||||||
|
let epoch = self.block_epoch;
|
||||||
|
let mut cache = self.block_defn_cache.borrow_mut();
|
||||||
|
for (key, defns) in prepared.block_defns {
|
||||||
|
cache.insert(key, (epoch, defns));
|
||||||
|
}
|
||||||
|
}
|
||||||
if let Some(index) = prepared.interaction_index {
|
if let Some(index) = prepared.interaction_index {
|
||||||
self.cache_interaction_index(self.geometry_epoch, prepared.wires, index);
|
self.cache_interaction_index(self.geometry_epoch, prepared.wires, index);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user