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

83 lines
2.2 KiB
C#

using System.Runtime.ExceptionServices;
namespace AcDream.App.Rendering;
/// <summary>
/// Runs a texture mutation with the caller's prior binding restored on every
/// exit path. The concrete GL adapter supplies checked commands; tests can
/// fault each edge without requiring a graphics context.
/// </summary>
internal sealed class RestoredTextureBindingMutation
{
private uint? _pendingRestore;
private bool _operationActive;
internal bool HasPendingRestore => _pendingRestore.HasValue;
public void Execute(
Func<uint> readBinding,
Action<uint> bind,
uint mutationBinding,
Action mutation)
{
ArgumentNullException.ThrowIfNull(readBinding);
ArgumentNullException.ThrowIfNull(bind);
ArgumentNullException.ThrowIfNull(mutation);
if (_operationActive)
throw new InvalidOperationException("A texture-binding mutation is already active.");
_operationActive = true;
try
{
ExecuteCore(readBinding, bind, mutationBinding, mutation);
}
finally
{
_operationActive = false;
}
}
private void ExecuteCore(
Func<uint> readBinding,
Action<uint> bind,
uint mutationBinding,
Action mutation)
{
if (_pendingRestore is uint pending)
{
bind(pending);
_pendingRestore = null;
}
uint previousBinding = readBinding();
_pendingRestore = previousBinding;
Exception? mutationFailure = null;
try
{
bind(mutationBinding);
mutation();
}
catch (Exception failure)
{
mutationFailure = failure;
}
try
{
bind(previousBinding);
_pendingRestore = null;
}
catch (Exception restoreFailure)
{
if (mutationFailure is not null)
throw new AggregateException(
"Texture mutation and binding restoration both failed.",
mutationFailure,
restoreFailure);
throw;
}
if (mutationFailure is not null)
ExceptionDispatchInfo.Capture(mutationFailure).Throw();
}
}