feat(stats): Add 1% low FPS tracking#2661
Conversation
|
| Filename | Overview |
|---|---|
| Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp | Core metric logic: replaces 30-sample static array with a 4096-slot ring buffer; addFpsSample, calculateAverageFPS, calculateLow1PercentFPS, updatePerformanceMetrics and getLow1PercentFPS all look correct — overflow bounds checked, nth_element comparator is correct, throttle and warm-start initialisation are sound. |
| GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp | Identical implementation to Generals counterpart; all the same correctness properties apply. |
| Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp | Adds m_renderFpsLowString lifecycle (alloc, setFont, setText, draw, free) and the new RenderFpsLowColor INI field; the lazy-allocation guard in refreshRenderFpsResources is consistent with the existing pattern and drawRenderFps is only reached when the string is guaranteed non-null. |
| GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp | Mirror of Generals InGameUI changes; format strings, default color, and layout math are now aligned with Generals. |
| Core/GameEngine/Include/GameClient/Display.h | Adds pure-virtual getLow1PercentFPS() declaration; all concrete subclasses implement or stub it. |
| Generals/Code/Tools/GUIEdit/Include/GUIEditDisplay.h | Adds a no-op override returning 0 to satisfy the new pure virtual; consistent with existing getAverageFPS/getCurrentFPS stubs. |
| GeneralsMD/Code/Tools/GUIEdit/Include/GUIEditDisplay.h | Same no-op stub as Generals counterpart. |
| Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DDisplay.h | Declares new helpers and adds ring-buffer members; uses #pragma once correctly. |
Reviews (10): Last reviewed commit: "fix vc6" | Re-trigger Greptile
Move FPS history state into W3DDisplay members. Implement accurate time-based windowing for frame metrics. Use ceiling logic for improved 1% low accuracy. Optimize percentile calculation using efficient selection algorithm. Rename and centralize performance update call sites. Increase history buffer for stable high-FPS monitoring.
Update average FPS math to use time-weighted mean. Move sortBuffer to class members for consistency.
|
I do not like the visuals of the new value. Can this look better? |
|
Maybe make the 1% low value a bit brighter. It can be difficult to read in game. |
…ootprint. Fast Indexing: Replaced slow modulo division with fast bitwise index masking. Loop Optimization: Eliminated per-iteration modulo checks from history search loops. Safe Sampling: Clamped minimum frame time to prevent division by zero errors. Removed Branching: Pre-primed timer in display init to remove redundant per-frame branches. Cleaned Capping: Used RenderFpsPreset constant instead of magic zero value for uncapped. Improved Windows: Widened average window and boosted low-percent telemetry update rates.
…gnedShort m_durationHistory), shrinking memory from 49 KB to 8 KB Remove redundant m_fpsHistory and m_sortBuffer member variables from W3DDisplay class Use fast integer additions and comparisons instead of float operations inside calculation loops Wrap ring buffer indices using a branchless bitwise AND mask (& 4095) instead of modulo and check branches Implement lazy evaluation of the 1% low metrics in the getter, caching calculations to run at most once per second Allocate the sorting buffer locally on the stack instead of storing it as a class member Use std::fill to initialize the duration history to a baseline of 30 FPS at startup Remove duplicate semicolons, outdated comments, and format spacing
This PR adds a 1% low FPS metric to the existing FPS counter HUD, displayed in parentheses next to the average FPS. The 1% low is a standard performance metric used to surface frame time spikes that the average FPS hides. Inspired by #1942.
Add 1% low FPS display to HUD counter
Add m_renderFpsLowString and supporting UI members
Add RenderFpsLowColor configuration to InGameUI INI
Increase history to 4,096 frames for bitwise indexing
Implement rolling 1.0s window for average FPS
Implement rolling 3.0s window for 1% lows
The following screenshot from AOD Cobalt Rush shows the 1% low FPS overlay compared to CapFrameX (an external benchmarking tool, centered right), demonstrating the value of surfacing this metric separately from the average.
This change was generated with AI assistance. All generated code has been reviewed, tested, and verified for correctness. The implementation went through multiple iterations, including fundamental changes to the underlying approach, as well as passes to apply simplifications, fix inconsistencies, and optimize performance. Both Generals and GeneralsMD implementations are included in this PR with identical code.