From edbb1ffebd4c255965ec9c3057dacce6e8a9f6d1 Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 5 Jul 2026 19:13:15 +0200 Subject: [PATCH] feat(pipeline): MP0 - wire FrameProfiler into GameWindow + DebugPanel toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/AcDream.App/Rendering/GameWindow.cs | 20 +++++++++++++++++-- .../Panels/Debug/DebugPanel.cs | 6 ++++++ .../Panels/Debug/DebugVM.cs | 11 ++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index 6b59c300..f45d7d2d 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -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(); diff --git a/src/AcDream.UI.Abstractions/Panels/Debug/DebugPanel.cs b/src/AcDream.UI.Abstractions/Panels/Debug/DebugPanel.cs index 6593f90c..01292581 100644 --- a/src/AcDream.UI.Abstractions/Panels/Debug/DebugPanel.cs +++ b/src/AcDream.UI.Abstractions/Panels/Debug/DebugPanel.cs @@ -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: diff --git a/src/AcDream.UI.Abstractions/Panels/Debug/DebugVM.cs b/src/AcDream.UI.Abstractions/Panels/Debug/DebugVM.cs index d7801f46..bf4123f8 100644 --- a/src/AcDream.UI.Abstractions/Panels/Debug/DebugVM.cs +++ b/src/AcDream.UI.Abstractions/Panels/Debug/DebugVM.cs @@ -291,6 +291,17 @@ public sealed class DebugVM set => PhysicsDiagnostics.ProbeAutoWalkEnabled = value; } + /// + /// Runtime mirror of RenderingDiagnostics.FrameProfEnabled + /// (env var ACDREAM_FRAME_PROF). Toggling here starts/stops the + /// [frame-prof] 5-second report live — no relaunch required. + /// + 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.