fix(physics): make collision activation starvation-free

This commit is contained in:
Erik 2026-07-31 18:34:46 +02:00
parent d94145e6b8
commit 6b28ff999c
14 changed files with 4637 additions and 474 deletions

View file

@ -15,11 +15,38 @@ as "delete everything and start over." A partial retail transition port exists:
engine snapshot, buildings, statics, and versioned retained-owner refloods.
Retained means every non-suspended dynamic or adjacent-root static touching
(or withdrawn from) the target prefix; only authored target-root statics are
superseded. All scans and replacement construction run through bounded
capture/seal cursors. `RuntimePhysicsState.CommitCollisionGeneration` does
one final mutation-version check and activates the sealed generation without
managed allocation on the single update thread. Cancellation tears down only
the named staging generation. The old generation remains queryable until
superseded. Stable per-prefix owner slots bound capture independently of
unrelated movement. One Runtime-scoped versioned journal records a live
mutation once, coalesces repeated changes by owner, and lets every draft
reconcile only that owner's latest exact state one owner per seal step.
Relevant owners subscribe only after discovery, preserving exact continuous
updates without the former preparations-by-mutations global fanout or a quiet
frame. A changed prefix routes first target entry/departure to the one
matching draft after its journal cursor has passed. During topology build,
visited unrelated owners receive only coalesced dirty notifications; one
later seal unit performs the exact mirror. After the topology seal exists,
observed owners temporarily write through exactly until same-call activation,
guaranteeing that the finite dirty queue drains under multi-owner motion.
Old slots are superseded at the tail rather than reused behind
live cursors, new drafts start at their captured suffix, and obsolete slots
plus empty prefix containers are reclaimed under churn.
Admission captures the active aggregate
root reference in O(1), then stable landblock/owner suffixes materialize the
non-target cache, CellGraph, engine, and shadow leaves one work unit per host
step. All scans and replacement construction run
through bounded preparation/capture/seal cursors. Cache, CellGraph, engine, and shadow
topology share one complete off-side `CollisionWorldState`; final activation
is a single zero-allocation volatile root transfer that preserves borrowed
facade identities and revokes staging. Deterministically ordered concurrent
preparations queue their deltas into later drafts only after commit; each
seal call rebases at most one cache/graph/landblock/owner leaf. Demotion or
withdrawal cancels matching queued/active rebases, suppresses stale source-
clone leaves, and retires one owner/cache/graph/outdoor leaf per seal step
from growable storage; commit rechecks pending retirement after sealing.
The host performs the zero-work root transfer in the same update-thread call
that completes final reconciliation, avoiding a quiet-frame requirement.
Cancellation tears down
only the named staging generation. The old generation remains queryable until
commit. See
`docs/research/2026-07-31-atomic-collision-generation.md`.
- `ShadowObjectRegistry` gives the resolver a broadphase over nearby objects.