From cccae4169aff23e73ac9c50b073021d287af5740 Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 27 Aug 2026 20:29:50 +0200 Subject: [PATCH] Document PAK v2 resource closeout --- README.md | 16 ++--- docs/architecture/acdream-architecture.md | 9 +++ docs/architecture/worldbuilder-inventory.md | 32 ++++++--- .../2026-08-27-pak-v2-resource-campaign.md | 70 +++++++++++++++---- src/AcDream.Content/Pak/PakFormat.cs | 3 +- 5 files changed, 98 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index d9b668c2..b7e9b5fa 100644 --- a/README.md +++ b/README.md @@ -97,8 +97,8 @@ missing. - A machine-local `acdream.pak` built from those DATs - A running ACE server for connected play; the examples use `127.0.0.1:9000` -- For the graphical client, a driver exposing the mandatory modern OpenGL - capabilities +- For the graphical client, a driver exposing the mandatory Vulkan + capabilities validated at startup The project does not distribute Microsoft/Turbine DAT files or derived prepared packages. @@ -111,10 +111,9 @@ dotnet build AcDream.slnx -c Release dotnet test AcDream.slnx -c Release --no-build ``` -The current baseline is a successful Release build with **8,826 passing tests -and 5 intentional skips**. The build currently reports 17 test-project -warnings tracked by [`#228`](docs/ISSUES.md#228--clean-release-build-emits-17-test-project-warnings); -production compilation has zero errors. +The current CI-filtered Windows baseline is a successful Release build with +**16,151 passing tests and zero failures**; opt-in live, installed-DAT, +prepared-package, manual, timing, and platform-specific lanes run separately. ## Prepare content @@ -127,8 +126,9 @@ dotnet run --project src\AcDream.Bake\AcDream.Bake.csproj -c Release -- ` --out "C:\Games\Asheron's Call\acdream.pak" ``` -A complete package is approximately 30 GB. It is machine-local and must not be -committed. `ACDREAM_PAK_PATH` overrides the default +A complete format-2 package from the standard installed DAT set is about +570 MiB (the former format-1 package was about 30 GB). It is machine-local and +must not be committed. `ACDREAM_PAK_PATH` overrides the default `\acdream.pak`. ## Run the graphical client diff --git a/docs/architecture/acdream-architecture.md b/docs/architecture/acdream-architecture.md index f7bb3adb..544d92d3 100644 --- a/docs/architecture/acdream-architecture.md +++ b/docs/architecture/acdream-architecture.md @@ -450,6 +450,15 @@ src/ Prepared-content launch contract -> `install.json` remains the strict backward-compatible base-pak authority + -> format 2 / recipe 6 is the current full-package contract: globally + shared texture payloads, independent adaptive blob compression, and + source-native BC1/2/3 for surfaces that require no pixel-local edit + -> format changes always use an explicit confirmed full rebuild; the + launcher preflights 2 GiB free space, shows long-work progress, validates + the candidate beside the active package, and promotes atomically + -> the prepared texture LRU is bounded to 64 MiB / 1,024 entries; GPU atlas + ownership remains in App and the whole-file mmap is virtual, not a + committed-copy cache -> optional `pak/content.current.json` binds one cumulative overlay to the base SHA; there is never an unbounded overlay chain -> `pak/content.client-pending` prevents newly migrated content from diff --git a/docs/architecture/worldbuilder-inventory.md b/docs/architecture/worldbuilder-inventory.md index f72fc1ad..b3d5ec59 100644 --- a/docs/architecture/worldbuilder-inventory.md +++ b/docs/architecture/worldbuilder-inventory.md @@ -22,7 +22,7 @@ particles), and uses the same Silk.NET + .NET stack we target. our tree (see CLAUDE.md for the full breakdown): - `src/AcDream.Core/Rendering/Wb/` — pure helpers (no GL): `TerrainUtils`, `TerrainEntry`, `RegionInfo`, `SceneryHelpers`, `TextureHelpers`. -- `src/AcDream.App/Rendering/Wb/` — GL infrastructure + mesh pipeline: +- `src/AcDream.App/Rendering/Wb/` — Vulkan/RHI infrastructure + mesh pipeline: `ObjectMeshManager`, `WbMeshAdapter`, `WbDrawDispatcher`, texture cache, shader infra, EnvCell/portal/scenery/terrain-blending pipeline classes. @@ -42,7 +42,7 @@ non-render content plus explicit bake/equivalence tooling. **MP1a (2026-07-05): CPU mesh-extraction half moved to `AcDream.Content`.** The GL-free portion of the former `ObjectMeshManager` — dat read → polygon -walk → vertex/index build → inline BCn/palette texture decode → +walk → vertex/index build → palette/conditional BCn texture decode → `ObjectMeshData` — is now `MeshExtractor` in a new `src/AcDream.Content/` assembly (no Silk.NET dependency), so the MP1b bake tool can run the exact same extraction code offline without an OpenGL context. This was a @@ -84,7 +84,7 @@ behavior change, no divergence-register row. survive, as they always did). The MP1b bake tool passes its own collector. - **Stays in `src/AcDream.App/Rendering/Wb/`:** `ObjectMeshManager` (the - staged-queue/worker-pool/Dispose-quiesce lifecycle and all GL upload; + staged-queue/worker-pool/Dispose-quiesce lifecycle and all Vulkan/RHI upload; production workers now consume `IPreparedAssetSource`), `ObjectRenderData`/`ObjectRenderBatch` (hold a GL `TextureAtlasManager` field), `TextureAtlasManager`, @@ -120,9 +120,8 @@ EnvCell, Surface, palette, and texture graphs during portals. The validated machine-local `acdream.pak` is opened through Content's `IPreparedAssetSource`; typed GfxObj and EnvCell requests deserialize immutable `ObjectMeshData` while retaining the existing App worker, staging, render-thread -upload, cache, ownership, and shutdown contracts. The original -format-1/bake-tool-3 render payload persists exact batch translucency so App -does not reconstruct a +upload, cache, ownership, and shutdown contracts. The prepared render payload +persists exact batch translucency so App does not reconstruct a `GfxObjMesh` for metadata. Setup activation uses the package TOC as an explicit type-presence index before reading valid Setup records through the bounded DAT cache. `DatPreparedAssetSource` and `MeshExtractor` remain explicit @@ -146,8 +145,25 @@ keeps either result non-launchable until the matching client is confirmed, including across a crash/restart. Design and gates: `docs/plans/2026-08-25-launcher-content-stabilization.md`. -**Slice I3 prepared collision extension (2026-07-25).** The package remains -format 1 and retains mesh type values 1–3; bake-tool 4 appends typed GfxObj, +**PAK v2 resource closeout (2026-08-27).** Format 2 / bake recipe 6 retains +the fixed header and sorted random-access TOC while moving texture arrays into +one globally shared type-8 payload partition and independently applying +adaptive Brotli compression with raw fallback. Unedited DXT1/3/5 surfaces +retain exact DAT BC blocks through Vulkan upload; clip maps and authored +translucency continue through the RGBA edit path. The reader bounds decoded +blobs and retains shared texture arrays in a 64 MiB / 1,024-entry LRU. The +complete installed package is 597,229,424 bytes versus 29,908,271,024 bytes +for format 1; four- and nine-worker bakes have identical SHA-256. The +authoritative connected before/after route reduced heavy-route working set by +48.0%, private bytes by 26.1%, and prepared-mesh GPU bytes by 25.9% with equal +or faster matching reveal/frame percentiles. Format changes remain mandatory +launcher-confirmed full rebuilds with beside-active validation and atomic +promotion; overlays never cross a format boundary. Design and evidence: +`docs/plans/2026-08-27-pak-v2-resource-campaign.md`. + +**Slice I3 prepared collision extension (2026-07-25).** At its introduction, +the package remained format 1 and retained mesh type values 1–3; bake-tool 4 +appended typed GfxObj, Setup, CellStruct, and EnvCell-topology collision payloads. Core owns the immutable flat records and deterministic raw-DAT flattener. Content owns the strict little-endian codec and `IPreparedCollisionSource`. diff --git a/docs/plans/2026-08-27-pak-v2-resource-campaign.md b/docs/plans/2026-08-27-pak-v2-resource-campaign.md index c4b08dd0..1e172bc1 100644 --- a/docs/plans/2026-08-27-pak-v2-resource-campaign.md +++ b/docs/plans/2026-08-27-pak-v2-resource-campaign.md @@ -11,8 +11,9 @@ Ship one crash-safe prepared-asset format migration that: - preserves decoded geometry, material metadata, texture bytes, deterministic baking, corruption isolation, and random-access loading; - does not regress cold or warm world-reveal latency or frame-time percentiles; -- reduces bake CPU/memory and client retained texture memory where the data - permits it, without changing the rendered result; +- reduces offline bake time and live client CPU/GPU memory where the data + permits it, without changing the rendered result; the one-time bake remains + bounded for the user-confirmed 16-32 GiB target machines; - gives launcher users a clear one-time update message and progress, while retaining the last verified package until the replacement is validated; - passes two different worker-count bakes with identical SHA-256, the complete @@ -51,9 +52,9 @@ The 64-byte header and 24-byte sorted TOC row remain fixed. Format version is their own exact dimensions, format, upload metadata, surface identity, translucency, culling, and index data. 2. Texture payload keys are the first 56 bits of SHA-256 under the type-8 - namespace. The writer retains and compares the full bytes for the complete - bake, making even a truncated-hash collision a loud bake failure rather - than silent substitution. + namespace. The writer retains the full SHA-256 digest and length for every + unique texture, making even a truncated-key collision a loud bake failure + rather than silent substitution without pinning another copy of all bytes. 3. Every physical blob is independently encoded. The high bit of the TOC length marks compression; the low 31 bits are the stored length. A compressed blob contains a four-byte decoded-length prefix followed by @@ -61,10 +62,15 @@ The 64-byte header and 24-byte sorted TOC row remain fixed. Format version is CRC-32 covers stored bytes, then decompression is independently validated. 4. Random access remains one binary search plus one mmap copy for raw blobs. Compressed blobs add decompression only when the writer proved a material - size win. Texture payloads use a bounded, thread-safe 256 MiB / 2,048-entry + size win. Texture payloads use a bounded, thread-safe 64 MiB / 1,024-entry LRU; concurrently decoded meshes converge on one shared array instance. 5. Whole-file compression is forbidden. It would destroy random access and make a small world reveal depend on unrelated content. +6. Unedited DAT DXT1/3/5 surfaces retain their exact BC1/2/3 source blocks + through bake, mmap, and Vulkan upload. Clip maps and surfaces with authored + translucency still decode to RGBA8 because their per-surface alpha edits + require pixels. This is smaller and closer to retail's hardware DXT path + than the former unconditional software decode. ## Determinism and publication @@ -79,7 +85,7 @@ DAT iterations, TOC counts, size, completion protocol, and SHA-256, then uses the existing atomic promotion/backup transaction. Cancellation or failure keeps the verified format-1 package. No overlay may cross this format change. -## Checkpoint evidence +## Complete installed-DAT evidence The first installed-DAT mixed sample (four GfxObj, three Setup, three EnvCell, all corresponding collision/topology payloads) produced 58 keys, 29 globally @@ -88,9 +94,42 @@ deduplicated texture payloads, and 57 physical blobs. Decoded payload was and three-worker bakes had the identical SHA-256 `78886DFA28A3EDF9368A1E25C9B02A3B69ADC5DFCC01358D64A073B549B5B532`. -This sample is a checkpoint only. The size and performance release gates are -decided by the complete installed-DAT package and connected world-loading -measurements. +The final complete four-worker and nine-worker bakes are byte-identical: + +- 2,237,866 logical keys and 786,892 physical blobs; +- 5,696 globally shared texture payloads; +- zero extraction/validation failures; +- 597,229,424 bytes (569.6 MiB), down 98.0% from 29,908,271,024 bytes; +- SHA-256 + `37BC0EA1778F899AF9E3B2397937D373F69D615B15D3E32041BF389D93B624BF`; +- 28.7 seconds for the final four-worker bake versus 79.1 seconds for the + same-machine recipe-5 format-1 baseline (63.7% faster). + +The exact before/after connected lifecycle route used the same machine, +server, 1280x720 Vulkan presentation, retail render pack, and matching client +code. Both runs passed fresh login, multi-world portal travel, same-location +revisit, fresh-process reconnect, and graceful teardown. + +| Matching live measurement | Format 1 | Format 2 | Change | +|---|---:|---:|---:| +| Heavy-route final working set | 2,621.9 MiB | 1,363.9 MiB | -48.0% | +| Heavy-route final private bytes | 2,470.5 MiB | 1,825.6 MiB | -26.1% | +| Holtburg prepared-mesh GPU bytes | 229.6 MiB | 170.1 MiB | -25.9% | +| Fresh reconnect working set | 974.1 MiB | 869.0 MiB | -10.8% | +| Fresh reconnect private bytes | 1,253.8 MiB | 1,166.6 MiB | -7.0% | +| Fresh reconnect to checkpoint | 59.3 s | 59.3 s | equal | + +Rynthid and Facility transition times were equal; Holtburg completed 2.9 s +faster; Aerlinthe revisit differed by 0.1 s. Matching stable CPU p50/p95 rows +were equal or slightly faster and GPU time remained within 0.1 ms. The first +raw login samples were excluded from comparison because the server started +them in different cells with 6,671 versus 11,799 world entities. Matching +screenshots preserve geometry, materials, lighting, transparency, texture +detail, and mip behavior. + +Automated gates at this checkpoint: 181/181 installed-DAT Content tests, +16,151/16,151 CI-filtered Windows tests, Release build with zero warnings, and +the authoritative connected lifecycle/reconnect gate all pass. ## Work ledger @@ -100,10 +139,11 @@ measurements. determinism. - [x] Integrate format-2 accounting and strict validation into the bake. - [x] Publish the recipe-6 mandatory full-rebuild launcher migration. -- [ ] Add launcher disk-space preflight and explicit long-work detail. -- [ ] Complete installed-DAT equivalence and dual-worker full bakes. -- [ ] Measure/tune package size, bake CPU/memory, read CPU/allocations, cold +- [x] Add launcher disk-space preflight and explicit long-work detail. +- [x] Complete installed-DAT equivalence and dual-worker full bakes. +- [x] Measure/tune package size, bake time, read CPU/allocations, cold and warm reveal latency, and frame-time percentiles. -- [ ] Evaluate source-native BC texture retention only if it remains visually +- [x] Evaluate source-native BC texture retention only if it remains visually exact and does not shift mip-generation work into the reveal frame. -- [ ] Pass complete tests, Windows CI, merge, push, and release gates. +- [x] Pass complete local tests and authoritative connected gates. +- [ ] Pass Gitea Windows CI, merge, push, and release gates. diff --git a/src/AcDream.Content/Pak/PakFormat.cs b/src/AcDream.Content/Pak/PakFormat.cs index 2256ced5..68898031 100644 --- a/src/AcDream.Content/Pak/PakFormat.cs +++ b/src/AcDream.Content/Pak/PakFormat.cs @@ -30,7 +30,8 @@ public static class PakFormat { /// 8 polygons all NoPos + Base1Solid, extracted to a 0-vertex mesh). The /// binary format remains version 1. Version 6 introduces pak format 2: /// globally shared texture payloads plus independently Brotli-compressed - /// blobs with raw fallback. Mesh geometry and decoded pixels remain exact. + /// blobs with raw fallback. Mesh geometry and source texture bytes remain + /// exact; unedited DXT surfaces now retain their native BC blocks. /// public const uint CurrentBakeToolVersion = 6; }