From c6d6a7d0bf8f59f42f3a91d71ea2b5992155651c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20B=C3=A1lint=20Misius?= Date: Thu, 5 Jan 2023 08:24:27 +0100 Subject: [PATCH] Cap FPS in a more stable way Time-related variables are now on the nanosecond scale. See #886. --- src/PowderToySDL.cpp | 46 ++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/PowderToySDL.cpp b/src/PowderToySDL.cpp index 745f1d4a6..6e4bb29c4 100644 --- a/src/PowderToySDL.cpp +++ b/src/PowderToySDL.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #ifdef WIN #include #endif @@ -298,8 +299,8 @@ unsigned int GetTicks() } int elapsedTime = 0, currentTime = 0, lastTime = 0, currentFrame = 0; -unsigned int lastTick = 0; -unsigned int lastFpsUpdate = 0; +uint64_t lastTick = 0; +uint64_t lastFpsUpdate = 0; float fps = 0; ui::Engine * engine = NULL; bool showLargeScreenDialog = false; @@ -467,18 +468,14 @@ void LargeScreenDialog() void EngineProcess() { - double frameTimeAvg = 0.0f, correctedFrameTimeAvg = 0.0f; + double correctedFrameTimeAvg = 0; SDL_Event event; - int drawingTimer = 0; - int frameStart = 0; + uint64_t drawingTimer = 0; + auto frameStart = uint64_t(SDL_GetTicks()) * UINT64_C(1'000'000); while(engine->Running()) { - int oldFrameStart = frameStart; - frameStart = SDL_GetTicks(); - drawingTimer += frameStart - oldFrameStart; - if(engine->Broken()) { engine->UnBreak(); break; } event.type = 0; while (SDL_PollEvent(&event)) @@ -491,7 +488,7 @@ void EngineProcess() engine->Tick(); int drawcap = ui::Engine::Ref().GetDrawingFrequencyLimit(); - if (!drawcap || drawingTimer > 1000.f/drawcap) + if (!drawcap || drawingTimer > 1e9f / drawcap) { engine->Draw(); drawingTimer = 0; @@ -506,24 +503,27 @@ void EngineProcess() blit(engine->g->vid); } - - int frameTime = SDL_GetTicks() - frameStart; - frameTimeAvg = frameTimeAvg * 0.8 + frameTime * 0.2; - float fpsLimit = ui::Engine::Ref().FpsLimit; - if(fpsLimit > 2) + auto fpsLimit = ui::Engine::Ref().FpsLimit; + auto now = uint64_t(SDL_GetTicks()) * UINT64_C(1'000'000); + auto oldFrameStart = frameStart; + frameStart = now; + if (fpsLimit > 2) { - double offset = 1000.0 / fpsLimit - frameTimeAvg; - if(offset > 0) - SDL_Delay(Uint32(offset + 0.5)); + auto timeBlockDuration = uint64_t(UINT64_C(1'000'000'000) / fpsLimit); + auto oldFrameStartTimeBlock = oldFrameStart / timeBlockDuration; + auto frameStartTimeBlock = oldFrameStartTimeBlock + 1U; + frameStart = std::max(frameStart, frameStartTimeBlock * timeBlockDuration); + SDL_Delay((frameStart - now) / UINT64_C(1'000'000)); } - int correctedFrameTime = SDL_GetTicks() - frameStart; - correctedFrameTimeAvg = correctedFrameTimeAvg * 0.95 + correctedFrameTime * 0.05; - if (frameStart - lastFpsUpdate > 200) + auto correctedFrameTime = frameStart - oldFrameStart; + drawingTimer += correctedFrameTime; + correctedFrameTimeAvg = correctedFrameTimeAvg + (correctedFrameTime - correctedFrameTimeAvg) * 0.05; + if (frameStart - lastFpsUpdate > UINT64_C(200'000'000)) { - engine->SetFps(1000.0 / correctedFrameTimeAvg); + engine->SetFps(1e9f / correctedFrameTimeAvg); lastFpsUpdate = frameStart; } - if (frameStart - lastTick > 100) + if (frameStart - lastTick > UINT64_C(100'000'000)) { lastTick = frameStart; Client::Ref().Tick();