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

@ -26,6 +26,10 @@ internal sealed class ContiguousRangeAllocator
public int HighWaterMark { get; private set; }
public int Free => Capacity - Used;
public int LargestFreeRange => _free.Count == 0 ? 0 : _free.Max(range => range.Length);
public int TrailingFreeLength =>
_free.Count != 0 && _free[^1].End == Capacity
? _free[^1].Length
: 0;
public bool TryAllocate(int length, out MeshBufferRange allocation)
{
@ -73,6 +77,30 @@ internal sealed class ContiguousRangeAllocator
InsertAndCoalesce(new MeshBufferRange(oldCapacity, newCapacity - oldCapacity));
}
/// <summary>
/// Removes an unused tail after the matching physical GPU buffer has been
/// replaced. Live offsets are unchanged, so no render-data rewrite is
/// required.
/// </summary>
public void Shrink(int newCapacity)
{
if (newCapacity <= 0 || newCapacity >= Capacity)
throw new ArgumentOutOfRangeException(nameof(newCapacity));
if (newCapacity < HighWaterMark)
throw new InvalidOperationException("Cannot trim a GPU arena through a live allocation.");
MeshBufferRange tail = _free.Count == 0 ? default : _free[^1];
if (tail.End != Capacity || tail.Offset > newCapacity)
throw new InvalidOperationException("The requested GPU arena tail is not wholly free.");
if (tail.Offset == newCapacity)
_free.RemoveAt(_free.Count - 1);
else
_free[^1] = new MeshBufferRange(tail.Offset, newCapacity - tail.Offset);
Capacity = newCapacity;
RecalculateHighWaterMark();
}
public void Release(MeshBufferRange allocation)
{
if (allocation.Length <= 0
@ -84,6 +112,7 @@ internal sealed class ContiguousRangeAllocator
InsertAndCoalesce(allocation);
Used = checked(Used - allocation.Length);
RecalculateHighWaterMark();
}
private void InsertAndCoalesce(MeshBufferRange released)
@ -114,4 +143,11 @@ internal sealed class ContiguousRangeAllocator
_free.Insert(index, new MeshBufferRange(start, end - start));
}
private void RecalculateHighWaterMark()
{
HighWaterMark = _free.Count != 0 && _free[^1].End == Capacity
? _free[^1].Offset
: Capacity;
}
}