Skip to content

feat(frame-pacer): overhaul logic and render FPS limits and presets#2699

Open
githubawn wants to merge 3 commits into
TheSuperHackers:mainfrom
githubawn:feature/frame-pacer-overhaul
Open

feat(frame-pacer): overhaul logic and render FPS limits and presets#2699
githubawn wants to merge 3 commits into
TheSuperHackers:mainfrom
githubawn:feature/frame-pacer-overhaul

Conversation

@githubawn
Copy link
Copy Markdown

  • Added 15 FPS to render presets and expanded logic presets (1 to 960 FPS). (15 is the minimum in skirmish, not 30)
  • Implemented array-based preset snapping for logic FPS.
  • Removed redundant logic speed scaling guards and safety asserts.
  • Removed debug lock for lower logic rates, they are fun features.

This change was generated with AI assistance. All generated code has been reviewed, tested, and verified for functionality.

- Added 15 FPS to render presets and expanded logic presets (1 to 960 FPS).
- Implemented array-based preset snapping for logic FPS.
- Renamed extraStep to snapValue for clarity.
- Removed redundant logic speed scaling guards and safety asserts.
- Cleaned up stale comments and dead code in CommandXlat.
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 10, 2026

Greptile Summary

This PR replaces the fixed-step (±5 FPS) logic for LogicTimeScaleFpsPreset with an array-based preset system (1–960 + uncapped) that also snaps to the current render FPS when it falls between presets, and adds 15 FPS to the render preset list. The changeLogicTimeScale callers in both game variants are simplified by removing the old remainder-ceiling arithmetic and deferring setLogicTimeScaleFps only to the enable path.

  • New preset arrays: RenderFpsPreset gains a 15 FPS entry; LogicTimeScaleFpsPreset replaces linear stepping with {1, 5, 15, 30, 45, 60, 75, 90, 105, 120, 240, 480, 960, UncappedFpsValue}, allowing extreme slow-motion (1 FPS) and fast-forward (960 FPS) with a first-class "uncapped" sentinel that disables the timescale entirely.
  • Snap-to-render behaviour: getNextFpsValue/getPrevFpsValue accept an optional snapValue (the render FPS cap) and select whichever candidate — preset or snap — lands closest to the current value, ensuring the render rate is always reachable as a natural stopping point.
  • Simplified changeLogicTimeScale: The remainder-ceiling math is gone; setLogicTimeScaleFps is now called only when enabling, intentionally preserving the last active logic FPS in the pacer so that re-enabling timescale from outside this path resumes from a sensible value.

Confidence Score: 5/5

Safe to merge — the algorithm handles all edge cases (floor at 1, UncappedFpsValue ceiling, value equal to snap, values between presets) correctly, and both game-variant call sites are updated symmetrically.

All snap, floor, and ceiling paths through the new getNextFpsValue/getPrevFpsValue logic produce correct results. The UncappedFpsValue sentinel flows correctly through the clamping guard and the enable/disable decision in both CommandXlat.cpp files. No data is silently dropped or miscalculated in any tested scenario.

No files require special attention.

Important Files Changed

Filename Overview
Core/GameEngine/Include/Common/FrameRateLimit.h Removed the StepFpsValue/MinFpsValue enum from LogicTimeScaleFpsPreset, replaced with array-based design; added optional snapValue params to all three public methods; moved s_fpsValues to private — clean interface change with no issues.
Core/GameEngine/Source/Common/FrameRateLimit.cpp Added 15 to RenderFpsPreset array, introduced LogicTimeScaleFpsPreset::s_fpsValues (1–960, UncappedFpsValue), replaced linear step logic with ascending-array search + snap; edge cases (floor at 1, ceiling at UncappedFpsValue, value==snapValue, value between presets) all handled correctly.
Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp Simplified changeLogicTimeScale: removed the remainder-ceiling math, passes maxRenderFps as snapValue, and only calls setLogicTimeScaleFps when actually enabling timescale so the last active FPS is preserved for sane re-enable — logic is correct for all capped/uncapped combinations.
GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp Identical simplification to the Generals/ counterpart; both files are kept in sync correctly.

Reviews (3): Last reviewed commit: "implemented feedback" | Re-trigger Greptile

Comment thread Core/GameEngine/Source/Common/FrameRateLimit.cpp
Comment thread GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp Outdated
Comment thread Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp Outdated
Comment thread Core/GameEngine/Source/Common/FrameRateLimit.cpp Outdated
Comment thread Core/GameEngine/Source/Common/FrameRateLimit.cpp Outdated
Comment thread Core/GameEngine/Source/Common/FrameRateLimit.cpp Outdated
Comment thread Core/GameEngine/Source/Common/FrameRateLimit.cpp Outdated
Comment thread Core/GameEngine/Source/Common/FrameRateLimit.cpp Outdated
Comment thread Core/GameEngine/Source/Common/FrameRateLimit.cpp Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants