refactor(lifetime): own render resource prefixes
Give terrain, sky, retained UI, portal preparation, and the update/render frame pair explicit single owners. Make shader, texture, text, bindless, and GL construction prefixes checked and retryable so partial failure cannot lose or replay resource ownership. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
fec0d94148
commit
c87b15303d
41 changed files with 3768 additions and 355 deletions
102
src/AcDream.App/Rendering/GlConstructionCleanupLedger.cs
Normal file
102
src/AcDream.App/Rendering/GlConstructionCleanupLedger.cs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
namespace AcDream.App.Rendering;
|
||||
|
||||
internal interface IRetryableResourceCleanup
|
||||
{
|
||||
bool IsCleanupComplete { get; }
|
||||
void RetryCleanup();
|
||||
}
|
||||
|
||||
internal sealed class GlResourceConstructionException : AggregateException,
|
||||
IRetryableResourceCleanup
|
||||
{
|
||||
private readonly IRetryableResourceCleanup _cleanup;
|
||||
|
||||
public GlResourceConstructionException(
|
||||
string message,
|
||||
IRetryableResourceCleanup cleanup,
|
||||
IEnumerable<Exception> failures)
|
||||
: base(message, failures)
|
||||
{
|
||||
_cleanup = cleanup ?? throw new ArgumentNullException(nameof(cleanup));
|
||||
}
|
||||
|
||||
public bool IsCleanupComplete => _cleanup.IsCleanupComplete;
|
||||
|
||||
public void RetryCleanup() => _cleanup.RetryCleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lifetime root for cleanup work that could not finish before a throwing GL
|
||||
/// factory returned control. The original exception remains the retry owner;
|
||||
/// this ledger prevents it and its exact pending names from becoming local-only.
|
||||
/// </summary>
|
||||
internal sealed class GlConstructionCleanupLedger : IDisposable
|
||||
{
|
||||
private readonly List<IRetryableResourceCleanup> _pending = [];
|
||||
private bool _disposing;
|
||||
|
||||
public bool IsComplete => _pending.Count == 0;
|
||||
|
||||
public bool RetainFrom(Exception failure)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(failure);
|
||||
bool retained = false;
|
||||
Visit(failure);
|
||||
return retained;
|
||||
|
||||
void Visit(Exception current)
|
||||
{
|
||||
if (current is IRetryableResourceCleanup cleanup)
|
||||
{
|
||||
if (!cleanup.IsCleanupComplete && !_pending.Contains(cleanup))
|
||||
_pending.Add(cleanup);
|
||||
retained = true;
|
||||
}
|
||||
|
||||
if (current is AggregateException aggregate)
|
||||
{
|
||||
foreach (Exception inner in aggregate.InnerExceptions)
|
||||
Visit(inner);
|
||||
}
|
||||
else if (current.InnerException is { } inner)
|
||||
{
|
||||
Visit(inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposing || _pending.Count == 0)
|
||||
return;
|
||||
|
||||
_disposing = true;
|
||||
List<Exception>? failures = null;
|
||||
try
|
||||
{
|
||||
for (int i = _pending.Count - 1; i >= 0; i--)
|
||||
{
|
||||
IRetryableResourceCleanup cleanup = _pending[i];
|
||||
try
|
||||
{
|
||||
cleanup.RetryCleanup();
|
||||
if (cleanup.IsCleanupComplete)
|
||||
_pending.RemoveAt(i);
|
||||
}
|
||||
catch (Exception failure)
|
||||
{
|
||||
(failures ??= []).Add(failure);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_disposing = false;
|
||||
}
|
||||
|
||||
if (failures is not null)
|
||||
throw new AggregateException(
|
||||
"One or more failed GL construction transactions remain pending.",
|
||||
failures);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue