Created
December 22, 2024 08:24
-
-
Save ripwin/8d10a7af1ccd2f51cdce53594cda5d26 to your computer and use it in GitHub Desktop.
Stuttering
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Stating point | |
/* Utilizes lockstep rendering. | |
* A big thanks to Tyler Glaiel for the sample code. | |
* https://github.com/TylerGlaiel/FrameTimingControl | |
* */ | |
void Application::run(std::unique_ptr<IGame> game) | |
{ | |
m_game = std::move(game); | |
auto const fixedStepTarget{ | |
std::chrono::nanoseconds{static_cast<int64_t>(1.0 / 60.0 * 1e9)}}; | |
auto const vSyncMaxError{ | |
std::chrono::nanoseconds{static_cast<int64_t>(0.0001 * 1e9)}}; | |
auto const windowRefreshRate{m_window->refreshRate()}; | |
auto const refreshRate{windowRefreshRate.has_value() | |
? windowRefreshRate.value() | |
: 60.0}; | |
// VSync multiples to snap delta time to | |
auto snapFrequencies{std::vector<std::chrono::nanoseconds>(8)}; | |
for (auto i{std::size_t{}}; i < snapFrequencies.size(); ++i) | |
{ | |
snapFrequencies[i] = std::chrono::nanoseconds{ | |
static_cast<int64_t>(static_cast<double>(i + 1) / refreshRate * 1e9)}; | |
} | |
// DeltaTime histories to calculate the average | |
auto const theoreticalDeltaTime{std::chrono::nanoseconds{ | |
static_cast<int64_t>(1.0 / refreshRate * 1e9)}}; | |
auto deltaTimeHistories{std::vector<std::chrono::nanoseconds>(4, theoreticalDeltaTime)}; | |
auto deltaTimeAverageResidual{std::chrono::nanoseconds{}}; | |
auto currentTime{std::chrono::steady_clock::now()}; | |
auto lastTime{std::chrono::steady_clock::now()}; | |
auto deltaTime{std::chrono::nanoseconds{0}}; | |
auto accumulator{std::chrono::nanoseconds{0}}; | |
while (!m_window->isClosing()) | |
{ | |
currentTime = std::chrono::steady_clock::now(); | |
deltaTime = currentTime - lastTime; | |
lastTime = currentTime; | |
// Cap unexpected time anomalies | |
if (deltaTime > theoreticalDeltaTime * 8) | |
{ | |
deltaTime = theoreticalDeltaTime; | |
} | |
else if (deltaTime < std::chrono::nanoseconds{0}) | |
{ | |
deltaTime = std::chrono::nanoseconds{0}; | |
} | |
// Snap to VSync | |
for (auto const& snap : snapFrequencies) | |
{ | |
auto difference{deltaTime - snap}; | |
if (difference < std::chrono::nanoseconds{}) | |
{ | |
difference = -difference; | |
} | |
if (difference < vSyncMaxError) | |
{ | |
deltaTime = snap; | |
break; | |
} | |
} | |
// DeltaTime history (shift and update) | |
for (auto i{std::size_t{}}; i < deltaTimeHistories.size() - 1; ++i) | |
{ | |
deltaTimeHistories[i] = deltaTimeHistories[i + 1]; | |
} | |
deltaTimeHistories.back() = deltaTime; | |
// DeltaTime average | |
auto totalDeltaTimeHistories{std::chrono::nanoseconds{0}}; | |
for (auto const& history : deltaTimeHistories) | |
{ | |
totalDeltaTimeHistories += history; | |
} | |
deltaTime = totalDeltaTimeHistories / deltaTimeHistories.size(); | |
deltaTimeAverageResidual += totalDeltaTimeHistories % deltaTimeHistories.size(); | |
deltaTime += deltaTimeAverageResidual / deltaTimeHistories.size(); | |
deltaTimeAverageResidual = deltaTimeAverageResidual % deltaTimeHistories.size(); | |
accumulator += deltaTime; | |
// Spiral of death protection | |
auto const maxAccumulator = theoreticalDeltaTime * 8; | |
if (accumulator > maxAccumulator) | |
{ | |
accumulator = maxAccumulator; | |
} | |
m_window->pollEvent(); | |
if (!m_window->isClosing()) | |
{ | |
// Update | |
while (accumulator >= fixedStepTarget) | |
{ | |
m_game->onUpdate( | |
std::chrono::duration_cast<std::chrono::duration<float>>(fixedStepTarget).count()); | |
accumulator -= fixedStepTarget; | |
} | |
m_game->onRender(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment