From a33f3c4c7f8127166dd873468678243931d9e315 Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 12 Apr 2026 09:02:00 +0200 Subject: [PATCH] feat(app): performance overlay in window title MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates the window title every ~0.5s with: FPS | frame time (ms) | visible/total landblocks | entity count | animated count Example: "acdream | 60 fps | 16.7 ms | lb 12/25 visible | ent 847 | anim 19" Zero rendering cost — uses Silk.NET's window title setter, no font renderer or GPU overlay needed. The visible/total landblock ratio makes Phase A.2 frustum culling's impact visible at a glance: turning the camera should show the visible count drop as landblocks behind the camera get culled. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/AcDream.App/Rendering/GameWindow.cs | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index fcfca4e..5d62d63 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -1331,6 +1331,11 @@ public sealed class GameWindow : IDisposable _capturedMouse = isFlyMode ? mouse : null; } + // Performance overlay state — updated every ~0.5s and written to the + // window title so there's zero rendering cost (no font/overlay needed). + private double _perfAccum; + private int _perfFrameCount; + private void OnRender(double deltaSeconds) { _gl!.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); @@ -1340,12 +1345,40 @@ public sealed class GameWindow : IDisposable if (_animatedEntities.Count > 0) TickAnimations((float)deltaSeconds); + int visibleLandblocks = 0; + int totalLandblocks = 0; + if (_cameraController is not null) { var camera = _cameraController.Active; var frustum = AcDream.App.Rendering.FrustumPlanes.FromViewProjection(camera.View * camera.Projection); _terrain?.Draw(camera, frustum); _staticMesh?.Draw(camera, _worldState.LandblockEntries, frustum); + + // Count visible vs total for the perf overlay. + foreach (var entry in _worldState.LandblockEntries) + { + totalLandblocks++; + if (AcDream.App.Rendering.FrustumCuller.IsAabbVisible(frustum, entry.AabbMin, entry.AabbMax)) + visibleLandblocks++; + } + } + + // Update the window title with performance stats every ~0.5s. + _perfAccum += deltaSeconds; + _perfFrameCount++; + if (_perfAccum >= 0.5) + { + double avgFrameTime = _perfAccum / _perfFrameCount * 1000.0; + double fps = _perfFrameCount / _perfAccum; + int entityCount = _worldState.Entities.Count; + int animatedCount = _animatedEntities.Count; + + _window!.Title = $"acdream | {fps:F0} fps | {avgFrameTime:F1} ms | " + + $"lb {visibleLandblocks}/{totalLandblocks} visible | " + + $"ent {entityCount} | anim {animatedCount}"; + _perfAccum = 0; + _perfFrameCount = 0; } }