167 lines
8.1 KiB
Markdown
167 lines
8.1 KiB
Markdown
# Modern Runtime Slice B — EnvCell geometry deduplication and full bake
|
||
|
||
**Date:** 2026-07-24
|
||
**Status:** Complete 2026-07-24; full installed-DAT gate passed
|
||
**Parent:** [`2026-07-24-modern-runtime-architecture.md`](2026-07-24-modern-runtime-architecture.md), Slice B
|
||
**Prior implementation:** [`../superpowers/plans/2026-07-05-mp1b-pak-and-bake.md`](../superpowers/plans/2026-07-05-mp1b-pak-and-bake.md)
|
||
|
||
## Outcome
|
||
|
||
Complete the existing MP1b bake without changing runtime behavior. Every
|
||
EnvCell file ID remains an independently addressable pak key, while cells with
|
||
the same collision-resistant geometry identity share one serialized blob and one
|
||
physical byte range. The client does not consume the pak in this slice.
|
||
|
||
The full gate must replace the failed 865 GB / 52 minute per-cell bake with a
|
||
bounded artifact whose size and time scale with unique geometry, not with all
|
||
729,888 cell instances.
|
||
|
||
## Fixed contracts
|
||
|
||
1. `DatCollection` remains the only DAT reader.
|
||
2. `MeshExtractor` remains the only mesh/texture interpreter used by both the
|
||
live path and the bake.
|
||
3. Geometry identity is one deterministic hash of the complete typed tuple:
|
||
`environmentId`, `cellStructure`, surface count, then the ordered surface
|
||
IDs. App and Bake call the same Core implementation.
|
||
4. The pure identity helper lives in
|
||
`AcDream.Core.Rendering.Wb.EnvCellGeometryIdentity`. It uses FNV-1a in a
|
||
dedicated high-bit namespace and retains bit 33 for compatibility with
|
||
existing synthetic-geometry diagnostics. WorldBuilder's original
|
||
`hash = hash * 31 + value` calculation remains as a conformance helper only.
|
||
5. Every EnvCell TOC key is still
|
||
`PakKey.Compose(PakAssetType.EnvCellMesh, fileId)`.
|
||
6. Aliases share `offset`, `length`, and `crc32`. The pak format and reader do
|
||
not gain an indirection table or a second lookup.
|
||
7. The shared payload carries the runtime geometry ID in
|
||
`ObjectMeshData.ObjectId`, matching the current live
|
||
`PrepareCellStructMeshData(geometryId, ...)` path.
|
||
8. A geometry-ID collision whose full source tuple differs fails loudly. It
|
||
may never alias silently.
|
||
9. Blob and TOC order remain deterministic across worker counts.
|
||
10. The destination pak is replaced only after a complete temporary artifact
|
||
opens and validates. Cancellation or failure preserves the prior file and
|
||
deletes the temporary file.
|
||
11. The pak itself and raw bake logs are machine artifacts and are not
|
||
committed. A compact, path-free bake report is committed.
|
||
|
||
**Full-DAT correction (2026-07-24):** the first guarded full-catalog run proved
|
||
that the original fixed contract was impossible. Installed retail DAT cells
|
||
`0x00030175` (`environment=0x277`, structure `0`, surfaces `0x013B,0x0034`)
|
||
and `0x01BC0105` (`environment=0x276`, structure `0`, surfaces
|
||
`0x04FC,0x0034`) both produce WorldBuilder ID `0x00000002020E8C13` despite
|
||
having visibly different geometry. The difference is algebraic:
|
||
one environment step contributes `31³`, exactly canceled by the first-surface
|
||
difference `0x04FC - 0x013B = 31²`. The collision guard stopped the bake before
|
||
writing an artifact. B1 was therefore corrected at the root: the old polynomial
|
||
is retained and tested as historical evidence, while runtime and bake now share
|
||
the stronger namespaced identity. The full-catalog collision gate remains
|
||
mandatory.
|
||
|
||
## Implementation checkpoints
|
||
|
||
### B1 — One geometry identity seam
|
||
|
||
- Extract one pure identity calculation from the App transaction builder into
|
||
`AcDream.Core.Rendering.Wb.EnvCellGeometryIdentity`.
|
||
- Retain the existing App entry points as delegates so production behavior and
|
||
callers do not change.
|
||
- Add Core conformance tests for empty surfaces, ordered surfaces, the
|
||
dedicated namespace, App/Bake shared values, and the installed-DAT collision
|
||
in the legacy WorldBuilder polynomial.
|
||
|
||
**Gate:** existing App geometry-ID tests and new Core tests pass with the exact
|
||
same values.
|
||
|
||
### B2 — Pak blob aliases
|
||
|
||
- Make `PakWriter` retain each written key's immutable TOC receipt.
|
||
- Add `AddAlias(aliasKey, existingKey)`, rejecting a duplicate alias key,
|
||
missing source key, alias after finish, and accidental self/duplicate use.
|
||
- Emit a normal TOC row with the alias key and the source receipt's physical
|
||
range and CRC.
|
||
- Keep the reader unchanged except for test-only receipt inspection.
|
||
|
||
**Gate:** primary and alias deserialize field-for-field equal, have identical
|
||
offset/length/CRC, use one physical blob, preserve sorted TOC order, and isolate
|
||
corruption consistently.
|
||
|
||
### B3 — Deduplicated bake catalog
|
||
|
||
- Enumerate EnvCell file IDs exactly as today.
|
||
- Read each EnvCell header once and produce:
|
||
- a sorted `fileId -> geometryId` alias stream;
|
||
- one full source descriptor per unique geometry ID.
|
||
- Validate equal geometry IDs against the complete tuple
|
||
`(environmentId, cellStructure, ordered surfaces)`.
|
||
- Extract only the unique descriptors through
|
||
`MeshExtractor.PrepareCellStructMeshData(geometryId, ...)`.
|
||
- Write the first sorted cell key as the physical blob and every remaining
|
||
cell key as an alias.
|
||
- Keep GfxObj, Setup, and side-staged particle-preload assets in the same
|
||
deterministic writer transaction.
|
||
- Report enumerated cells, valid aliases, unique geometries, dedup ratio,
|
||
physical blobs, failures, wall time, output bytes, and peak working set.
|
||
|
||
**Gate:** a filtered real-DAT fixture containing duplicate cells produces more
|
||
EnvCell keys than physical EnvCell blobs, and every key reads the same payload
|
||
as a live unique-geometry extraction.
|
||
|
||
### B4 — Transactional output and compatibility
|
||
|
||
- Bake beside the destination under a unique temporary filename.
|
||
- Pass cancellation through enumeration and parallel extraction.
|
||
- Open the completed temporary pak and validate its header, TOC count, source
|
||
DAT iterations, bake-tool version, and every TOC range before replacement.
|
||
- Atomically replace the destination on the same volume.
|
||
- Add stale-version, corruption, cancellation, partial-write, existing-target
|
||
preservation, and temporary-file cleanup tests.
|
||
|
||
**Gate:** no failure path damages or replaces a previously valid pak.
|
||
|
||
### B5 — Full-scale gate
|
||
|
||
- Run Release build and the focused Content/Bake suites.
|
||
- Run two filtered bakes with different thread counts and require byte-identical
|
||
output.
|
||
- Run the complete unfiltered bake against the installed retail DAT files.
|
||
- Open the result, validate every TOC entry, sample aliases across multiple
|
||
landblocks, and run the existing live-vs-pak field equivalence suite.
|
||
- Record counts, dedup ratio, failures, elapsed time, peak working set, and
|
||
artifact size in `docs/research/`.
|
||
- Delete the generated pak after validation unless it is intentionally retained
|
||
outside the repository for the later Slice C cutover.
|
||
|
||
**Exit:** the full bake completes in practical time and disk space, all
|
||
equivalence and determinism gates are green, duplicate EnvCells demonstrably
|
||
share physical offsets, and no runtime code consumes the pak.
|
||
|
||
## Completion record
|
||
|
||
All checkpoints B1-B5 are complete. The full installed-DAT bake produced
|
||
751,141 addressable keys backed by 38,370 physical blobs with zero failures.
|
||
The 729,888 EnvCell keys resolve to 17,117 unique geometries and 712,771 aliases
|
||
(42.6×). The validated package is 28,192.4 MiB and publishes atomically in
|
||
81.4 seconds on the reference machine. A cross-landblock eight-key alias fixture
|
||
is byte-identical with one versus eight workers. Full evidence:
|
||
[`../research/2026-07-24-slice-b-full-bake-report.md`](../research/2026-07-24-slice-b-full-bake-report.md).
|
||
|
||
No runtime code reads the pak in this slice. Slice C remains blocked by the
|
||
physical-local Slice A baseline gate. Release build and the complete solution
|
||
suite pass (8,059 passed, five skipped, zero failed); generated pak artifacts
|
||
were deleted after validation.
|
||
|
||
## Verification
|
||
|
||
Run:
|
||
|
||
```powershell
|
||
dotnet test tests/AcDream.Content.Tests/AcDream.Content.Tests.csproj -c Release
|
||
dotnet test tests/AcDream.Bake.Tests/AcDream.Bake.Tests.csproj -c Release
|
||
dotnet build AcDream.slnx -c Release
|
||
dotnet test AcDream.slnx -c Release
|
||
```
|
||
|
||
Then run the full bake from the Release binary, capture its compact report, and
|
||
validate the output with `PakReader`. No visual gate is required because this
|
||
slice changes no runtime path.
|