refactor(render): extract world scene frame owner
Move fallback and PView world drawing, shared alpha, particles, visibility, selection, and diagnostics behind focused frame owners. Make exceptional frames failure-atomic by restoring the shared GL baseline and preserving primary alpha failures through cleanup. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
85239fb373
commit
28e1cf8029
25 changed files with 2679 additions and 844 deletions
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AcDream.App.Rendering;
|
||||
|
|
@ -56,7 +57,16 @@ internal readonly record struct RetailAlphaSubmission(
|
|||
/// material/renderer sort.</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
internal sealed class RetailAlphaQueue
|
||||
internal interface IWorldSceneAlphaFrame
|
||||
{
|
||||
void BeginFrame();
|
||||
|
||||
void EndFrame();
|
||||
|
||||
void AbortFrame();
|
||||
}
|
||||
|
||||
internal sealed class RetailAlphaQueue : IWorldSceneAlphaFrame
|
||||
{
|
||||
private readonly List<RetailAlphaSubmission> _submissions = new(256);
|
||||
private readonly List<IRetailAlphaDrawSource> _sources = new(4);
|
||||
|
|
@ -108,58 +118,93 @@ internal sealed class RetailAlphaQueue
|
|||
if (!IsCollecting)
|
||||
throw new InvalidOperationException("Retail alpha flush requires an active frame.");
|
||||
|
||||
Exception? drawFailure = null;
|
||||
List<Exception>? resetFailures = null;
|
||||
try
|
||||
{
|
||||
if (_submissions.Count == 0)
|
||||
return;
|
||||
|
||||
SortRetailOrder();
|
||||
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++)
|
||||
if (_submissions.Count > 0)
|
||||
{
|
||||
IRetailAlphaDrawSource source = _sources[sourceIndex];
|
||||
int sourceCount = 0;
|
||||
for (int i = 0; i < _submissions.Count; i++)
|
||||
SortRetailOrder();
|
||||
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++)
|
||||
{
|
||||
RetailAlphaSubmission submission = _submissions[i];
|
||||
if (ReferenceEquals(submission.Source, source))
|
||||
_tokenScratch[sourceCount++] = submission.Token;
|
||||
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));
|
||||
}
|
||||
|
||||
source.PrepareAlphaDraws(_tokenScratch.AsSpan(0, sourceCount));
|
||||
}
|
||||
int start = 0;
|
||||
while (start < _submissions.Count)
|
||||
{
|
||||
IRetailAlphaDrawSource source = _submissions[start].Source;
|
||||
int end = start + 1;
|
||||
while (end < _submissions.Count
|
||||
&& ReferenceEquals(_submissions[end].Source, source))
|
||||
end++;
|
||||
|
||||
int start = 0;
|
||||
while (start < _submissions.Count)
|
||||
{
|
||||
IRetailAlphaDrawSource source = _submissions[start].Source;
|
||||
int end = start + 1;
|
||||
while (end < _submissions.Count
|
||||
&& ReferenceEquals(_submissions[end].Source, source))
|
||||
end++;
|
||||
|
||||
int count = end - start;
|
||||
int sourceIndex = FindSourceIndex(source);
|
||||
int firstPreparedDraw = _sourceDrawOffsets[sourceIndex];
|
||||
source.DrawPreparedAlphaBatch(firstPreparedDraw, count);
|
||||
_sourceDrawOffsets[sourceIndex] += count;
|
||||
start = end;
|
||||
int count = end - start;
|
||||
int sourceIndex = FindSourceIndex(source);
|
||||
int firstPreparedDraw = _sourceDrawOffsets[sourceIndex];
|
||||
source.DrawPreparedAlphaBatch(firstPreparedDraw, count);
|
||||
_sourceDrawOffsets[sourceIndex] += count;
|
||||
start = end;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
drawFailure = error;
|
||||
}
|
||||
finally
|
||||
{
|
||||
for (int i = 0; i < _sources.Count; i++)
|
||||
_sources[i].ResetAlphaSubmissions();
|
||||
{
|
||||
try
|
||||
{
|
||||
_sources[i].ResetAlphaSubmissions();
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
(resetFailures ??= []).Add(error);
|
||||
}
|
||||
}
|
||||
_sources.Clear();
|
||||
_submissions.Clear();
|
||||
}
|
||||
|
||||
if (drawFailure is not null)
|
||||
{
|
||||
if (resetFailures is { Count: > 0 })
|
||||
{
|
||||
resetFailures.Insert(0, drawFailure);
|
||||
throw new AggregateException(
|
||||
"Retail alpha drawing failed and its submissions could not be fully reset.",
|
||||
resetFailures);
|
||||
}
|
||||
|
||||
ExceptionDispatchInfo.Capture(drawFailure).Throw();
|
||||
}
|
||||
|
||||
if (resetFailures is { Count: > 0 })
|
||||
{
|
||||
throw new AggregateException(
|
||||
"Retail alpha submissions could not be fully reset.",
|
||||
resetFailures);
|
||||
}
|
||||
}
|
||||
|
||||
public void EndFrame()
|
||||
|
|
@ -177,6 +222,39 @@ internal sealed class RetailAlphaQueue
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Discards an incomplete frame without drawing it. Every source is still
|
||||
/// told to release its retained submission payload so the next frame begins
|
||||
/// from the same empty invariant as a successful flush.
|
||||
/// </summary>
|
||||
public void AbortFrame()
|
||||
{
|
||||
List<Exception>? failures = null;
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < _sources.Count; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
_sources[i].ResetAlphaSubmissions();
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
(failures ??= []).Add(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sources.Clear();
|
||||
_submissions.Clear();
|
||||
IsCollecting = false;
|
||||
}
|
||||
|
||||
if (failures is { Count: > 0 })
|
||||
throw new AggregateException("Retail alpha frame abort failed.", failures);
|
||||
}
|
||||
|
||||
private void SortRetailOrder()
|
||||
{
|
||||
int count = _submissions.Count;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue