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>
This commit is contained in:
Erik 2026-07-18 21:35:16 +02:00
parent 3971997689
commit 749e8ceeb1
225 changed files with 29107 additions and 3914 deletions

View file

@ -488,6 +488,44 @@ identity plus animation, motion, physics, collision, selection, and
dead-reckoning owners. Only a real delete/despawn tears those owners down. This
matches retail's `CPhysicsObj::DoObjDescChangesFromDefault` behavior.
### Runtime resource ownership and bounded residency
Logical lifetime, spatial residency, and physical GPU lifetime are separate
contracts. A live entity or landblock owns stable logical references; moving it
between buckets does not reacquire resources. Appearance replacement acquires
the complete new mesh/texture set before publication, then releases the old set.
Despawn and landblock demotion withdraw every public render reference before
their physical resources become reclaimable. All of these transactions are
retryable and generation-scoped, so a failed release cannot silently strand a
half-retired owner or affect a reused server GUID.
Runtime content residency is deliberately bounded rather than proportional to
every region visited:
- `RuntimeDatCollectionFactory` keeps DAT indexes on demand but uses
`FileCachingStrategy.Never`; `DatCollection` remains the sole raw reader.
- Each typed DAT facade has a 256-entry / 64 MiB estimated LRU. Unknown object
graphs are conservatively charged at least 128 KiB. Canonical decoded texture
pixels use a separate 128-entry / 64 MiB cache.
- Standalone bindless textures retain at most 256 unowned entries / 32 MiB and
retire at most one per frame. Owner-scoped composite textures use a 64 MiB
unowned budget and 128 MiB physical budget, admitting at most 16 uploads or
8 MiB per frame.
- `ObjectMeshManager` may retain at most 32 empty texture atlases / 64 MiB.
`GlobalMeshBuffer` owns reclaimable vertex/index ranges capped at 384 MiB and
128 MiB respectively, with an 896 MiB physical ceiling that includes an
in-progress migration and its retired predecessor.
OpenGL deletion and range/slot reuse are not synonymous with logical release.
`GpuFrameFlightController` fences three frames in flight. Mesh-buffer stores,
texture handles, atlas layers, terrain slots, and landblock render records enter
retirement only after they are no longer publishable, and their physical ids are
recycled only after the corresponding fence signals. Shutdown follows the same
dependency order and remains retryable: UI/controllers and render registrations
withdraw first, then owner leases and caches, then GL backing stores. This keeps
drivers from reading freed memory without adding a portal-specific purge or a
visual-distance reduction.
---
## Per-Frame Update Order (current runtime)