perf(flatten): pre-sized collect/plan and batched entity publish

flatten_plan 2,289 -> 1,098-1,144 us (-50..-52%); flatten_scene_apply 1,213 -> 789-813 us (-33..-35% vs baseline; -28.5% same-session O2-vs-O1, drift-corrected).

Pre-sizes collect/plan vectors and adds Scene::update_entities so FLATTEN issues one geometry bump per batch instead of per entity; single update_entity keeps its exact bump-per-call semantics.
This commit is contained in:
Karim Jerbi committed 2026-09-28 20:29:40 +01:00
1 parent f4dfead9c0
commit fdbbcc419e
3 files changed
+77 -37

No files matched your search

+49 -3
View File
@@ -514,7 +514,53 @@ impl Scene {
/// [`add_entity`](Self::add_entity) used to commit a plugin's edit of an
/// existing entity.
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
pub fn update_entity(&mut self, mut entity: EntityType) -> bool {
pub fn update_entity(&mut self, entity: EntityType) -> bool {
let mut changed = Vec::with_capacity(1);
let mut block_change = false;
if !self.update_entity_impl(entity, &mut changed, &mut block_change) {
return false;
}
self.publish_entity_updates(changed, block_change);
true
}
/// Batched variant of [`update_entity`]: writes every replacement, then
/// issues a single geometry bump for the whole set instead of one bump per
/// entity.
pub fn update_entities(&mut self, updates: Vec<EntityType>) -> usize {
let mut moved = 0usize;
let mut changed = Vec::with_capacity(updates.len());
let mut block_change = false;
for entity in updates {
if self.update_entity_impl(entity, &mut changed, &mut block_change) {
moved += 1;
}
}
if !changed.is_empty() || block_change {
self.publish_entity_updates(changed, block_change);
}
moved
}
/// One geometry bump for a set of slot writes already reflected in
/// `changed` / `block_change`.
fn publish_entity_updates(&mut self, changed: Vec<(Handle, ChangeKind)>, block_change: bool) {
if block_change {
self.bump_geometry();
} else {
self.bump_entities(&changed);
}
}
/// Shared write path for `update_entity` / `update_entities`: validates,
/// records undo, writes the slot, and reseeds derived caches, recording
/// the change instead of bumping geometry.
fn update_entity_impl(
&mut self,
mut entity: EntityType,
changed: &mut Vec<(Handle, ChangeKind)>,
block_change: &mut bool,
) -> bool {
let handle = entity.common().handle;
if self.is_layer_locked(handle) {
return false;
@@ -637,12 +683,12 @@ impl Scene {
if affects_blocks {
self.mark_entity_dirty(handle);
self.bump_geometry();
*block_change = true;
} else {
// One entity changed in place: report just this handle so every
// derived cache patches it instead of rebuilding (bump_entities also
// drops it from the tessellation memos).
self.bump_entities(&[(handle, ChangeKind::Modified)]);
changed.push((handle, ChangeKind::Modified));
}
true
}