-
Notifications
You must be signed in to change notification settings - Fork 199
feat(frame-pacer): overhaul logic and render FPS limits and presets #2699
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,9 +60,11 @@ Real FrameRateLimit::wait(UnsignedInt maxFps) | |
|
|
||
|
|
||
| const UnsignedInt RenderFpsPreset::s_fpsValues[] = { | ||
| 30, 50, 56, 60, 65, 70, 72, 75, 80, 85, 90, 100, 110, 120, 144, 240, 480, UncappedFpsValue }; | ||
| 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. | ||
| const UnsignedInt LogicTimeScaleFpsPreset::s_fpsValues[] = { | ||
| 1, 5, 15, 30, 45, 60, 75, 90, 105, 120, 240, 480, 960, RenderFpsPreset::UncappedFpsValue }; | ||
|
|
||
| UnsignedInt RenderFpsPreset::getNextFpsValue(UnsignedInt value) | ||
| { | ||
|
|
@@ -102,30 +104,66 @@ UnsignedInt RenderFpsPreset::changeFpsValue(UnsignedInt value, FpsValueChange ch | |
| } | ||
| } | ||
|
|
||
|
|
||
| UnsignedInt LogicTimeScaleFpsPreset::getNextFpsValue(UnsignedInt value) | ||
| UnsignedInt LogicTimeScaleFpsPreset::getNextFpsValue(UnsignedInt value, UnsignedInt snapValue) | ||
| { | ||
| return value + StepFpsValue; | ||
| UnsignedInt nextValue = s_fpsValues[ARRAY_SIZE(s_fpsValues) - 1]; // Default to Uncapped | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| // Check if snapValue (e.g. current render FPS) is the next closest candidate | ||
| if (snapValue > value && snapValue < nextValue) | ||
| { | ||
| nextValue = snapValue; | ||
| } | ||
|
|
||
| // Check predefined steps | ||
| for (size_t i = 0; i < ARRAY_SIZE(s_fpsValues); ++i) | ||
| { | ||
| if (s_fpsValues[i] > value) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I could be wrong, but wouldn't add a slight performance increase, as otherwise the array value needs to be looked up three times? |
||
| { | ||
| if (s_fpsValues[i] < nextValue) | ||
| { | ||
| nextValue = s_fpsValues[i]; | ||
| } | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| return nextValue; | ||
| } | ||
|
|
||
| UnsignedInt LogicTimeScaleFpsPreset::getPrevFpsValue(UnsignedInt value) | ||
| UnsignedInt LogicTimeScaleFpsPreset::getPrevFpsValue(UnsignedInt value, UnsignedInt snapValue) | ||
| { | ||
| if (value - StepFpsValue < MinFpsValue) | ||
| UnsignedInt prevValue = s_fpsValues[0]; // Floor/seed for the search loop | ||
|
|
||
| // Check if snapValue (e.g. current render FPS) is the previous closest candidate. | ||
| // Note: if snapValue == value, neither branch below fires and the snap point is | ||
| // intentionally skipped — the caller must step to a different preset. | ||
| if (snapValue < value && snapValue > prevValue) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. swapping conditions
has better readability |
||
| { | ||
| return MinFpsValue; | ||
| prevValue = snapValue; | ||
| } | ||
| else | ||
|
|
||
| // Check predefined steps | ||
| for (int i = (int)ARRAY_SIZE(s_fpsValues) - 1; i >= 0; --i) | ||
| { | ||
| return value - StepFpsValue; | ||
| if (s_fpsValues[i] < value) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I could be wrong, but wouldn't add a slight performance increase, as otherwise the array value needs to be looked up three times? |
||
| { | ||
| if (s_fpsValues[i] > prevValue) | ||
| { | ||
| prevValue = s_fpsValues[i]; | ||
| } | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| return prevValue; | ||
| } | ||
|
|
||
| UnsignedInt LogicTimeScaleFpsPreset::changeFpsValue(UnsignedInt value, FpsValueChange change) | ||
| UnsignedInt LogicTimeScaleFpsPreset::changeFpsValue(UnsignedInt value, FpsValueChange change, UnsignedInt snapValue) | ||
| { | ||
| switch (change) | ||
| { | ||
| default: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default falls through to Better (if possible): |
||
| case FpsValueChange_Increase: return getNextFpsValue(value); | ||
| case FpsValueChange_Decrease: return getPrevFpsValue(value); | ||
| case FpsValueChange_Increase: return getNextFpsValue(value, snapValue); | ||
| case FpsValueChange_Decrease: return getPrevFpsValue(value, snapValue); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't default and case indented here? |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -211,37 +211,28 @@ bool changeLogicTimeScale(FpsValueChange change) | |
| return false; | ||
|
|
||
| const UnsignedInt maxRenderFps = TheFramePacer->getFramesPerSecondLimit(); | ||
| UnsignedInt maxRenderRemainder = LogicTimeScaleFpsPreset::StepFpsValue; | ||
| maxRenderRemainder -= maxRenderFps % LogicTimeScaleFpsPreset::StepFpsValue; | ||
| maxRenderRemainder %= LogicTimeScaleFpsPreset::StepFpsValue; | ||
|
|
||
| UnsignedInt logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps(); | ||
| // Set the value to the max render fps value plus a bit when time scale is | ||
| // disabled. This ensures that the time scale does not re-enable with a | ||
| // 'surprise' value. | ||
|
|
||
| if (!TheFramePacer->isLogicTimeScaleEnabled()) | ||
| { | ||
| logicTimeScaleFps = maxRenderFps + maxRenderRemainder; | ||
| logicTimeScaleFps = maxRenderFps; | ||
| } | ||
| // Ceil the value at the max render fps value plus a bit so that the next fps | ||
| // value decrease would undercut the max render fps at the correct step value. | ||
| // Example: render fps 72 -> logic value ceiled to 75 -> decreased to 70. | ||
| logicTimeScaleFps = min(logicTimeScaleFps, maxRenderFps + maxRenderRemainder); | ||
| logicTimeScaleFps = LogicTimeScaleFpsPreset::changeFpsValue(logicTimeScaleFps, change); | ||
|
|
||
| // Set value before potentially disabling it. | ||
| if (TheFramePacer->isLogicTimeScaleEnabled()) | ||
| logicTimeScaleFps = LogicTimeScaleFpsPreset::changeFpsValue(logicTimeScaleFps, change, maxRenderFps); | ||
|
|
||
| // Ensure logic FPS never exceeds render FPS | ||
| if (logicTimeScaleFps > maxRenderFps && logicTimeScaleFps != RenderFpsPreset::UncappedFpsValue) | ||
| { | ||
| TheFramePacer->setLogicTimeScaleFps(logicTimeScaleFps); | ||
| logicTimeScaleFps = maxRenderFps; | ||
| } | ||
|
|
||
| TheFramePacer->enableLogicTimeScale(logicTimeScaleFps < maxRenderFps); | ||
|
|
||
| // Set value after potentially enabling it. | ||
| if (TheFramePacer->isLogicTimeScaleEnabled()) | ||
| const bool enableTimescale = (logicTimeScaleFps < maxRenderFps); | ||
| // TheSuperHackers @info Preserve the last real FPS in m_logicTimeScaleFPS so re-enabling timescale resumes from a sane value. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add empty line between const bool declaration and comment for better readability |
||
| if (enableTimescale) | ||
| { | ||
| TheFramePacer->setLogicTimeScaleFps(logicTimeScaleFps); | ||
| } | ||
| TheFramePacer->enableLogicTimeScale(enableTimescale); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add empty line between closing bracket and TheFramePacer->enableLogicTimeScale for better readability. Optionally: remove whiteline below TheFramePacer->enableLogicTimeScale |
||
|
|
||
| logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps(); | ||
| const UnsignedInt actualLogicTimeScaleFps = TheFramePacer->getActualLogicTimeScaleFps(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -210,37 +210,28 @@ bool changeLogicTimeScale(FpsValueChange change) | |
| return false; | ||
|
|
||
| const UnsignedInt maxRenderFps = TheFramePacer->getFramesPerSecondLimit(); | ||
| UnsignedInt maxRenderRemainder = LogicTimeScaleFpsPreset::StepFpsValue; | ||
| maxRenderRemainder -= maxRenderFps % LogicTimeScaleFpsPreset::StepFpsValue; | ||
| maxRenderRemainder %= LogicTimeScaleFpsPreset::StepFpsValue; | ||
|
|
||
| UnsignedInt logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps(); | ||
| // Set the value to the max render fps value plus a bit when time scale is | ||
| // disabled. This ensures that the time scale does not re-enable with a | ||
| // 'surprise' value. | ||
|
|
||
| if (!TheFramePacer->isLogicTimeScaleEnabled()) | ||
| { | ||
| logicTimeScaleFps = maxRenderFps + maxRenderRemainder; | ||
| logicTimeScaleFps = maxRenderFps; | ||
| } | ||
| // Ceil the value at the max render fps value plus a bit so that the next fps | ||
| // value decrease would undercut the max render fps at the correct step value. | ||
| // Example: render fps 72 -> logic value ceiled to 75 -> decreased to 70. | ||
| logicTimeScaleFps = min(logicTimeScaleFps, maxRenderFps + maxRenderRemainder); | ||
| logicTimeScaleFps = LogicTimeScaleFpsPreset::changeFpsValue(logicTimeScaleFps, change); | ||
|
|
||
| // Set value before potentially disabling it. | ||
| if (TheFramePacer->isLogicTimeScaleEnabled()) | ||
| logicTimeScaleFps = LogicTimeScaleFpsPreset::changeFpsValue(logicTimeScaleFps, change, maxRenderFps); | ||
|
|
||
| // Ensure logic FPS never exceeds render FPS | ||
| if (logicTimeScaleFps > maxRenderFps && logicTimeScaleFps != RenderFpsPreset::UncappedFpsValue) | ||
| { | ||
| TheFramePacer->setLogicTimeScaleFps(logicTimeScaleFps); | ||
| logicTimeScaleFps = maxRenderFps; | ||
| } | ||
|
|
||
| TheFramePacer->enableLogicTimeScale(logicTimeScaleFps < maxRenderFps); | ||
|
|
||
| // Set value after potentially enabling it. | ||
| if (TheFramePacer->isLogicTimeScaleEnabled()) | ||
| const bool enableTimescale = (logicTimeScaleFps < maxRenderFps); | ||
| // TheSuperHackers @info Preserve the last real FPS in m_logicTimeScaleFPS so re-enabling timescale resumes from a sane value. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add empty line between const bool declaration and comment for better readability |
||
| if (enableTimescale) | ||
| { | ||
| TheFramePacer->setLogicTimeScaleFps(logicTimeScaleFps); | ||
| } | ||
| TheFramePacer->enableLogicTimeScale(enableTimescale); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add empty line between closing bracket and Optionally: remove whiteline below |
||
|
|
||
| logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps(); | ||
| const UnsignedInt actualLogicTimeScaleFps = TheFramePacer->getActualLogicTimeScaleFps(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.