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.
This commit is contained in:
Erik 2026-07-24 16:34:28 +02:00
parent 3e18fc2730
commit f2644d42c2
18 changed files with 857 additions and 34 deletions

View file

@ -360,7 +360,7 @@ ack-per-packet).
## #240 — RetailAnimationLoader caches every parsed Animation forever
**Status:** OPEN
**Status:** DONE — 2026-07-24, Modern Runtime Slice D3
**Severity:** MEDIUM
**Filed:** 2026-07-24
**Component:** content / animation
@ -373,12 +373,14 @@ creature/object motion tables grows this monotonically — same class as the
audio caches bounded on 2026-07-24, and structurally incapable of the
plateau behavior the audit measured for entities/particles.
**Root cause / status:** 2026-07-24 audit review finding. Bound with the
same LRU/byte-budget shape as `DatSoundCache` (2026-07-24) /
`BoundedDatObjectCache`. Note sequencers retain references to loaded
animations — eviction must not invalidate an Animation a live sequencer
still holds (they keep the object alive; the cache only drops its own
reference).
**Fix:** `RetailAnimationLoader` now has one concurrent in-flight gate plus a
64 MiB / 512-entry LRU (startup-configurable through
`ResidencyBudgetOptions`). Duplicate requests coalesce, unrelated reads remain
parallel, null and oversize results cannot grow the cache, and eviction only
drops the cache reference. Live sequencers therefore keep their immutable
`Animation` object valid. Deterministic count, byte-pressure, oversize, LRU,
and concurrent-load tests cover the policy; the unified residency snapshot
reports decoded bytes, budget, and evictions.
**Files:** `src/AcDream.Content/Vfx/RetailAnimationLoader.cs:19-53`.
@ -435,7 +437,7 @@ the ordered array on the publication object.
## #243 — ObjectMeshManager._boundsCache grows unbounded
**Status:** OPEN
**Status:** DONE — 2026-07-24, stale audit finding closed by Slice D3
**Severity:** LOW
**Filed:** 2026-07-24
**Component:** render
@ -446,8 +448,12 @@ but the one unbounded container in a class where every other cache is
deliberately budgeted, and another no-plateau curve for long-uptime
sessions.
**Root cause / status:** 2026-07-24 audit review finding. Bound with the
existing entry-count LRU shape; plan Slice D's accounting covers it.
**Resolution:** The named field and insertion site do not exist in the current
tree: a repository-wide search finds no `_boundsCache` and
`ObjectMeshManager` has no bounds memoization owner. The review appears to
have described an older or different tree. Slice D deliberately does not add
a cache merely to bound it; current mesh/prepared/staging/arena owners are all
reported through the residency ledger.
**Files:** `src/AcDream.App/Rendering/Wb/ObjectMeshManager.cs` (`_boundsCache`,
insert site ~1757).