refactor(render): extract frame presentation diagnostics
This commit is contained in:
parent
7e4cfb37c3
commit
733126a272
20 changed files with 3767 additions and 1021 deletions
387
src/AcDream.App/Rendering/DevToolsFramePresenter.cs
Normal file
387
src/AcDream.App/Rendering/DevToolsFramePresenter.cs
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
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 IDevToolsFrameBackend
|
||||
{
|
||||
void BeginFrame(float deltaSeconds);
|
||||
|
||||
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
|
||||
{
|
||||
private readonly ImGuiBootstrapper _bootstrap;
|
||||
private readonly ImGuiPanelHost _panels;
|
||||
|
||||
public ImGuiDevToolsFrameBackend(
|
||||
ImGuiBootstrapper 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 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Mutable late-composition seam for player-mode menu operations.</summary>
|
||||
internal sealed class DevToolsCameraMenuOperations : IDevToolsCameraMenuOperations
|
||||
{
|
||||
private readonly CameraController _camera;
|
||||
private PlayerModeController? _playerMode;
|
||||
|
||||
public DevToolsCameraMenuOperations(CameraController camera)
|
||||
{
|
||||
_camera = camera ?? throw new ArgumentNullException(nameof(camera));
|
||||
}
|
||||
|
||||
public bool IsFlyMode => _camera.IsFlyMode;
|
||||
|
||||
public bool CollideCamera
|
||||
{
|
||||
get => AcDream.Core.Rendering.CameraDiagnostics.CollideCamera;
|
||||
set => AcDream.Core.Rendering.CameraDiagnostics.CollideCamera = value;
|
||||
}
|
||||
|
||||
public void Bind(PlayerModeController playerMode)
|
||||
{
|
||||
_playerMode = playerMode ?? throw new ArgumentNullException(nameof(playerMode));
|
||||
}
|
||||
|
||||
public void ToggleFlyOrChase() => _playerMode?.ToggleFlyOrChase();
|
||||
}
|
||||
|
||||
/// <summary>Resolves the reconnect-safe command bus at draw time.</summary>
|
||||
internal sealed class DevToolsCommandBusSource : IDevToolsCommandBusSource
|
||||
{
|
||||
private LiveSessionController? _session;
|
||||
|
||||
public ICommandBus Current =>
|
||||
_session?.Commands ?? NullCommandBus.Instance;
|
||||
|
||||
public void Bind(LiveSessionController session)
|
||||
{
|
||||
_session = session ?? throw new ArgumentNullException(nameof(session));
|
||||
}
|
||||
}
|
||||
|
||||
/// <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
|
||||
{
|
||||
private readonly IDevToolsFrameBackend _backend;
|
||||
private readonly IDevToolsCameraMenuOperations _camera;
|
||||
private readonly IDevToolsCommandBusSource _commands;
|
||||
private readonly IDevToolsPanelSet _panels;
|
||||
private readonly AcDream.App.Diagnostics.FrameProfiler _profiler;
|
||||
|
||||
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) => _backend.BeginFrame(deltaSeconds);
|
||||
|
||||
public void Render(double deltaSeconds, int viewportWidth, int viewportHeight)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue