perf(scene): delta journal so edits touch only changed entities (phase 0-1)

Every geometry mutation previously bumped a monotonic geometry_epoch and each
derived cache compared its stamp and, on a miss, re-walked the whole document —
so drawing or moving one line rebuilt every hatch / image / mesh / wipeout model
even though none of them changed.

Add a per-mutation delta journal: bump_entities(&[(Handle, ChangeKind)]) records
exactly which handles changed (Added / Removed / Modified), folding the memo drop
and the epoch bump into one call a site cannot forget. A bounded ring keeps the
recent deltas; replay_since(last_epoch) coalesces the changes past a consumer or
returns None (full rebuild) when a spanned delta is `full` or the consumer fell
off the ring. bump_geometry / bump_geometry_no_blocks push a `full` delta so the
journal is never missing a step.

Route the common single-entity ops through it: add (Added), update (Modified),
erase (Removed), move/rotate/scale/mirror (Modified), copy (Added). Gate the four
per-category model caches on category relevance — an edit whose handles are not in
a category leaves that category cache warm; a removal is treated conservatively
(the erased handle can no longer be classified), and the mesh cache also
invalidates on any INSERT edit since it carries the instanced block meshes.

Net: a plain-entity edit no longer rebuilds the hatch/image/mesh/wipeout model
sets. The resident wire clone-all and the GPU re-upload are still whole-set — the
journal is the groundwork for making those per-entity next. Adds 6 journal unit
tests (replay, coalescing, full-fallback, ring overflow, category gate).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-14 23:50:04 +03:00
1 parent 4bcf5063e9
commit 57401f6f32
4 files changed
+365 -32

No files matched your search

+10 -7
View File
@@ -170,12 +170,12 @@ impl Scene {
}
}
// Only the transformed entities changed (a top-level move/rotate/scale/
// mirror never edits a block definition) — re-tessellate just those and
// keep the block cache + every other entity's memoized wires.
for &h in handles {
self.mark_entity_dirty(h);
}
self.bump_geometry_no_blocks();
// mirror never edits a block definition) — report just those so the
// resident set re-tessellates only them and every derived cache patches
// per-handle, keeping the block cache + all other memoized wires.
let changes: Vec<(Handle, ChangeKind)> =
handles.iter().map(|&h| (h, ChangeKind::Modified)).collect();
self.bump_entities(&changes);
}
/// Give a freshly-cloned entity brand-new handles for every *inline*
@@ -346,7 +346,10 @@ impl Scene {
}
// The copies are new handles (natural memo misses, tessellated fresh)
// and reference only already-cached blocks — no block defn changes.
self.bump_geometry_no_blocks();
// Report them as additions so derived caches patch in exactly the copies.
let changes: Vec<(Handle, ChangeKind)> =
new_handles.iter().map(|&h| (h, ChangeKind::Added)).collect();
self.bump_entities(&changes);
new_handles
}