acdream/src/AcDream.App/Rendering/BindlessTexturePair.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

94 lines
2.5 KiB
C#

namespace AcDream.App.Rendering;
/// <summary>
/// Owns the two independent residency edges used by TerrainAtlas. Acquisition
/// and release publish each successful edge immediately, so a partial failure
/// retries only work that is still pending.
/// </summary>
internal sealed class BindlessTexturePair(
uint firstTexture,
uint secondTexture,
Func<uint, ulong> acquire,
Action<ulong> release)
{
private ulong _firstHandle;
private ulong _secondHandle;
private bool _firstResident;
private bool _secondResident;
public bool IsFullyResident => _firstResident && _secondResident;
public bool HasAnyResident => _firstResident || _secondResident;
public (ulong First, ulong Second) Acquire()
{
if (!_firstResident)
{
_firstHandle = acquire(firstTexture);
_firstResident = true;
}
if (!_secondResident)
{
try
{
_secondHandle = acquire(secondTexture);
_secondResident = true;
}
catch (Exception acquisitionFailure)
{
try
{
Release();
}
catch (Exception cleanupFailure)
{
throw new AggregateException(
"Second bindless texture acquisition failed and the resident prefix did not cleanly roll back.",
acquisitionFailure,
cleanupFailure);
}
throw;
}
}
return (_firstHandle, _secondHandle);
}
public void Release()
{
List<Exception>? failures = null;
if (_firstResident)
{
try
{
release(_firstHandle);
_firstResident = false;
_firstHandle = 0;
}
catch (Exception failure)
{
(failures ??= []).Add(failure);
}
}
if (_secondResident)
{
try
{
release(_secondHandle);
_secondResident = false;
_secondHandle = 0;
}
catch (Exception failure)
{
(failures ??= []).Add(failure);
}
}
if (failures is not null)
throw new AggregateException(
"One or more bindless texture handles could not be made non-resident.",
failures);
}
}