feat(pipeline): MP0 - wire FrameProfiler into GameWindow + DebugPanel toggle

GameWindow gets one field (_frameProfiler), one FrameBoundary() call at
the top of OnRender, three stage scopes (Update in OnUpdate, Upload
around _wbMeshAdapter?.Tick(), ImGui around _imguiBootstrap.Render()),
and one Dispose() call in teardown before _dats?.Dispose() releases the
GpuFrameTimer query ring. No new feature bodies land in GameWindow.cs —
all profiler logic lives in AcDream.App.Diagnostics.

DebugVM.FrameProf mirrors RenderingDiagnostics.FrameProfEnabled so the
DebugPanel checkbox ("Frame profiler ([frame-prof])") toggles the 5s
report live. Note: the checkbox lives in
AcDream.UI.Abstractions/Panels/Debug/DebugPanel.cs (IPanelRenderer,
backend-agnostic) alongside the other Diagnostics-section checkboxes —
not in AcDream.UI.ImGui, which holds no panel-drawing code per Code
Structure Rule 3.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-05 19:13:15 +02:00
parent 73bb8777a9
commit edbb1ffebd
3 changed files with 35 additions and 2 deletions

View file

@ -122,6 +122,11 @@ public sealed class GameWindow : IDisposable
private readonly long[] _applyLockWaitSamples = new long[256];
private int _applyLockWaitSampleCursor;
// MP0 (2026-07-05): permanent frame profiler — one FrameBoundary call
// per OnRender + three stage scopes. All logic lives in
// AcDream.App.Diagnostics.FrameProfiler (structure rule 1).
private readonly AcDream.App.Diagnostics.FrameProfiler _frameProfiler = new();
// Phase A.1: streaming fields replacing the one-shot _entities list.
private AcDream.App.Streaming.LandblockStreamer? _streamer;
private AcDream.App.Streaming.GpuWorldState _worldState = new();
@ -7951,6 +7956,8 @@ public sealed class GameWindow : IDisposable
private void OnUpdate(double dt)
{
using var _updStage = _frameProfiler.BeginStage(AcDream.App.Diagnostics.FrameStage.Update);
// [FRAME-DIAG]: applies run later this frame inside _streamingController.Tick;
// flush the PREVIOUS OnUpdate's accumulated apply cost here (a clean per-frame
// boundary, independent of where in OnUpdate the applies landed) and reset.
@ -8755,6 +8762,8 @@ public sealed class GameWindow : IDisposable
private void OnRender(double deltaSeconds)
{
_frameProfiler.FrameBoundary(_gl!);
// Phase G.1: set the clear color from the current sky's fog
// tint so the horizon band continues naturally past the
// rendered geometry. Fog blends to this color at max distance
@ -8810,7 +8819,10 @@ public sealed class GameWindow : IDisposable
// [FRAME-DIAG]: this OnRender drain is where staged GfxObj/entity meshes hit
// the GPU (uncapped) — the H2 entity-upload suspect, separate from the apply.
long fdE0 = _frameDiag ? System.Diagnostics.Stopwatch.GetTimestamp() : 0L;
_wbMeshAdapter?.Tick();
using (var _uplStage = _frameProfiler.BeginStage(AcDream.App.Diagnostics.FrameStage.Upload))
{
_wbMeshAdapter?.Tick();
}
if (_frameDiag)
FrameDiagPush(_entityUploadSamples, ref _entityUploadSampleCursor,
System.Diagnostics.Stopwatch.GetTimestamp() - fdE0);
@ -9818,7 +9830,10 @@ public sealed class GameWindow : IDisposable
// panels but still over the 3D scene. Cheap when no
// selection — internal early-return on null guid.
_targetIndicator?.Render();
_imguiBootstrap.Render();
using (var _imguiStage = _frameProfiler.BeginStage(AcDream.App.Diagnostics.FrameStage.ImGui))
{
_imguiBootstrap.Render();
}
}
// Update the window title with performance stats every ~0.5s.
@ -14005,6 +14020,7 @@ public sealed class GameWindow : IDisposable
_uiHost?.Dispose();
_textRenderer?.Dispose();
_debugFont?.Dispose();
_frameProfiler.Dispose(); // MP0: releases the GpuFrameTimer query ring
_dats?.Dispose();
_input?.Dispose();
_gl?.Dispose();

View file

@ -254,6 +254,12 @@ public sealed class DebugPanel : IPanel
if (r.Checkbox("Probe auto-walk (ACDREAM_PROBE_AUTOWALK)",
ref probeAutoWalk)) _vm.ProbeAutoWalk = probeAutoWalk;
// MP0 (2026-07-05): permanent frame profiler toggle — not a
// throwaway investigation probe, so it lives with the other
// always-available diagnostics rather than a dated section.
bool frameProf = _vm.FrameProf;
if (r.Checkbox("Frame profiler ([frame-prof])", ref frameProf)) _vm.FrameProf = frameProf;
// ── Indoor rendering diagnostics (2026-05-19) ───────────────
// Pinpoint where the EnvCell rendering chain breaks for
// hypothesis-driven Phase 2 fix. Spec:

View file

@ -291,6 +291,17 @@ public sealed class DebugVM
set => PhysicsDiagnostics.ProbeAutoWalkEnabled = value;
}
/// <summary>
/// Runtime mirror of <c>RenderingDiagnostics.FrameProfEnabled</c>
/// (env var <c>ACDREAM_FRAME_PROF</c>). Toggling here starts/stops the
/// [frame-prof] 5-second report live — no relaunch required.
/// </summary>
public bool FrameProf
{
get => RenderingDiagnostics.FrameProfEnabled;
set => RenderingDiagnostics.FrameProfEnabled = value;
}
// ── Indoor rendering diagnostics (2026-05-19) ───────────────────
// Mirror RenderingDiagnostics statics so DebugPanel checkbox toggles
// take effect on the next render frame without relaunching.