feat(render): Campaign V slice V11 commit 1 - delete ImGui, Studio, and the DevTools frontend

The ImGui developer-tools stack (AcDream.UI.ImGui), UI Studio
(src/AcDream.App/Studio), and the DevToolsFramePresenter/
SettingsDevToolsCompositionPhase ImGui composition machinery are removed.
Vulkan never composed a DevTools frontend (DevToolsEnabled already forced
false whenever the backend was Vulkan); this commit makes that permanent by
deleting the only implementation rather than leaving a dead branch behind.

What moved: Studio/SampleData.cs is a live production dependency
(InteractionRetainedUiComposition's character-sheet fallback, plus three
UI.Layout test files) - git mv'd to src/AcDream.App/UI/Layout/SampleData.cs,
namespace AcDream.App.UI.Layout, and trimmed to the SampleCharacter API that
is actually still called (BuildObjectTable/AddItem/AddEquipped/the item-guid
and icon constants had zero callers left once the Studio fixture provider
that used them was deleted).

What survives as backend-neutral seams, per the tests that still exercise
them: IDevToolsFrameLifecycle (moved into RenderFramePreparationController.cs,
now always bound to null), IFramebufferDevToolsTarget/FramebufferDevToolsBinding
in FramebufferResizeController.cs (its concrete DevToolsFramebufferTarget
adapter is deleted), and IDevToolsGameplayCommands in
GameplayInputCommandController.cs (DevToolsGameplayCommands becomes a
documented no-op instead of forwarding to the deleted presenter). A follow-up
re-homes Settings/Debug onto the retained UI through IPanelRenderer; until
then keybind remapping falls back to editing keybinds.json.

DevToolsEnabled is now `private const bool DevToolsEnabled = false`.
RuntimeOptions.DevTools is unchanged and still reaches VulkanGraphicsContext
for the optional debug-utils extensions; Program.cs now logs one line when
ACDREAM_DEVTOOLS=1 explaining that the ImGui UI is gone and the flag is
Vulkan-only now.

Removed: AcDream.UI.ImGui (project + ImGui.NET/Silk.NET.OpenGL.Extensions.ImGui
package refs), src/AcDream.App/Studio (minus SampleData.cs),
DevToolsFramePresenter.cs and everything only it constructed
(ISettingsDevToolsCompositionFactory, RetailSettingsDevToolsCompositionFactory,
DevToolsCompositionOwner, IGameWindowSettingsDevToolsPublication,
SettingsDevToolsOptionalDependencies, the "developer tools" shutdown-ledger
stage and its DevTools-typed fields on IngressShutdownRoots/
RenderShutdownRoots), the ui-studio Program.cs verb, and the cimgui native
manifest entries in GraphicalHostPlatformServices. GameWindow.cs's DevTools
composition branch, its _vitalsVm/_debugVm/_devToolsComposition/
_devToolsFramePresenter/_devToolsCommandBus fields, and every settingsDevTools
.DevTools?.* access across FrameRootComposition.cs/SessionPlayerComposition.cs
are gone with it.

Build green; complete Release solution suite 8,830 / 5 skips (App Tests
4,097/3 skips run standalone - one #250-family zero-allocation test flakes
under the full parallel `dotnet test AcDream.slnx` run, a pre-existing,
documented class unrelated to this change).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-28 23:56:04 +02:00
parent db4426d5ef
commit 844cf092a1
48 changed files with 227 additions and 6201 deletions

View file

@ -1,491 +0,0 @@
using System.Numerics;
using AcDream.App.Input;
using AcDream.App.Net;
using AcDream.UI.Abstractions;
using AcDream.UI.Abstractions.Panels.Chat;
using AcDream.UI.Abstractions.Panels.Debug;
using AcDream.UI.Abstractions.Panels.Settings;
using AcDream.UI.Abstractions.Panels.Vitals;
using AcDream.UI.ImGui;
using ImGuiNET;
namespace AcDream.App.Rendering;
internal enum DevToolsPanelLayoutCondition
{
FirstUseEver,
Always,
}
internal enum DevToolsPanelKind
{
Vitals,
Chat,
Debug,
Settings,
}
internal interface IDevToolsFrameLifecycle : IRenderFrameFailureRecovery
{
void BeginFrame(float deltaSeconds);
void Render(double deltaSeconds, int viewportWidth, int viewportHeight);
}
internal interface IDevToolsFrameBackend : IDisposable
{
void BeginFrame(float deltaSeconds);
void AbortFrame();
bool BeginMainMenuBar();
void EndMainMenuBar();
bool BeginMenu(string label);
void EndMenu();
bool MenuItem(string label, string? shortcut = null, bool selected = false);
void Separator();
void RenderPanels(PanelContext context);
void RenderDrawData();
void SetWindowLayout(
string title,
Vector2 position,
Vector2 size,
DevToolsPanelLayoutCondition condition);
}
internal interface IDevToolsCameraMenuOperations
{
bool IsFlyMode { get; }
bool CollideCamera { get; set; }
void ToggleFlyOrChase();
}
internal interface IDevToolsCommandBusSource
{
ICommandBus Current { get; }
}
internal interface IDevToolsPanelSet
{
bool Contains(DevToolsPanelKind kind);
string? GetTitle(DevToolsPanelKind kind);
bool IsVisible(DevToolsPanelKind kind);
void Toggle(DevToolsPanelKind kind);
void FocusChatInput();
void AddDebugToast(string message);
}
/// <summary>Concrete ImGui backend for the developer presentation owner.</summary>
internal sealed class ImGuiDevToolsFrameBackend : IDevToolsFrameBackend, IDisposable
{
private readonly IImGuiBootstrapper _bootstrap;
private readonly ImGuiPanelHost _panels;
private bool _disposed;
public ImGuiDevToolsFrameBackend(
IImGuiBootstrapper bootstrap,
ImGuiPanelHost panels)
{
_bootstrap = bootstrap ?? throw new ArgumentNullException(nameof(bootstrap));
_panels = panels ?? throw new ArgumentNullException(nameof(panels));
}
public void BeginFrame(float deltaSeconds) => _bootstrap.BeginFrame(deltaSeconds);
public void AbortFrame() => _bootstrap.AbortFrame();
public bool BeginMainMenuBar() => ImGuiNET.ImGui.BeginMainMenuBar();
public void EndMainMenuBar() => ImGuiNET.ImGui.EndMainMenuBar();
public bool BeginMenu(string label) => ImGuiNET.ImGui.BeginMenu(label);
public void EndMenu() => ImGuiNET.ImGui.EndMenu();
public bool MenuItem(string label, string? shortcut = null, bool selected = false) =>
ImGuiNET.ImGui.MenuItem(label, shortcut ?? string.Empty, selected);
public void Separator() => ImGuiNET.ImGui.Separator();
public void RenderPanels(PanelContext context) => _panels.RenderAll(context);
public void RenderDrawData() => _bootstrap.Render();
public void SetWindowLayout(
string title,
Vector2 position,
Vector2 size,
DevToolsPanelLayoutCondition condition)
{
ImGuiCond imguiCondition = condition switch
{
DevToolsPanelLayoutCondition.FirstUseEver => ImGuiCond.FirstUseEver,
DevToolsPanelLayoutCondition.Always => ImGuiCond.Always,
_ => throw new ArgumentOutOfRangeException(nameof(condition)),
};
ImGuiNET.ImGui.SetWindowPos(title, position, imguiCondition);
ImGuiNET.ImGui.SetWindowSize(title, size, imguiCondition);
}
public void Dispose()
{
if (_disposed)
return;
_bootstrap.Dispose();
_disposed = true;
}
}
/// <summary>Mutable late-composition seam for player-mode menu operations.</summary>
internal sealed class DevToolsCameraMenuOperations : IDevToolsCameraMenuOperations
{
private readonly CameraController _camera;
private readonly IDevToolsPlayerModeCommands _playerMode;
public DevToolsCameraMenuOperations(
CameraController camera,
IDevToolsPlayerModeCommands playerMode)
{
_camera = camera ?? throw new ArgumentNullException(nameof(camera));
_playerMode = playerMode ?? throw new ArgumentNullException(nameof(playerMode));
}
public bool IsFlyMode => _camera.IsFlyMode;
public bool CollideCamera
{
get => AcDream.Core.Rendering.CameraDiagnostics.CollideCamera;
set => AcDream.Core.Rendering.CameraDiagnostics.CollideCamera = value;
}
public void ToggleFlyOrChase() => _playerMode.ToggleFlyOrChase();
}
/// <summary>Resolves the reconnect-safe command bus at draw time.</summary>
internal sealed class DevToolsCommandBusSource : IDevToolsCommandBusSource
{
private ILiveUiSessionTarget? _session;
private bool _deactivated;
public ICommandBus Current =>
!_deactivated && _session is { } session
? session.Commands
: NullCommandBus.Instance;
public void Bind(ILiveUiSessionTarget session)
{
ArgumentNullException.ThrowIfNull(session);
ObjectDisposedException.ThrowIf(_deactivated, this);
if (_session is not null && !ReferenceEquals(_session, session))
throw new InvalidOperationException(
"Developer command-bus authority is already bound.");
_session = session;
}
public IDisposable BindOwned(ILiveUiSessionTarget session)
{
ArgumentNullException.ThrowIfNull(session);
ObjectDisposedException.ThrowIf(_deactivated, this);
if (_session is not null)
{
throw new InvalidOperationException(
"Developer command-bus authority is already bound.");
}
_session = session;
return new Binding(this, session);
}
public void Unbind(ILiveUiSessionTarget session)
{
ArgumentNullException.ThrowIfNull(session);
if (ReferenceEquals(_session, session))
_session = null;
}
public void Deactivate()
{
_deactivated = true;
_session = null;
}
private sealed class Binding : IDisposable
{
private DevToolsCommandBusSource? _owner;
private readonly ILiveUiSessionTarget _expected;
public Binding(
DevToolsCommandBusSource owner,
ILiveUiSessionTarget expected)
{
_owner = owner;
_expected = expected;
}
public void Dispose() =>
Interlocked.Exchange(ref _owner, null)?.Unbind(_expected);
}
}
/// <summary>Typed panel operations used by menu and input presentation.</summary>
internal sealed class DevToolsPanelSet : IDevToolsPanelSet
{
private readonly VitalsPanel _vitals;
private readonly ChatPanel _chat;
private readonly DebugPanel _debug;
private readonly DebugVM _debugViewModel;
private readonly SettingsPanel? _settings;
public DevToolsPanelSet(
VitalsPanel vitals,
ChatPanel chat,
DebugPanel debug,
DebugVM debugViewModel,
SettingsPanel? settings)
{
_vitals = vitals ?? throw new ArgumentNullException(nameof(vitals));
_chat = chat ?? throw new ArgumentNullException(nameof(chat));
_debug = debug ?? throw new ArgumentNullException(nameof(debug));
_debugViewModel = debugViewModel
?? throw new ArgumentNullException(nameof(debugViewModel));
_settings = settings;
}
public bool Contains(DevToolsPanelKind kind) =>
kind != DevToolsPanelKind.Settings || _settings is not null;
public string? GetTitle(DevToolsPanelKind kind) => GetPanel(kind)?.Title;
public bool IsVisible(DevToolsPanelKind kind) => GetPanel(kind)?.IsVisible == true;
public void Toggle(DevToolsPanelKind kind)
{
if (GetPanel(kind) is { } panel)
panel.IsVisible = !panel.IsVisible;
}
public void FocusChatInput() => _chat.FocusInput();
public void AddDebugToast(string message) => _debugViewModel.AddToast(message);
private IPanel? GetPanel(DevToolsPanelKind kind) => kind switch
{
DevToolsPanelKind.Vitals => _vitals,
DevToolsPanelKind.Chat => _chat,
DevToolsPanelKind.Debug => _debug,
DevToolsPanelKind.Settings => _settings,
_ => throw new ArgumentOutOfRangeException(nameof(kind)),
};
}
/// <summary>
/// Owns the optional ImGui developer frame, menu policy, panel actions, and
/// reusable default layout. Retained gameplay UI remains a separate earlier
/// presentation phase.
/// </summary>
internal sealed class DevToolsFramePresenter : IDevToolsFrameLifecycle
{
private readonly IDevToolsFrameBackend _backend;
private readonly IDevToolsCameraMenuOperations _camera;
private readonly IDevToolsCommandBusSource _commands;
private readonly IDevToolsPanelSet _panels;
private readonly AcDream.App.Diagnostics.FrameProfiler _profiler;
private bool _frameOpen;
public DevToolsFramePresenter(
IDevToolsFrameBackend backend,
IDevToolsCameraMenuOperations camera,
IDevToolsCommandBusSource commands,
AcDream.App.Diagnostics.FrameProfiler profiler,
IDevToolsPanelSet panels)
{
_backend = backend ?? throw new ArgumentNullException(nameof(backend));
_camera = camera ?? throw new ArgumentNullException(nameof(camera));
_commands = commands ?? throw new ArgumentNullException(nameof(commands));
_profiler = profiler ?? throw new ArgumentNullException(nameof(profiler));
_panels = panels ?? throw new ArgumentNullException(nameof(panels));
}
public void BeginFrame(float deltaSeconds)
{
if (_frameOpen)
{
throw new InvalidOperationException(
"The previous developer-tools frame must render or abort before a new frame begins.");
}
_frameOpen = true;
_backend.BeginFrame(deltaSeconds);
}
public void Render(double deltaSeconds, int viewportWidth, int viewportHeight)
{
if (!_frameOpen)
{
throw new InvalidOperationException(
"BeginFrame must open the developer-tools frame before Render.");
}
var context = new PanelContext((float)deltaSeconds, _commands.Current);
DrawMenu(viewportWidth, viewportHeight);
_backend.RenderPanels(context);
using var stage = _profiler.BeginStage(AcDream.App.Diagnostics.FrameStage.ImGui);
_backend.RenderDrawData();
_frameOpen = false;
}
public void AbortFrame()
{
if (!_frameOpen)
return;
try
{
_backend.AbortFrame();
}
finally
{
_frameOpen = false;
}
}
public void ToggleDebugPanel()
{
_panels.Toggle(DevToolsPanelKind.Debug);
_panels.AddDebugToast(
$"Debug panel {(_panels.IsVisible(DevToolsPanelKind.Debug) ? "ON" : "OFF")}");
}
public void FocusChatInput() => _panels.FocusChatInput();
public void ToggleSettingsPanel()
{
_panels.Toggle(DevToolsPanelKind.Settings);
}
public void ResetLayout(
int viewportWidth,
int viewportHeight,
DevToolsPanelLayoutCondition condition)
{
float width = Math.Max(viewportWidth, 480);
float height = Math.Max(viewportHeight, 320);
SetLayout(
DevToolsPanelKind.Vitals,
new Vector2(10f, 30f),
new Vector2(220f, 110f),
condition);
SetLayout(
DevToolsPanelKind.Chat,
new Vector2(10f, height - 320f),
new Vector2(450f, 300f),
condition);
SetLayout(
DevToolsPanelKind.Debug,
new Vector2(width - 380f, 30f),
new Vector2(370f, 520f),
condition);
if (_panels.Contains(DevToolsPanelKind.Settings))
{
SetLayout(
DevToolsPanelKind.Settings,
new Vector2((width - 700f) * 0.5f, (height - 500f) * 0.5f),
new Vector2(700f, 500f),
condition);
}
}
private void DrawMenu(int viewportWidth, int viewportHeight)
{
if (!_backend.BeginMainMenuBar())
return;
try
{
if (_backend.BeginMenu("View"))
{
try
{
if (_panels.Contains(DevToolsPanelKind.Settings)
&& _backend.MenuItem("Settings", "F11"))
{
_panels.Toggle(DevToolsPanelKind.Settings);
}
if (_backend.MenuItem("Vitals"))
_panels.Toggle(DevToolsPanelKind.Vitals);
if (_backend.MenuItem("Chat"))
_panels.Toggle(DevToolsPanelKind.Chat);
if (_backend.MenuItem("Debug", "Ctrl+F1"))
_panels.Toggle(DevToolsPanelKind.Debug);
_backend.Separator();
if (_backend.MenuItem("Reset window layout"))
ResetLayout(
viewportWidth,
viewportHeight,
DevToolsPanelLayoutCondition.Always);
}
finally
{
_backend.EndMenu();
}
}
if (_backend.BeginMenu("Camera"))
{
try
{
string flyLabel = _camera.IsFlyMode
? "Exit Free-Fly Mode"
: "Enter Free-Fly Mode";
if (_backend.MenuItem(flyLabel, "Ctrl+Shift+F"))
_camera.ToggleFlyOrChase();
bool collide = _camera.CollideCamera;
if (_backend.MenuItem(
"Collide Camera (spring arm)",
selected: collide))
{
_camera.CollideCamera = !collide;
}
}
finally
{
_backend.EndMenu();
}
}
}
finally
{
_backend.EndMainMenuBar();
}
}
private void SetLayout(
DevToolsPanelKind panel,
Vector2 position,
Vector2 size,
DevToolsPanelLayoutCondition condition)
{
string? title = _panels.GetTitle(panel);
if (!string.IsNullOrEmpty(title))
_backend.SetWindowLayout(title, position, size, condition);
}
}

View file

@ -37,19 +37,6 @@ internal interface IFramebufferDevToolsTarget
void ResetLayout(int width, int height);
}
internal sealed class DevToolsFramebufferTarget(DevToolsFramePresenter presenter)
: IFramebufferDevToolsTarget
{
private readonly DevToolsFramePresenter _presenter = presenter
?? throw new ArgumentNullException(nameof(presenter));
public void ResetLayout(int width, int height) =>
_presenter.ResetLayout(
width,
height,
DevToolsPanelLayoutCondition.Always);
}
/// <summary>Expected-owner lease for the optional Phase-3 resize edge.</summary>
internal sealed class FramebufferDevToolsBinding : IDisposable
{
@ -124,8 +111,9 @@ internal sealed class FramebufferResizeController
return;
// Frozen order: GL viewport, shared aspect publication, camera, then
// optional ImGui layout reset. Late binding never replays an earlier
// resize transition.
// the optional developer-tools layout reset (unbound since Campaign V
// slice V11 removed the ImGui frontend). Late binding never replays an
// earlier resize transition.
_viewport?.ResizeViewport(width, height);
_viewportAspect.Update(width, height);
_camera?.SetAspect(width / (float)height);

View file

@ -26,7 +26,6 @@ public sealed class GameWindow :
IGameWindowPlatformPublication<GameWindowGraphics, IInputContext>,
IGameWindowHostInputCameraPublication,
IGameWindowContentEffectsAudioPublication,
IGameWindowSettingsDevToolsPublication,
IGameWindowWorldRenderPublication,
IGameWindowInteractionRetainedUiPublication,
IGameWindowLivePresentationPublication,
@ -91,11 +90,11 @@ public sealed class GameWindow :
private readonly AcDream.App.Rendering.WorldSceneDebugState
_worldSceneDebugState = new();
// Phase I.2: the old StbTrueTypeSharp DebugOverlay was deleted in
// favor of the ImGui-backed DebugPanel (see _debugVm below). The
// TextRenderer + BitmapFont fields stay alive because they're shared
// Phase I.2: the old StbTrueTypeSharp DebugOverlay was deleted in favor of
// the ImGui-backed DebugPanel, which Campaign V slice V11 removed in turn.
// The TextRenderer + BitmapFont fields stay alive because they're shared
// with UiHost and reserved for the future world-space HUD (D.6 —
// damage floaters, name plates) where ImGui can't reach into the 3D
// damage floaters, name plates) where ImGui couldn't reach into the 3D
// scene. They are no longer used for any debug overlay.
private TextRenderer? _textRenderer;
private BitmapFont? _debugFont;
@ -379,12 +378,9 @@ public sealed class GameWindow :
public AcDream.Core.Player.LocalPlayerState LocalPlayer =>
_runtimeCharacter.LocalPlayer;
// Phase D.2a — ImGui devtools UI overlay. Null unless ACDREAM_DEVTOOLS=1.
// See docs/plans/2026-04-24-ui-framework.md for the staged UI strategy.
private AcDream.App.Rendering.DevToolsFramePresenter? _devToolsFramePresenter;
private AcDream.App.Rendering.DevToolsCommandBusSource? _devToolsCommandBus;
private DevToolsCompositionOwner? _devToolsComposition;
private AcDream.UI.Abstractions.Panels.Vitals.VitalsVM? _vitalsVm;
// Phase D.2a — ImGui devtools UI overlay. Removed at Campaign V slice V11;
// see docs/plans/2026-04-24-ui-framework.md for the staged UI strategy and
// docs/plans/2026-07-27-vulkan-campaign.md for the removal.
private AcDream.UI.Abstractions.Panels.Chat.ChatVM? _retailChatVm;
// Phase D.2b — retained host + composition runtime. Null unless ACDREAM_RETAIL_UI=1.
private AcDream.App.UI.UiHost? _uiHost;
@ -418,19 +414,13 @@ public sealed class GameWindow :
_creatureAppraisalFramePresenter;
// Phase D.2b Task 9 — plugin UI registrations buffered before OnLoad; drained in OnLoad.
private readonly AcDream.App.Plugins.BufferedUiRegistry? _uiRegistry;
// Phase I.2: ImGui debug panel ViewModel. The devtools presenter owns
// its panel; the VM remains here because runtime feedback producers bind
// directly to it during composition.
private AcDream.UI.Abstractions.Panels.Debug.DebugVM? _debugVm;
// DevToolsEnabled reads through typed RuntimeOptions.
//
// Campaign V slice V6h: the developer frontend is ImGui, which is not ported
// to Vulkan and which slice V11 deletes outright, so a Vulkan host composes
// none regardless of ACDREAM_DEVTOOLS. The flag still reaches
// VulkanInstanceFactory, where it selects the optional debug-utils
// instance extensions.
private bool DevToolsEnabled =>
_options.DevTools && _options.RenderBackend != RenderBackendKind.Vulkan;
// Campaign V slice V11 deleted the ImGui developer-tools frontend along
// with the OpenGL backend it required, so no host ever composes a
// developer UI regardless of ACDREAM_DEVTOOLS. The flag still reaches
// VulkanGraphicsContext, where it selects the optional debug-utils
// instance/device extensions — see the ACDREAM_DEVTOOLS log line in
// Run() below, which is the one remaining observable effect of the flag.
private const bool DevToolsEnabled = false;
// Phase G.1-G.2 world lighting/time state. The environment owner keeps
// the clock, selected day group, and weather transitions coherent.
@ -922,26 +912,6 @@ public sealed class GameWindow :
_audioSink = value.HookSink;
}
void IGameWindowSettingsDevToolsPublication.PublishDevTools(
DevToolsCompositionOwner value)
{
ArgumentNullException.ThrowIfNull(value);
if (_devToolsComposition is not null
|| _devToolsFramePresenter is not null
|| _devToolsCommandBus is not null
|| _debugVm is not null)
{
throw new InvalidOperationException(
"The GameWindow composition shell already owns developer tools.");
}
_devToolsComposition = value;
_devToolsFramePresenter = value.Presenter;
_devToolsCommandBus = value.CommandBus;
_vitalsVm = value.Vitals;
_debugVm = value.Debug;
}
void IGameWindowWorldRenderPublication.PublishBindlessSupport(
BindlessSupport value) =>
PublishCompositionOwner(
@ -1048,7 +1018,6 @@ public sealed class GameWindow :
{
_uiHost = retained.Host;
_retailUiRuntime = retained.Runtime;
_vitalsVm ??= retained.Vitals;
_retailChatVm = retained.Chat;
_characterSheetProvider = retained.CharacterSheet;
_frameScreenshots = retained.Screenshots;
@ -1347,65 +1316,15 @@ public sealed class GameWindow :
Console.Error.WriteLine),
this).Compose(platformResult, hostInputCamera),
(platformResult, hostInputCamera, contentEffectsAudio) =>
{
SettingsDevToolsOptionalDependencies? optionalDevTools = null;
if (DevToolsEnabled)
{
var devToolsWorldEntities =
new DeferredCanonicalWorldEntityCountSource();
var devToolsFrameDiagnostics =
new DeferredRenderFrameDiagnosticsSource();
var devToolsPlayerModeCommands =
new DeferredDevToolsPlayerModeCommands();
var devToolsFacts = new DevToolsRuntimeFacts(
_localPlayerMode,
_playerControllerSlot,
hostInputCamera.CameraController,
devToolsWorldEntities,
_animatedEntities,
_debugVmRenderFacts,
_physicsEngine,
_worldSceneDebugState,
_renderRange,
hostInputCamera.CameraPointerInput,
_worldEnvironment,
Lighting,
contentEffectsAudio.ParticleSystem,
devToolsFrameDiagnostics);
IRuntimeKeyBindingTarget? keyBindingTarget =
hostInputCamera.InputDispatcher is { } settingsDispatcher
? new RuntimeKeyBindingTarget(
settingsDispatcher,
_applicationPaths.KeyBindingsFile)
: null;
optionalDevTools = new SettingsDevToolsOptionalDependencies(
devToolsFacts,
keyBindingTarget,
devToolsWorldEntities,
devToolsFrameDiagnostics,
devToolsPlayerModeCommands);
}
return new SettingsDevToolsCompositionPhase(
new SettingsDevToolsCompositionPhase(
new SettingsDevToolsDependencies(
_window!,
_runtimeSettings,
new RuntimeSettingsStartupTargets(
new SilkRuntimeDisplayWindowTarget(_window!),
_displayFramePacing,
hostInputCamera.CameraController,
contentEffectsAudio.Audio?.Engine),
_hostQuiescence,
_runtime,
optionalDevTools,
_runtimeDiagnosticCommands,
_combatFeedback,
_keyBindings,
_frameProfiler,
_framebufferResize,
Console.WriteLine),
this).Compose(platformResult, hostInputCamera, contentEffectsAudio);
},
contentEffectsAudio.Audio?.Engine)))
.Compose(platformResult, hostInputCamera, contentEffectsAudio),
(platformResult, contentEffectsAudio, settingsDevTools) =>
{
const uint initialCenterLandblockId = 0xA9B4FFFFu;
@ -1433,9 +1352,10 @@ public sealed class GameWindow :
},
(platformResult, hostInputCamera, contentEffectsAudio, settingsDevTools, worldRender) =>
{
Action<string>? compositionToast = settingsDevTools.DevTools is { } devTools
? text => devTools.Debug.AddToast(text)
: null;
// The ImGui developer-tools debug toast sink was removed at
// Campaign V slice V11 along with the rest of the ImGui
// frontend; there is no replacement toast surface yet.
Action<string>? compositionToast = null;
return new InteractionRetainedUiCompositionPhase(
new InteractionRetainedUiDependencies(
_options,
@ -1469,7 +1389,7 @@ public sealed class GameWindow :
_window!,
viewPlane),
_uiFrameDiagnostics,
settingsDevTools.DevTools?.Vitals,
ExistingVitals: null,
compositionToast,
ClientTimerNow,
Console.WriteLine,
@ -1490,9 +1410,10 @@ public sealed class GameWindow :
worldRender,
interactionUi) =>
{
Action<string>? compositionToast = settingsDevTools.DevTools is { } devTools
? text => devTools.Debug.AddToast(text)
: null;
// The ImGui developer-tools debug toast sink was removed at
// Campaign V slice V11 along with the rest of the ImGui
// frontend; there is no replacement toast surface yet.
Action<string>? compositionToast = null;
return new LivePresentationCompositionPhase(
new LivePresentationDependencies(
_options,
@ -1527,8 +1448,8 @@ public sealed class GameWindow :
_hookRouter,
_renderDiagnosticLog,
WorldTime,
settingsDevTools.DevTools?.LateBindings.WorldEntities,
settingsDevTools.DevTools?.LateBindings.FrameDiagnostics,
DevWorldEntities: null,
DevFrameDiagnostics: null,
_uiFrameDiagnostics,
Console.WriteLine,
compositionToast),
@ -1761,7 +1682,6 @@ public sealed class GameWindow :
_kbSource,
_retailUiLease,
_uiHost,
_devToolsComposition,
_runtime,
_runtimeSettings,
_movementInput,
@ -1797,7 +1717,6 @@ public sealed class GameWindow :
new RenderShutdownRoots(
_gpuFrameFlights,
_gpuDevice,
_devToolsComposition,
_localPlayerTeleport,
_portalTunnelFallback,
_paperdollViewportRenderer,

View file

@ -63,7 +63,6 @@ internal sealed record IngressShutdownRoots(
RetailUiRuntimeLease RetailUi,
// Keeps failed physical UI bindings alive through native-window release.
UiHost? RetainedUiHost,
DevToolsCompositionOwner? DevTools,
GameRuntime Runtime,
RuntimeSettingsController Settings,
DispatcherMovementInputSource MovementInput,
@ -102,7 +101,6 @@ internal sealed record LiveShutdownRoots(
internal sealed record RenderShutdownRoots(
GpuFrameFlightController? FrameFlights,
IGpuDevice? GpuDevice,
DevToolsCompositionOwner? DevTools,
LocalPlayerTeleportController? LocalTeleport,
TransferableResourceSlot<PortalTunnelPresentation> PortalTunnelFallback,
PaperdollViewportRenderer? Paperdoll,
@ -350,7 +348,6 @@ internal static class GameWindowShutdownManifest
Hard("mouse source", () => ingress.MouseSource?.Deactivate()),
Hard("keyboard source", () => ingress.KeyboardSource?.Deactivate()),
Hard("retained UI input", ingress.RetailUi.QuiesceInput),
Hard("developer tools input", () => ingress.DevTools?.DeactivateInput()),
Hard("game runtime session", ingress.Runtime.StopSession),
]),
new ResourceShutdownStage("physical ingress cleanup",
@ -359,7 +356,6 @@ internal static class GameWindowShutdownManifest
Soft("retained gameplay", () => DisposeRetainedGameplay(ingress.RetainedGameplay)),
Soft("gameplay actions", () => DisposeGameplayActions(ingress.GameplayActions)),
Soft("retained UI input", ingress.RetailUi.DeactivateInput),
Soft("developer tools input", () => ingress.DevTools?.DetachInput()),
Soft("camera pointer", () => DisposeCameraPointer(ingress.CameraPointer)),
Soft("dispatcher", () => DisposeDispatcher(ingress)),
Soft("mouse source", () => DisposeMouseSource(ingress.MouseSource)),
@ -420,7 +416,6 @@ internal static class GameWindowShutdownManifest
]),
new ResourceShutdownStage("render frontends",
[
Hard("developer tools", () => DisposeDevToolsFrontend(render.DevTools)),
Hard("portal tunnel", () =>
{
render.LocalTeleport?.Dispose();
@ -600,12 +595,4 @@ internal static class GameWindowShutdownManifest
throw new InvalidOperationException("OpenAL native-resource cleanup remains pending.");
}
private static void DisposeDevToolsFrontend(DevToolsCompositionOwner? owner)
{
if (owner is null)
return;
owner.DisposeFrontend();
if (!owner.IsFrontendDisposalComplete)
throw new InvalidOperationException("Developer-tools frontend cleanup remains incomplete.");
}
}

View file

@ -5,6 +5,20 @@ internal interface IRenderWeatherFramePhase
void Tick(double deltaSeconds);
}
/// <summary>
/// Optional per-frame developer-presentation hook. Its one production
/// implementation (the ImGui developer-tools frontend) was removed at
/// Campaign V slice V11; the interface survives as the seam a follow-up
/// re-homing Settings/Debug onto the retained UI will implement, and every
/// current caller already treats it as optional (<c>null</c>-conditional).
/// </summary>
internal interface IDevToolsFrameLifecycle : IRenderFrameFailureRecovery
{
void BeginFrame(float deltaSeconds);
void Render(double deltaSeconds, int viewportWidth, int viewportHeight);
}
/// <summary>
/// Preserves the accepted pre-world order after the GPU/resource transaction:
/// begin optional developer UI, advance render-time weather, then hand the