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

@ -67,17 +67,18 @@ public sealed class GameWindowSlice8BoundaryTests
"_dats = RuntimeDatCollectionFactory.OpenReadOnly(_datDir);",
"_runtimeSettings.ApplyStartup(",
"new RuntimeSettingsStartupTargets(",
"_uiHost = new AcDream.App.UI.UiHost(",
"_uiHost = _retailUiLease.AcquireHost(",
"_uiHost.WireMouse(m)",
"_uiHost.WireKeyboard(kb)",
"_retailUiRuntime = AcDream.App.UI.RetailUiRuntime.Mount(",
"_retailUiRuntime = _retailUiLease.Mount(",
"_liveEntities = new AcDream.App.World.LiveEntityRuntime(",
"_selectionInteractions ??= new AcDream.App.Interaction.SelectionInteractionController(",
"_retainedUiGameplayBinding =",
"AcDream.App.Input.RetainedUiGameplayBinding.Create(",
"_retainedUiGameplayBinding.Attach();",
"_renderFrameOrchestrator =",
"_updateFrameOrchestrator = new AcDream.App.Update.UpdateFrameOrchestrator(",
"var renderFrameOrchestrator =",
"var updateFrameOrchestrator = new AcDream.App.Update.UpdateFrameOrchestrator(",
"_frameGraphs.Publish(updateFrameOrchestrator, renderFrameOrchestrator);",
"_liveSessionHost = CreateLiveSessionHost();",
"_liveCombatModeCommands.Bind(combatCommand);",
"_runtimeDiagnosticCommands.Bind(runtimeDiagnostics);",
@ -265,7 +266,7 @@ public sealed class GameWindowSlice8BoundaryTests
shutdown,
"_runtimeSettings.UnbindViewModel()",
"_runtimeSettings.UnbindRuntimeTargets",
"_retailUiRuntime?.Dispose()",
"_retailUiLease.Dispose()",
"_streamer?.Dispose()",
"_wbDrawDispatcher?.Dispose()",
"_terrain?.Dispose()");
@ -292,12 +293,12 @@ public sealed class GameWindowSlice8BoundaryTests
"private void OnClosing()",
"private ResourceShutdownTransaction CreateShutdownTransaction()");
Assert.Equal(1, CountOccurrences(update, "_updateFrameOrchestrator.Tick("));
Assert.Equal(1, CountOccurrences(render, "_renderFrameOrchestrator!.Render("));
Assert.Equal(1, CountOccurrences(update, "_frameGraphs.Tick("));
Assert.Equal(1, CountOccurrences(render, "_frameGraphs.Render("));
AssertAppearsInOrder(
render,
"Vector2D<int> size = _window!.Size;",
"_renderFrameOrchestrator!.Render(",
"_frameGraphs.Render(",
"new AcDream.App.Rendering.RenderFrameInput(");
Assert.DoesNotContain("FramebufferSize", render, StringComparison.Ordinal);
Assert.Contains(
@ -338,6 +339,8 @@ public sealed class GameWindowSlice8BoundaryTests
"new ResourceShutdownStage(\"shared texture owners\"",
"new ResourceShutdownStage(\"mesh adapter\"",
"new ResourceShutdownStage(\"remaining render owners\"",
"new ResourceShutdownStage(\"dedicated render resources\"",
"new ResourceShutdownStage(\"failed render construction cleanup\"",
"new ResourceShutdownStage(\"frame flight owner\"",
"new ResourceShutdownStage(\"content mappings\"",
"new ResourceShutdownStage(\"input\"",
@ -390,6 +393,66 @@ public sealed class GameWindowSlice8BoundaryTests
character => Assert.True(char.IsWhiteSpace(character) || character == '}'));
}
[Fact]
public void ResourceRootsAndFramePairHaveExplicitAcquireTransferAndReleaseBoundaries()
{
string source = GameWindowSource();
string load = MethodBody(
"private void OnLoad()",
"private AcDream.App.Net.LiveSessionHost");
string shutdown = Slice(
source,
"private ResourceShutdownTransaction CreateShutdownTransaction()",
"private void OnFocusChanged(bool focused)");
string terrainAtlas = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Rendering",
"TerrainAtlas.cs"));
AssertAppearsInOrder(
load,
"_renderResourceLifetime.AcquireTerrainAtlas(",
"_terrain = new TerrainModernRenderer(",
"_portalTunnelFallback.AcquirePrepared(",
"static portalTunnel => portalTunnel.PrepareResources());",
"_renderResourceLifetime.AcquireSkyShader(",
"_skyRenderer = new AcDream.App.Rendering.Sky.SkyRenderer(",
"_portalTunnelFallback.Transfer(",
"_frameGraphs.Publish(updateFrameOrchestrator, renderFrameOrchestrator);");
AssertAppearsInOrder(
shutdown,
"_frameGraphs.Withdraw();",
"_retailUiLease.Dispose();",
"_localPlayerTeleport?.Dispose();",
"_portalTunnelFallback.ReleaseFallback();",
"_skyRenderer?.Dispose()",
"_terrain?.Dispose();",
"_renderResourceLifetime.ReleaseSkyShader",
"_renderResourceLifetime.ReleaseTerrainAtlas",
"_glConstructionCleanup.Dispose",
"_gl?.Dispose();");
Assert.DoesNotContain(
"RetailUiRuntime.Mount(",
load,
StringComparison.Ordinal);
Assert.DoesNotContain("gl.GenTexture()", terrainAtlas, StringComparison.Ordinal);
Assert.DoesNotContain(
"portalTunnel.PrepareResources();",
load,
StringComparison.Ordinal);
Assert.DoesNotContain("_portalTunnel =", source, StringComparison.Ordinal);
Assert.DoesNotContain("_renderFrameOrchestrator", source, StringComparison.Ordinal);
Assert.DoesNotContain("_updateFrameOrchestrator", source, StringComparison.Ordinal);
AssertAppearsInOrder(
source,
"_window.Run();",
"_glConstructionCleanup.RetainFrom(failure);",
"_glConstructionCleanup.Dispose");
}
private static string MethodBody(string start, string end) =>
Slice(GameWindowSource(), start, end);