Commit graph

6 commits

Author SHA1 Message Date
Erik
dc0468cc2b fix(tests): replace sleep-race concurrency proofs in RetailDatLoaderTests
Two tests proved "these two unrelated DAT reads ran concurrently" by
racing a fixed Thread.Sleep(40) window against .NET thread-pool
scheduling latency for a second Task.Run. Under the CPU contention of
a full `dotnet test AcDream.slnx` run (all 9 test projects' VSTest
hosts launch concurrently) plus a busy machine, thread-pool injection
can occasionally miss the window, making MaxConcurrentReads read 1
instead of 2 and failing the assertion with no underlying code defect.

RetailAnimationLoader and RetailPhysicsScriptLoader both coalesce
same-key reads correctly via ConcurrentDictionary<K, Lazy<T>>.GetOrAdd,
which is atomic and timing-independent (verified by reading, not just
running) - only the test's method of proving cross-key overlap was
timing-fragile. DecodedTextureCacheTests already uses the correct
deterministic-gate pattern; this brings RetailDatLoaderTests in line
with it via a Barrier-backed rendezvous instead of a sleep race.

Filed as #248 (docs/ISSUES.md) with the full attempt matrix: could not
catch the originally-reported AcDream.Content.Tests failure in the act
despite ~72 Content.Tests executions across four contention strategies
over ~30 full-suite-equivalent runs, though the general mechanism
reproduced 3x in AcDream.App.Tests's already-known zero-allocation
flake class (left untouched, out of scope here).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 11:03:03 +02:00
Erik
c7861020e1 test(content): give the loader concurrency tests real concurrency
Fixing the two failures that were stopping portable-headless earlier let
the job reach AcDream.Content.Tests for the first time on either
operating system - the test loop exits on the first failing project, so
the windows leg had never got past the apt step and the ubuntu leg had
never got past Core.Net. Two RetailDatLoaderTests cases were waiting
there, and they failed on both.

Both assert on RawDatabase.MaxConcurrentReads after issuing two Task.Run
reads that each block 40 ms in Thread.Sleep. A pair of pool work items is
not a guarantee of two workers in flight: on a low-core or saturated pool
the second queues behind the first, the reads run back to back,
MaxConcurrentReads stays 1, and the assertion fails for a reason that has
nothing to do with the loader. Pinning the suite to two CPUs on Ubuntu
reproduces it 5 times in 6; Windows is clean 6 of 6 at sixteen cores,
which is why nobody had seen it.

The pairs now start with TaskCreationOptions.LongRunning on the default
scheduler, which asks for a thread each. No assertion is changed - they
still fail if the loader serialises. The two coalescing cases moved onto
the same helper on purpose: two callers genuinely in flight is the
situation coalescing exists for, and a sequential pair was only ever
exercising a cache hit. Ten of ten clean under the same pin.

Release build green. App tests 4,152 / 3 skipped. Content 124 / 124.
Filed as #255.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 21:52:53 +02:00
Erik
f2644d42c2 perf(render): bound animation and alpha scratch residency
Complete Slice D3 by replacing the unbounded animation dictionary with a concurrent byte/count LRU and by putting the three retail alpha scratch owners behind one typed aggregate budget. Preserve immediate growth and draw order while reclaiming one-frame density spikes after sustained under-use. Close stale bounds-cache issue evidence without inventing a cache.
2026-07-24 16:34:28 +02:00
Erik
749e8ceeb1 fix(rendering): bound portal resource lifetime
Separate logical ownership, render publication, and GPU retirement across live entities, landblocks, particles, textures, mesh arenas, portal/UI teardown, and per-frame scratch storage. Add bounded DAT/texture caches, upload budgets, three-frame fence retirement, exact-incarnation appearance reconciliation, frame pacing, and extensive lifetime conformance coverage.\n\nThe seven-destination connected route now cuts peak working/private memory roughly in half, returns Caul to 125-153 FPS locally, and produces no WER or AMD reset.\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
2026-07-18 21:35:16 +02:00
Erik
96ddfdf175 feat(vfx): port retail effect scheduling and delivery 2026-07-14 09:25:44 +02:00
Erik
363e046112 feat(vfx): decode retail hooks and typed tables
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>
2026-07-14 08:17:44 +02:00