acdream/src/AcDream.App/Rendering/GlTextureConstructionTransaction.cs
Erik c87b15303d 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>
2026-07-22 14:42:14 +02:00

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();
}