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

@ -25,7 +25,13 @@ internal static class RetailAlphaOrdering
/// </summary>
internal interface IRetailAlphaDrawSource
{
void DrawAlphaBatch(ReadOnlySpan<int> tokens);
/// <summary>Uploads this source's complete payload for the current sorted
/// alpha scope exactly once. Tokens arrive in final far-to-near order.</summary>
void PrepareAlphaDraws(ReadOnlySpan<int> tokens);
/// <summary>Draws a contiguous range from the payload prepared above.</summary>
void DrawPreparedAlphaBatch(int firstPreparedDraw, int drawCount);
void ResetAlphaSubmissions();
}
@ -55,6 +61,7 @@ internal sealed class RetailAlphaQueue
private readonly List<RetailAlphaSubmission> _submissions = new(256);
private readonly List<IRetailAlphaDrawSource> _sources = new(4);
private int[] _tokenScratch = new int[256];
private int[] _sourceDrawOffsets = new int[4];
private long _nextSequence;
public bool IsCollecting { get; private set; }
@ -109,6 +116,26 @@ internal sealed class RetailAlphaQueue
_submissions.Sort(CompareRetailOrder);
EnsureTokenCapacity(_submissions.Count);
EnsureSourceCapacity(_sources.Count);
Array.Clear(_sourceDrawOffsets, 0, _sources.Count);
// Prepare each renderer once for this alpha scope. The filtered
// token sequence preserves final retail order for that source, so
// every later adjacent source-run maps to one contiguous prepared
// range without another GPU upload.
for (int sourceIndex = 0; sourceIndex < _sources.Count; sourceIndex++)
{
IRetailAlphaDrawSource source = _sources[sourceIndex];
int sourceCount = 0;
for (int i = 0; i < _submissions.Count; i++)
{
RetailAlphaSubmission submission = _submissions[i];
if (ReferenceEquals(submission.Source, source))
_tokenScratch[sourceCount++] = submission.Token;
}
source.PrepareAlphaDraws(_tokenScratch.AsSpan(0, sourceCount));
}
int start = 0;
while (start < _submissions.Count)
@ -120,9 +147,10 @@ internal sealed class RetailAlphaQueue
end++;
int count = end - start;
for (int i = 0; i < count; i++)
_tokenScratch[i] = _submissions[start + i].Token;
source.DrawAlphaBatch(_tokenScratch.AsSpan(0, count));
int sourceIndex = FindSourceIndex(source);
int firstPreparedDraw = _sourceDrawOffsets[sourceIndex];
source.DrawPreparedAlphaBatch(firstPreparedDraw, count);
_sourceDrawOffsets[sourceIndex] += count;
start = end;
}
}
@ -168,4 +196,19 @@ internal sealed class RetailAlphaQueue
return;
Array.Resize(ref _tokenScratch, count + 256);
}
private void EnsureSourceCapacity(int count)
{
if (_sourceDrawOffsets.Length >= count)
return;
Array.Resize(ref _sourceDrawOffsets, count + 4);
}
private int FindSourceIndex(IRetailAlphaDrawSource source)
{
for (int i = 0; i < _sources.Count; i++)
if (ReferenceEquals(_sources[i], source))
return i;
throw new InvalidOperationException("Retail alpha source was not registered for this frame.");
}
}