Introduce explicit loaded, missing, and corrupt outcomes over typed pak keys, validate package/DAT identity at open, add zero-I/O TOC probes, preserve cancellation, and route the package path through RuntimeOptions. Production injection follows in the next checkpoint.
An adversarial performance review found our own instruments cannot
measure the project's own performance gates:
- FrameProfiler aggregated CPU/GPU/alloc/stage samples into ~5-second
windows and reset the ring buffers after each report, so route-wide
p50/p95/p99 distributions across a whole soak could not be
reconstructed after the fact. ACDREAM_FRAME_HISTORY=<path> now opts
into a separate per-frame history (one record per frame, ~72
bytes/record, accumulated in memory with zero frame-thread I/O) that
a shutdown-only Dispose() writes as CSV. The aggregated [frame-prof]
report format and its existing metrics are unchanged.
- The canonical checkpoint JSON tracked cache residency (entry/byte
counts) but never LOH size/fragmentation, process-wide allocated
bytes, or cache hit/miss/eviction traffic — a committed audit JSON
showed 65% LOH fragmentation that no tracked instrument recorded,
and "does a revisit portal hit or miss the caches" was unanswerable
from an artifact alone. WorldLifecycleResourceSnapshot now carries
loh_size_bytes/loh_fragmentation_bytes (GCMemoryInfo.GenerationInfo
index 3), process_total_allocated_bytes (GC.GetTotalAllocatedBytes),
and Interlocked hit/miss/eviction counters for the CPU mesh cache,
decoded-texture cache, and the four bounded DAT-object caches
(portal/cell/highRes/language, aggregated).
- run-connected-r6-soak.ps1 unconditionally forced
ACDREAM_UNCAPPED_RENDER=1 with no capped mode, while its sibling
lifecycle-gate script correctly gated it behind a switch. Added
-Uncapped (default capped, matching the sibling script's pattern),
fixed the stationary dwell (12s -> 26s, past the 25s
LiveEntityLivenessController deadline the adjacent comment already
cited), and now write an env-disclosure.json into the automation
artifact directory before every launch listing every ACDREAM_* var
the script sets plus -Uncapped, since the prior audit could only see
ACDREAM_DUMP_MOVE_TRUTH and nothing else was ever recorded anywhere.
Cache counters are wired via the existing composition path
(ObjectMeshManager already owns the CPU mesh cache and the mesh
extractor directly; content.Dats is threaded into
WorldLifecycleResourceSnapshotSource the same way every other
composition consumer receives it). The DAT-object cache lives behind
IDatReaderWriter, a third-party interface from the DatReaderWriter
package that cannot be extended; RuntimeDatCollection (the one
production implementation) exposes the aggregate stats directly and a
pattern match reads them, degrading to zero for any test double —
no new static registry was introduced (GpuMemoryTracker remains the
one precedented process-wide static).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 1da2c33c875b41fa383dd79694ee2765f0e21896)
Three small verified MeshExtractor.cs fixes from an allocation audit:
- CellStruct clip-map clone guard: the CellStruct polygon-surface path
cloned decoded texture data unconditionally before applying clip-map
transparency, even though the comment above it says "if we got this
from the cache, we need to clone it." The GfxObj path already had the
correct pattern (clone only when textureDataIsCached). Wired the same
textureDataIsCached flag through CellStruct's TryGet/GetOrCreate/
RetainOrUse call sites and gated the clone on it, matching GfxObj
exactly — freshly-decoded arrays (the common case) now skip a
redundant clone + BlockCopy.
- Solid-color texture cache: isSolid surfaces (Base1Solid / NoPos-
stippled polys) allocated a fresh 32x32 RGBA array (4 KiB) on every
polygon, even for repeated colors. Added a MeshExtractor-scoped
ConcurrentDictionary<uint, byte[]> keyed by packed ARGB, bounded by
distinct colors observed (a few hundred at most) rather than call
volume. Traced every downstream consumer of the solid-color array
(clip-map clone-guard, translucency clone-guard, GL upload, pak
serialization) — none currently mutate a solid-color array in place,
since isSolid is mutually exclusive with the texture-decode branch
those guards live in — but wired textureDataIsCached = true at both
call sites anyway so the existing clone-before-mutate machinery
protects the shared array if that ever changes.
- Dropped the redundant .ToList() on both _dats.ResolveId(...) calls:
DatCollectionAdapter.ResolveId already returns a materialized List<T>
typed as IEnumerable; the immediate .OrderByDescending().FirstOrDefault()
enumerates once, so the extra copy was pure waste.
Added SolidColorTextureCacheTests (dat-gated, matching suite convention)
exercising the new cache directly via internal visibility.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit e9f9d7539966cc26343e45d653a751c5d1045810)
Replace the incomplete package path with one DatCollection-backed compatibility seam for PhysicsScripts and Animations. Preserve CreateBlockingParticle's inherited payload and following cursor, route every production and audit consumer through the corrected loaders, and apply retail's post-UnPack StartTime ordering.
Add exact stored-order PhysicsScriptTable upper-threshold resolution, high-byte DID and embedded-ID validation, plus live effect profiles with Setup-to-PhysicsDesc precedence across top-level and attached entity lifetimes. Keep blocking execution deferred and narrow TS-11 accordingly.
Pin synthetic malformed/cursor/order fixtures, installed-DAT blocking and recall audits, high-index IDs, IEEE boundaries, profile teardown, and ordinary decoder parity; synchronize architecture, inventory, milestones, roadmap, research, and memory.
Co-Authored-By: Codex <noreply@openai.com>
Coordinator-directed final cleanup before the user gate; none behavioral:
1. MeshExtractor public surface narrowed to the cross-assembly entry
points App actually calls (PrepareMeshData, PrepareCellStructMeshData,
CollectParts, ComputeBounds); PrepareSetupMeshData,
CollectEmittersFromScript, PrepareGfxObjMeshData,
PrepareEnvCellMeshData, PrepareCellStructEdgeLineData back to private
(internal dispatch, only reached via PrepareMeshData).
2. sideStagedSink constructor parameter is now REQUIRED (no default;
type stays nullable for a conscious null): a bake tool that forgot
the sink would silently lose particle-preload meshes.
3. AcDream.Content.csproj gains TreatWarningsAsErrors + LangVersion
latest (parity with AcDream.Core.csproj). Surfaced zero warnings.
4. Dead usings removed from ObjectMeshManager.cs (BCnEncoder.*,
SixLabors.*) — the inline decode moved out in Task 4.
5. Doc fixes: ObjectMeshData.cs cross-assembly <see cref> ->
plain text (Content can't resolve App types); IDatReaderWriter.cs
stale Phase O-T7 'both in this namespace' sentence rewritten.
6. Stale test doc comments updated to MeshExtractor.PrepareGfxObjMeshData
(StipplingSurfaceEquivalenceTests, Issue119UpNullGfxObjDumpTests) —
comments only, no code/assertion changes.
dotnet build green (0 warnings in Content under warnings-as-errors);
full test suite 4059 passed / 0 failed / 4 skipped.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Coordinator-directed follow-up. The buffer-and-drain seam diverged from
the original on the exception path: pre-MP1a, CollectEmittersFromScript
enqueued particle-preload meshes DIRECTLY into _stagedMeshData
mid-Prepare, so preloads staged before a later throw in the same
Prepare* call (reachable via PrepareEnvCellMeshData side-staging during
its StaticObjects loop, then PrepareCellStructMeshData throwing on a
malformed-dat texture decode) were already safely enqueued. The drain
version only flushed after a successful return — on throw, entries
stranded on the shared extractor until an unrelated successful call
flushed them, and were silently dropped on dispose.
Fix: MeshExtractor takes an Action<ObjectMeshData>? sideStagedSink
constructor parameter; the two CollectEmittersFromScript sites become
_sideStagedSink?.Invoke(meshData) — the original code shape (immediate
hand-off) at those exact lines. ObjectMeshManager wires the sink to
_stagedMeshData.Enqueue, restoring the original immediate-enqueue
semantics including on mid-Prepare throw. _sideStaged buffer,
DrainSideStaged(), and the ProcessQueueAsync drain loop are deleted.
The MP1b bake tool passes its own collector.
Inventory doc updated: MP1a note now records the sink seam and the
Content-owned upload enums, so its no-behavior-change claim is accurate.
dotnet build green; full test suite 4059 passed / 0 failed / 4 skipped.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Coordinator-directed follow-up: AcDream.Content must stay Silk.NET-free
(the MP1b bake tool must not ship GL binaries). The Silk.NET.OpenGL
PackageReference added for the PixelFormat?/PixelType? upload hints is
replaced by Content-owned UploadPixelFormat/UploadPixelType enums
(UploadFormats.cs) whose underlying values are the GL ABI constants
(Rgba = 0x1908, UnsignedByte = 0x1401), verified numerically identical
to the Silk.NET.OpenGL members against 2.23.0. This is the one
sanctioned edit to the verbatim-moved Prepare* bodies: enum literal for
enum literal, numeric value identical, behavior unchanged. App casts at
the single upload boundary (AddTexture call in UploadGfxObjMeshData)
via lifted nullable enum conversion — value- and null-preserving.
Also hardens the MP1a _sideStaged hand-off seam: List -> ConcurrentQueue.
One MeshExtractor is shared by up to MaxParallelLoads (4) decode workers;
the original code enqueued to the thread-safe _stagedMeshData directly,
so the hand-off buffer must be thread-safe too. Drain ordering verified:
side-staged entries enqueue BEFORE the top-level result, preserving the
original mid-Prepare FIFO order.
Verified: grep -i silk on the csproj -> no matches; deps.json has zero
Silk entries; dotnet build 0 errors; full test suite green (4059 passed).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>