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:
Erik 2026-07-22 14:42:14 +02:00
parent fec0d94148
commit c87b15303d
41 changed files with 3768 additions and 355 deletions

View file

@ -0,0 +1,67 @@
using AcDream.App.Update;
namespace AcDream.App.Rendering;
internal interface IGameUpdateFrameRoot
{
void Tick(UpdateFrameInput input);
}
internal interface IGameRenderFrameRoot
{
RenderFrameOutcome Render(RenderFrameInput input);
}
/// <summary>
/// Sole atomic publication point for the current update/render frame pair.
/// Native callbacks are deliberately inert before publication and after
/// withdrawal so a partial load or converging shutdown cannot enter half a
/// frame graph.
/// </summary>
internal sealed class GameFrameGraphSlot
{
private sealed record FrameGraphPair(
IGameUpdateFrameRoot Update,
IGameRenderFrameRoot Render);
private FrameGraphPair? _pair;
public bool IsPublished => Volatile.Read(ref _pair) is not null;
public void Publish(IGameUpdateFrameRoot update, IGameRenderFrameRoot render)
{
ArgumentNullException.ThrowIfNull(update);
ArgumentNullException.ThrowIfNull(render);
var pair = new FrameGraphPair(update, render);
if (Interlocked.CompareExchange(ref _pair, pair, null) is not null)
throw new InvalidOperationException("A game frame graph is already published.");
}
public bool Tick(UpdateFrameInput input)
{
FrameGraphPair? pair = Volatile.Read(ref _pair);
if (pair is null)
return false;
pair.Update.Tick(input);
return true;
}
public bool Render(RenderFrameInput input, out RenderFrameOutcome outcome)
{
FrameGraphPair? pair = Volatile.Read(ref _pair);
if (pair is null)
{
outcome = default;
return false;
}
outcome = pair.Render.Render(input);
return true;
}
public void Withdraw()
{
Interlocked.Exchange(ref _pair, null);
}
}