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; } }