feat(physics): define deterministic flat collision assets

Add immutable indexed physics and containment BSP records, exact-bit polygon and Setup payloads, separated CellStruct/topology ownership, and iterative positive-before-negative flattening. Reject malformed graphs and ranges, and prove source identity over synthetic edge cases and installed retail DAT samples without cutting production traversal over.
This commit is contained in:
Erik 2026-07-25 14:52:47 +02:00
parent 16d182c2f0
commit d9300c7854
11 changed files with 2094 additions and 21 deletions

View file

@ -1650,13 +1650,17 @@ port in any phase — no separate listing here.
> CPU/GPU p50 1.869/1.096 ms. Slice I1 now removes the isolated
> transition/query owner: player, remote, projectile, camera, and grounded
> walkable-publication profiles each measure 0 B/resolve, and retained
> transitions remain bit-identical to the fresh-graph oracle.
> transitions remain bit-identical to the fresh-graph oracle. Slice I2 now
> defines deterministic immutable collision records and validates exact
> source identity over synthetic corruption cases and representative installed
> DATs; production traversal remains on the graph oracle until the later
> differential gate.
> The historical exact cutover
> rollback remains
> `git revert ef1d263337997bb030eadb7b8e71d73dc659907a`. Slice H is complete:
> retained UI/frame work, exact bounded light selection, and
> pooled/borrowed/direct network I/O pass 8,292 Release tests / 5 skips and the
> connected lifecycle/reconnect gate. Slice I is active at I2 from
> connected lifecycle/reconnect gate. Slice I is active at I3 from
> [its detailed plan](2026-07-25-modern-runtime-slice-i.md).
**Spec:** `docs/superpowers/specs/2026-07-05-modern-pipeline-design.md` (the
@ -1681,11 +1685,11 @@ hitch and follows.
| MP1a | `AcDream.Content` extraction (GL-free MeshExtractor + boundary records out of App) | ✅ SHIPPED 2026-07-05 — user-gated (renders identical, zero tripwires, perf-neutral); 8 commits `651d041e`..`b0758d77` |
| MP1b | Pak format + `acdream-bake` CLI + mmap `PakReader` + equivalence/full-scale gate | ✅ **SHIPPED 2026-07-24** — 729,888 EnvCell keys → 17,117 unique geometries + 712,771 aliases (42.6×), 751,141 total keys / 38,370 physical blobs, zero failures, 28,192.4 MiB, 81.4 s validated atomic publish; full-DAT gate also corrected a real collision in WB's legacy geometry hash. [Report](../research/2026-07-24-slice-b-full-bake-report.md) |
| **MP-Alloc (safe batch)** | Reuse per-frame buffers: anim pose, particle draw-list, interior partition, animatedIds/drawableCells sets | ✅ SHIPPED + USER-GATED 2026-07-05 — dense-town frame-time spikes 2087ms → 610ms, alloc spikes (3075MB single-frame) eliminated, gen2 GC 511/window → ~0; user confirms FPS steady. 4 commits `b8c05e2b`..`91afea24` |
| MP-Alloc (hard sites) | EnvCell settled-camera rebuild gate + physics `Transition` reuse | 🟡 Physics half SHIPPED 2026-07-25 — retail-shaped reset-complete transition/query scratch is bit-identical and 0 B/resolve across five profiles; flat collision storage continues in Slice I2. |
| MP-Alloc (hard sites) | EnvCell settled-camera rebuild gate + physics `Transition` reuse | 🟡 Physics half SHIPPED 2026-07-25 — retail-shaped reset-complete transition/query scratch is bit-identical and 0 B/resolve across five profiles; immutable flat collision storage is defined and package integration continues in Slice I3. |
| MP1c | Streaming cutover to pak + hitch gate | ✅ **SHIPPED 2026-07-24** — typed package-only production source, explicit Setup probes, non-allocating Portal-first lookup, exact translucency metadata, and ordered teardown; capped/uncapped/dense physical routes + user visual gate pass. Uncapped CPU p99 7.825→6.496 ms, GPU p99 3.406→2.706 ms, portal frame max 202.9→39.9 MiB, process allocation 41.8%, zero invalid Setup probes. [Report](../research/2026-07-24-slice-c-prepared-asset-cutover-report.md) |
| MP2 | Retail particle distance/cell-view degradation: DAT-authored range, exact infinite/finite off-view update branches, emitter-first render scan, user-requested 2× default range | ✅ SHIPPED 2026-07-17 — Aerlinthe hotspot `0x32000223` resolves to retail 64m and defaults to 128m; landscape/entity streaming distance unchanged |
| MP3 | Arch ECS render world + delta submission (the 300-FPS lever) | ⚪ — note: does NOT fix the steady-state GC churn (that's MP-Alloc); MP3 is the draw-submission throughput lever |
| MP4 | Zero-alloc frame loop + flat physics data (residual, post-MP-Alloc) | 🟡 ACTIVE — Slice I1 zero-allocation transition scratch shipped; I2 immutable flat collision assets in progress. |
| MP4 | Zero-alloc frame loop + flat physics data (residual, post-MP-Alloc) | 🟡 ACTIVE — Slice I1 zero-allocation transition scratch and I2 immutable flat collision schema shipped; I3 package serialization/bake is active. |
| MP5 | Job-system parallelism | ⚪ stretch, evidence-gated |
---

View file

@ -118,10 +118,14 @@ ordinary production profile pass. The final stable production minute sustains
519.7 FPS with CPU/GPU p50 1.869/1.096 ms and 22.34 KiB/frame allocation.
Slice I1 subsequently removes the isolated per-resolve owner: player, remote,
projectile, camera, and grounded walkable-publication profiles are all
0 B/resolve with bit-identical fresh-vs-reused outcomes. The complete I1
Release gate is 3,826 App tests / 3 skips and 8,342 solution tests / 5 skips.
0 B/resolve with bit-identical fresh-vs-reused outcomes. Slice I2 adds
deterministic immutable physics/containment BSP, polygon, Setup, bounds, and
EnvCell-topology records without cutting traversal over. Synthetic corruption
gates and representative installed-DAT source identity pass bit-for-bit. The
complete I2 Release gate is 3,826 App tests / 3 skips and 8,355 solution tests
/ 5 skips.
Slice H is complete: retained UI/frame work, exact bounded light selection,
and pooled/borrowed/direct network I/O pass. Slice I is active at I2 under
and pooled/borrowed/direct network I/O pass. Slice I is active at I3 under
[`2026-07-25-modern-runtime-slice-i.md`](2026-07-25-modern-runtime-slice-i.md).
The historical exact G4 visual rollback remains
`git revert ef1d263337997bb030eadb7b8e71d73dc659907a`.

View file

@ -2,8 +2,8 @@
**Date:** 2026-07-24
**Status:** Slices AH and I0I1 are complete. Slices FL were explicitly
approved 2026-07-24; Slice I2 is active.
**Status:** Slices AH and I0I2 are complete. Slices FL were explicitly
approved 2026-07-24; Slice I3 is active.
**Scope:** Reconcile and sequence the existing Modern Pipeline (`MP`) and
Linux/headless (`LH`) tracks using the 2026-07-24 connected performance audit.
@ -1197,12 +1197,18 @@ Frame-thread allocation at the G5 checkpoint was 22.34 KiB median. I1 has now
replaced fresh per-resolve transition graphs and query temporaries with
reset-complete retained scratch: player, remote, projectile, camera, and
grounded walkable-publication profiles each measure 0 B/resolve, while
fresh-vs-reused output and body state remain bit-identical. I2 immutable flat
collision assets are the current execution point. Evidence:
fresh-vs-reused output and body state remain bit-identical. I2 now supplies
validated immutable flat collision records: synthetic and installed-DAT
source identity, exact float bits, deterministic pre-order, and corruption
tripwires pass without changing production traversal. I3 package
serialization and complete-catalog baking are the current execution point.
Evidence:
[`../research/2026-07-25-slice-g5-production-profile.md`](../research/2026-07-25-slice-g5-production-profile.md)
and
[`../research/2026-07-25-slice-i1-transition-scratch.md`](../research/2026-07-25-slice-i1-transition-scratch.md)
and
[`../research/2026-07-25-slice-i2-flat-collision-schema.md`](../research/2026-07-25-slice-i2-flat-collision-schema.md)
and
[`2026-07-25-modern-runtime-slice-i.md`](2026-07-25-modern-runtime-slice-i.md).
The intended order is therefore:
@ -1216,7 +1222,7 @@ honest metrics + committed baselines (A — exit criteria block C)
-> incremental render scene (F)
-> delta GPU submission (G)
-> residual frame cleanup (H-a, H-b, H-c)
-> flat collision assets (I — CURRENT at I2)
-> flat collision assets (I — CURRENT at I3)
-> presentation-independent runtime (J — §0.3 authorization)
-> Linux/headless/multi-session (K)
-> evidence-gated GPU jobs (L)

View file

@ -1,7 +1,7 @@
# Modern runtime Slice I — flat collision assets and zero-allocation physics
**Status:** I0I1 COMPLETE — retail oracle, graph fixtures, and
reset-complete zero-allocation transition/query scratch fixed; I2 active
**Status:** I0I2 COMPLETE — retail oracle, zero-allocation transition
scratch, and deterministic immutable collision records fixed; I3 active
**Parent:** `2026-07-24-modern-runtime-architecture.md`, Slice I
**Purpose:** remove parsed DAT object graphs and steady collision allocations
without changing one retail collision decision, float, comparison, or traversal
@ -146,7 +146,12 @@ Issue #237 is closed. Evidence:
6. Keep source graph and flat asset together only in test/shadow fixtures.
Gate: round-trip structural equality over synthetic edge cases and installed
DAT samples; no traversal cutover yet.
DAT samples; no traversal cutover yet. **PASSED 2026-07-25:** 13 focused
structural/installed-DAT tests, the 131-test expanded Slice I oracle, complete
Core/solution Release gates, exact float-bit comparisons, and corruption
tripwires pass. The graph route remains the only executable traversal.
Evidence:
[`../research/2026-07-25-slice-i2-flat-collision-schema.md`](../research/2026-07-25-slice-i2-flat-collision-schema.md).
### I3 — package serialization, bake, and prepared collision source

View file

@ -0,0 +1,104 @@
# Slice I2 — deterministic immutable collision assets
**Status:** COMPLETE
**Parent:** `docs/plans/2026-07-25-modern-runtime-slice-i.md`
**Scope:** Core collision storage and preparation only. Production movement,
BSP traversal, streaming publication, and package format are unchanged.
## Result
Slice I2 defines the immutable representation that will replace parsed
collision DBObj graphs after the later shadow and differential gates:
- physics BSP nodes in deterministic positive-before-negative pre-order;
- explicit signed child indices, where `-1` means absent;
- one ordered leaf polygon-index stream;
- polygon rows with checked vertex ranges and one contiguous vertex stream;
- a separate, smaller cell-containment BSP schema;
- ordered Setup cylinder/sphere rows and verbatim dimensions;
- separate reusable CellStruct geometry and per-EnvCell topology;
- direct prepared indices from portals and BSP leaves to polygon rows;
- optional GfxObj root bounds and visual bounds.
All collections are immutable arrays. Per-EnvCell transforms remain
publication state and are deliberately absent from reusable CellStruct
geometry.
## Retail and source ordering
The storage contract is pinned to the existing retail traversal oracle:
- `BSPTREE::find_collisions` `0x0053A440`
- `BSPNODE::find_walkable` `0x0053CC80`
- `BSPLEAF::find_walkable` `0x0053D6F0`
The source graph is flattened iteratively. A stack pushes the negative child
before the positive child, so the output records the current node, then its
complete positive subtree, then its complete negative subtree. Leaf polygon
references stay in their stored order. Polygon-table rows are sorted by ID,
which makes output independent of dictionary insertion and bucket order;
leaves and portals resolve IDs to direct indices during preparation.
No plane is reconstructed and no vector is normalized. `Plane`, `Vector3`,
sphere, cylinder, Setup-dimension, and visual-bound floats are copied directly.
Tests compare all components through `BitConverter.SingleToInt32Bits`.
## Corruption contracts
Construction rejects:
- unsupported BSP node types or child presence inconsistent with the encoded
node type;
- source cycles and shared child nodes;
- flat cycles, shared children, unreachable nodes, and non-pre-order layouts;
- child, polygon-index, polygon, or vertex ranges outside their arrays;
- missing leaf or portal polygon IDs;
- duplicate/unsorted polygon IDs or visible-cell IDs;
- polygon key/row-ID disagreement or point-count disagreement;
- default immutable arrays and null source rows.
An empty tree uses root index `-1`. A populated deterministic tree always uses
root index `0`.
## Evidence
The synthetic suite covers:
- positive/negative child ordering;
- multi-polygon leaf order;
- dictionary-order independence;
- negative-zero and nontrivial float payload preservation;
- source mutation after preparation;
- missing IDs, invalid ranges, malformed child patterns, cycles, shared
children, and unreachable/non-pre-order shapes;
- asymmetric containment trees;
- a 20,000-node containment tree without call-stack recursion;
- Setup row order and exact dimensions;
- portal direct-index resolution and deterministic visible-cell ordering.
Installed retail DAT coverage:
- Facility Hub cells `0x8A02016E` and `0x8A02017A`;
- Holtburg cottage cell `0xA9B4013F`;
- cottage, stair/ramp, and multipart-door GfxObjs `0x01000A2B`,
`0x01000AC5`, and `0x010044B5`;
- player, wide-low collision-family, and multipart-door Setups `0x02000001`,
`0x020019E3`, and `0x020019FF`.
For every installed sample, tests compare source node type, plane bits,
positive/negative child identity, leaf index, solid marker, bounds, polygon
order, polygon metadata, vertex bits, Setup shapes, dimensions, and visual
bounds.
Release gates:
- I2 focused structural/installed-DAT tests: **13 passed**
- expanded Slice I graph + flat oracle: **131 passed**
- complete Core tests: **3,263 passed / 2 skipped**
- `dotnet build AcDream.slnx -c Release --no-restore`: **passed**
- complete solution tests: **8,355 passed / 5 skipped**
There is intentionally no traversal cutover and no new retail divergence in
this commit. Slice I3 serializes these records into the package, adds typed
prepared collision access, and bakes the complete installed catalog before any
flat query can affect gameplay.