Skip to content

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

Open
githubawn wants to merge 8 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 8 commits into
TheSuperHackers:mainfrom
githubawn:feature/frame-pacer-overhaul

Conversation

@githubawn

@githubawn githubawn commented May 10, 2026

Copy link
Copy Markdown
  • Added 15 FPS to render presets and expanded logic presets (1 to 960 FPS). (15 is the minimum in singleplayer, 30 is minimum in multiplayer)
  • 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.

@greptile-apps

greptile-apps Bot commented May 10, 2026

Copy link
Copy Markdown

Greptile Summary

Updates frame-pacing controls:

  • Adds a 15 FPS render preset and expands logic-rate presets from 1 through 960 FPS.
  • Replaces fixed-step logic-rate changes with preset-array navigation and render-rate snapping.
  • Allows lower logic rates and preserves the last active logic-rate setting when time scaling is disabled.
  • Keeps network rendering at or above the simulation tick rate.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
Core/GameEngine/Include/Common/FrameRateLimit.h Replaces fixed logic-rate bounds and steps with preset-based APIs that accept an optional snap value.
Core/GameEngine/Source/Common/FramePacer.cpp Ensures network sessions render at no less than the fixed simulation frame rate.
Core/GameEngine/Source/Common/FrameRateLimit.cpp Adds render and logic presets, documents their ascending-order invariant, and implements nearest-preset traversal.
Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp Updates logic-time-scale commands to snap against render FPS and retain the last enabled logic rate.

Reviews (4): Last reviewed commit: "Fix review comments: simplify UncappedFp..." | Re-trigger Greptile

Comment thread Core/GameEngine/Source/Common/FrameRateLimit.cpp
Comment thread Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp Outdated
Comment thread GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp Outdated
Comment thread Generals/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
@xezon
xezon requested a review from Skyaero42 May 28, 2026 19:22

@xezon xezon left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My review is incomplete.

This change conflicts a bit with a change I have in the works where logic step can run faster than render update. I can only continue it after the particle decoupling.

This change should wait.

Comment thread Core/GameEngine/Source/Common/FrameRateLimit.cpp Outdated
Comment thread Core/GameEngine/Source/Common/FrameRateLimit.cpp Outdated
@githubawn
githubawn marked this pull request as draft June 27, 2026 18:45
- 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.
@githubawn
githubawn force-pushed the feature/frame-pacer-overhaul branch from 36bfef6 to 0e498df Compare July 31, 2026 21:11
@githubawn
githubawn marked this pull request as ready for review July 31, 2026 21:11
@githubawn

Copy link
Copy Markdown
Author

This PR is great for testing decoupling and interpolation. I found some microstutter using the 1 fps logic rate and it saves running OBS to see the frame by frame stuff.

Comment thread Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp
Comment thread Core/GameEngine/Source/Common/FrameRateLimit.cpp

@Caball009 Caball009 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incomplete review.

Comment thread Core/GameEngine/Source/Common/FrameRateLimit.cpp Outdated
15, 30, 50, 56, 60, 65, 70, 72, 75, 80, 85, 90, 100, 110, 120, 144, 240, 480, UncappedFpsValue };

static_assert(LOGICFRAMES_PER_SECOND <= 30, "Min FPS values need to be revisited!");
// TheSuperHackers @info s_fpsValues MUST be strictly ascending; the search loops break on first match.

@Caball009 Caball009 Aug 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of a comment we can check this at compile-time:

static_assert(std::is_sorted(std::begin(s_fpsValues), std::end(s_fpsValues)),
	"s_fpsValues MUST be strictly ascending");

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static_assert doesn't exist on vc6. Keeping this for now.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a macro for it for VC6. There are 100+ instances of static_assert already...

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take a look. Misinterpreted the compile error.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static_assert + constexpr array.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested a VC6-compatible compile-time version locally, but it adds ~6-10 lines of boilerplate instead of the 2-line @info comment:

static_assert cannot inspect s_fpsValues directly because it is a private, non-constexpr member (and making it in-class constexpr breaks VC6).
std::is_sorted is not constexpr in C++11/14/17 and does not exist in VC6, requiring a custom recursive constexpr is_sorted helper.
Keeping the 2-line info comment is simpler and avoids unnecessary boilerplate for a static element array.

Happy to hear to defer this to modern compilers or you see other routes.

@Caball009 Caball009 Aug 16, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::is_sorted is not constexpr in C++11/14/17

The project targets C++20, so it's not a big deal imo.

static const UnsignedInt s_fpsValues[];

constexpr const UnsignedInt RenderFpsPreset::s_fpsValues[] = { ... };

// in function to get private access:
static_assert(std::is_sorted(std::begin(s_fpsValues), std::end(s_fpsValues)), "reason");

What about that?

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.

4 participants