fix(rendering): bound portal resource lifetime

Separate logical ownership, render publication, and GPU retirement across live entities, landblocks, particles, textures, mesh arenas, portal/UI teardown, and per-frame scratch storage. Add bounded DAT/texture caches, upload budgets, three-frame fence retirement, exact-incarnation appearance reconciliation, frame pacing, and extensive lifetime conformance coverage.\n\nThe seven-destination connected route now cuts peak working/private memory roughly in half, returns Caul to 125-153 FPS locally, and produces no WER or AMD reset.\n\nCo-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-18 21:35:16 +02:00
parent 3971997689
commit 749e8ceeb1
225 changed files with 29107 additions and 3914 deletions

View file

@ -12,6 +12,7 @@ using AcDream.Core.Net;
using AcDream.Core.Net.Messages;
using AcDream.Core.Selection;
using AcDream.Core.Spells;
using AcDream.Content;
using AcDream.UI.Abstractions;
using AcDream.UI.Abstractions.Panels.Chat;
using AcDream.UI.Abstractions.Panels.Settings;
@ -22,7 +23,7 @@ using Silk.NET.Input;
namespace AcDream.App.UI;
public sealed record RetailUiAssets(
DatCollection Dats,
IDatReaderWriter Dats,
object DatLock,
Func<uint, (uint Texture, int Width, int Height)> ResolveSprite,
Func<uint, UiDatFont?> ResolveFont,
@ -186,14 +187,15 @@ public sealed class RetailUiRuntime : IDisposable
{
private readonly RetailUiRuntimeBindings _bindings;
private StackSplitQuantityState StackSplitQuantity => _bindings.StackSplitQuantity;
private readonly RetailWindowLayoutPersistence? _persistence;
private readonly RetailUiAutomationScriptRunner? _automation;
private RetailWindowLayoutPersistence? _persistence;
private RetailUiAutomationScriptRunner? _automation;
private readonly RetailPanelUiController _panelUi;
private GameplayConfirmationController? _gameplayConfirmationController;
private RetailItemConfirmationController? _itemConfirmationController;
private RetailSkillTrainingConfirmationController? _skillTrainingConfirmationController;
private UiShortcutDigitGraphics? _shortcutDigitGraphics;
private VividTargetIndicatorController? _vividTargetIndicator;
private ResourceShutdownTransaction? _shutdown;
private bool _disposed;
private RetailUiRuntime(RetailUiRuntimeBindings bindings)
@ -203,6 +205,11 @@ public sealed class RetailUiRuntime : IDisposable
bindings.Host.IsWindowVisible,
bindings.Host.ShowWindow,
bindings.Host.HideWindow);
}
private void Initialize()
{
RetailUiRuntimeBindings bindings = _bindings;
MountFpsDisplay();
MountVividTargetIndicator();
MountVitals();
@ -284,13 +291,26 @@ public sealed class RetailUiRuntime : IDisposable
public static RetailUiRuntime Mount(RetailUiRuntimeBindings bindings)
{
ArgumentNullException.ThrowIfNull(bindings);
var runtime = new RetailUiRuntime(bindings);
try
{
return new RetailUiRuntime(bindings);
runtime.Initialize();
return runtime;
}
catch
catch (Exception initializationFailure)
{
bindings.Host.Dispose();
try
{
runtime.Dispose();
}
catch (Exception cleanupFailure)
{
throw new AggregateException(
"Retail UI initialization failed and its partially constructed ownership could not be fully released.",
initializationFailure,
cleanupFailure);
}
throw;
}
}
@ -1699,14 +1719,57 @@ public sealed class RetailUiRuntime : IDisposable
public void Dispose()
{
if (_disposed) return;
_disposed = true;
_persistence?.Dispose();
Host.WindowManager.WindowVisibilityChanged -= OnWindowVisibilityChanged;
_itemConfirmationController?.Dispose();
_gameplayConfirmationController?.Dispose();
DialogFactory?.Dispose();
_panelUi.Dispose();
Host.Dispose();
if (_disposed)
return;
_shutdown ??= CreateShutdownTransaction(
() => _persistence?.Dispose(),
() => Host.WindowManager.WindowVisibilityChanged -= OnWindowVisibilityChanged,
() => _itemConfirmationController?.Dispose(),
() => _gameplayConfirmationController?.Dispose(),
() => DialogFactory?.Dispose(),
_panelUi.Dispose,
Host.Dispose);
_shutdown.CompleteOrThrow();
_disposed = _shutdown.IsComplete;
}
internal static ResourceShutdownTransaction CreateShutdownTransaction(
Action disposePersistence,
Action unsubscribeWindowVisibility,
Action disposeItemConfirmation,
Action disposeGameplayConfirmation,
Action disposeDialogFactory,
Action disposePanelController,
Action disposeHost)
{
ArgumentNullException.ThrowIfNull(disposePersistence);
ArgumentNullException.ThrowIfNull(unsubscribeWindowVisibility);
ArgumentNullException.ThrowIfNull(disposeItemConfirmation);
ArgumentNullException.ThrowIfNull(disposeGameplayConfirmation);
ArgumentNullException.ThrowIfNull(disposeDialogFactory);
ArgumentNullException.ThrowIfNull(disposePanelController);
ArgumentNullException.ThrowIfNull(disposeHost);
return new ResourceShutdownTransaction(
new ResourceShutdownStage("retail UI observers",
[
new("window persistence", disposePersistence),
new("window visibility", unsubscribeWindowVisibility),
]),
new ResourceShutdownStage("retail UI semantic controllers",
[
new("item confirmation", disposeItemConfirmation),
new("gameplay confirmation", disposeGameplayConfirmation),
]),
new ResourceShutdownStage("retail UI panel composition",
[
new("dialog factory", disposeDialogFactory),
new("panel controller", disposePanelController),
]),
new ResourceShutdownStage("retained UI host",
[
new("host", disposeHost),
]));
}
}