acdream/docs/research/2026-07-25-slice-i1-transition-scratch.md
Erik 16d182c2f0 perf(physics): reuse reset-complete transition scratch
Mirror retail's ten-deep LIFO transition lifetime, retain all query scratch with complete reset contracts, and remove Tier-0 enum boxing without changing collision decisions. Fresh and retained engines are bit-identical across the expanded oracle, while measured transition profiles now allocate 0 bytes per resolve.
2026-07-25 14:37:02 +02:00

4.7 KiB

Slice I1 — reset-complete transition and query scratch

Status: COMPLETE Parent: docs/plans/2026-07-25-modern-runtime-slice-i.md Scope: storage lifetime and allocation only; retail collision math, comparisons, branch order, traversal order, and returned behavior are unchanged.

Retail lifetime carried forward

Named retail establishes a retained, stack-disciplined transition lifetime:

  • CTransition::makeTransition 0x0050B150
  • CTransition::cleanupTransition 0x00509DC0
  • CTransition::init 0x00509DD0
  • COLLISIONINFO::init 0x00509D60
  • SPHEREPATH::init 0x0050C330

Retail owns ten pre-existing CTransition records, selects one by nesting depth, resets it before use, and releases it in LIFO order. The acdream port now gives each PhysicsEngine the same ten-deep, same-thread lease contract. ResolveWithTransition and ResolvePlacement always return their lease in a finally block.

Storage changes

  • ObjectInfo, CollisionInfo, SpherePath, and Transition have complete ResetForReuse contracts.
  • Fixed sphere objects, collision GUID storage, exact-length walkable buffers, candidate-cell storage, and reentrant ordered-cell lists retain identity. Every logical value and retained buffer content is reset before reuse.
  • Neighboring-cell scans use a retained LIFO list arena. This is required because a cell collision may recursively enter step-up/step-down and another CheckOtherCells before the outer scan resumes.
  • BSPQuery.CollisionSphere is value/ref scratch instead of a short-lived reference object.
  • Terrain's always-three-vertex surface sample is stored inline. Production BSP entry points take center/radius values directly instead of constructing temporary DAT Sphere objects.
  • CellTransit reuses the transition-owned ordered/deduplicated candidate collection.
  • Walkable publication to PhysicsBody reuses only an exact-length backing array; a real polygon-size change replaces it.
  • Hot enum predicates use equivalent bit masks. This removes Tier-0 Enum.HasFlag boxing while preserving the exact branch condition.

The wider collision oracle exposed the reentrant-cell-list hazard before landing: three Facility Hub corridor seam replays initially threw Collection was modified. The final LIFO query arena keeps every nested scan independent; all three exact seam replays pass.

Differential and structural evidence

TransitionScratchResetTests:

  • reflection-poisons every stored member;
  • resets and compares the complete logical graph with a fresh transition;
  • proves sphere, child, list, candidate, and walkable-buffer identities are retained while their values are cleared;
  • proves exact-length walkable reuse and replacement on a true size change;
  • proves ten distinct nested leases, capacity rejection, LIFO enforcement, same-thread enforcement, and clean reuse.

TransitionScratchDifferentialTests runs a fresh-transition engine beside the production retained engine and compares every ResolveResult float by bits plus every public PhysicsBody side effect. The hostile alternating sequence covers:

  • successful and failed transitions;
  • grounded and airborne movement;
  • wall collision and carried sliding state;
  • step-up and step-down inputs;
  • two-sphere characters;
  • one-sphere projectiles;
  • viewer/camera sweeps;
  • placement overlap search;
  • different mover and designated-target identities.

The fixed graph oracle, including corridor seams, cellar, doors, projectiles, camera, stairs, cylinder/sphere families, and the new I1 gates, is 118 passed / 0 skipped / 0 failed.

Allocation result

Release, one thread, 256 warmups and 4,096 measured resolves per profile:

Profile I0 graph baseline I1 retained scratch
player, two spheres 4,448 B/resolve 0 B/resolve
remote, two spheres 4,448 B/resolve 0 B/resolve
projectile, one 5 cm sphere 6,848 B/resolve 0 B/resolve
camera/viewer, one 30 cm sphere 2,512 B/resolve 0 B/resolve
grounded walkable publication not isolated 0 B/resolve

This is an isolated transition/query-scratch gate. Optional diagnostics and capture remain intentionally allocating, and the ordinary connected frame-allocation profile is repeated at Slice I7 after flat-asset cutover.

Release gates

  • focused collision oracle: 118 passed
  • complete Core tests: 3,250 passed / 2 skipped
  • dotnet build AcDream.slnx -c Release --no-restore: passed
  • complete solution tests: 8,342 passed / 5 skipped

Issue #237 is closed. Slice I2 begins with immutable Core-only flat collision records and deterministic source-graph flattening; the current graph traversal remains the executable behavior oracle.