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>
85 lines
2.5 KiB
C#
85 lines
2.5 KiB
C#
namespace AcDream.App.Rendering;
|
|
|
|
internal interface IGlTextureNameApi
|
|
{
|
|
uint GenTexture();
|
|
void DeleteTexture(uint texture);
|
|
}
|
|
|
|
internal sealed class GlTextureNameApi(Silk.NET.OpenGL.GL gl) : IGlTextureNameApi
|
|
{
|
|
public uint GenTexture() =>
|
|
GlResourceCommand.CreateTexture(gl, "terrain construction texture");
|
|
|
|
public void DeleteTexture(uint texture) =>
|
|
GlResourceCommand.DeleteTexture(
|
|
gl,
|
|
texture,
|
|
$"delete terrain construction texture {texture}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retains every texture name immediately after allocation until the complete
|
|
/// aggregate owner has been constructed. A failed factory rolls names back in
|
|
/// reverse acquisition order and attempts every deletion.
|
|
/// </summary>
|
|
internal sealed class GlTextureConstructionTransaction(IGlTextureNameApi api)
|
|
: IRetryableResourceCleanup
|
|
{
|
|
private readonly List<uint> _ownedNames = [];
|
|
private bool _finished;
|
|
|
|
public bool IsCleanupComplete => _finished;
|
|
|
|
public uint Allocate()
|
|
{
|
|
if (_finished)
|
|
throw new InvalidOperationException("The texture construction transaction is finished.");
|
|
|
|
uint texture = api.GenTexture();
|
|
if (texture == 0)
|
|
throw new InvalidOperationException("OpenGL returned no texture name.");
|
|
_ownedNames.Add(texture);
|
|
return texture;
|
|
}
|
|
|
|
public void Commit()
|
|
{
|
|
if (_finished)
|
|
throw new InvalidOperationException("The texture construction transaction is finished.");
|
|
_ownedNames.Clear();
|
|
_finished = true;
|
|
}
|
|
|
|
public void Rollback()
|
|
{
|
|
if (_finished)
|
|
return;
|
|
|
|
List<Exception>? failures = null;
|
|
for (int i = _ownedNames.Count - 1; i >= 0; i--)
|
|
{
|
|
uint texture = _ownedNames[i];
|
|
try
|
|
{
|
|
api.DeleteTexture(texture);
|
|
_ownedNames.RemoveAt(i);
|
|
}
|
|
catch (Exception failure)
|
|
{
|
|
(failures ??= []).Add(new InvalidOperationException(
|
|
$"Texture construction rollback could not delete OpenGL name {texture}.",
|
|
failure));
|
|
}
|
|
}
|
|
|
|
if (failures is not null)
|
|
throw new AggregateException(
|
|
"Texture construction rollback did not release every allocated OpenGL name.",
|
|
failures);
|
|
|
|
_finished = true;
|
|
}
|
|
|
|
public void RetryCleanup() => Rollback();
|
|
}
|