Cap FPS in a more stable way

Time-related variables are now on the nanosecond scale. See #886.
This commit is contained in:
Tamás Bálint Misius 2023-01-05 08:24:27 +01:00
parent 225293cc80
commit c6d6a7d0bf
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2

View File

@ -5,6 +5,7 @@
#include <optional> #include <optional>
#include <ctime> #include <ctime>
#include <climits> #include <climits>
#include <cstdint>
#ifdef WIN #ifdef WIN
#include <direct.h> #include <direct.h>
#endif #endif
@ -298,8 +299,8 @@ unsigned int GetTicks()
} }
int elapsedTime = 0, currentTime = 0, lastTime = 0, currentFrame = 0; int elapsedTime = 0, currentTime = 0, lastTime = 0, currentFrame = 0;
unsigned int lastTick = 0; uint64_t lastTick = 0;
unsigned int lastFpsUpdate = 0; uint64_t lastFpsUpdate = 0;
float fps = 0; float fps = 0;
ui::Engine * engine = NULL; ui::Engine * engine = NULL;
bool showLargeScreenDialog = false; bool showLargeScreenDialog = false;
@ -467,18 +468,14 @@ void LargeScreenDialog()
void EngineProcess() void EngineProcess()
{ {
double frameTimeAvg = 0.0f, correctedFrameTimeAvg = 0.0f; double correctedFrameTimeAvg = 0;
SDL_Event event; SDL_Event event;
int drawingTimer = 0; uint64_t drawingTimer = 0;
int frameStart = 0; auto frameStart = uint64_t(SDL_GetTicks()) * UINT64_C(1'000'000);
while(engine->Running()) while(engine->Running())
{ {
int oldFrameStart = frameStart;
frameStart = SDL_GetTicks();
drawingTimer += frameStart - oldFrameStart;
if(engine->Broken()) { engine->UnBreak(); break; } if(engine->Broken()) { engine->UnBreak(); break; }
event.type = 0; event.type = 0;
while (SDL_PollEvent(&event)) while (SDL_PollEvent(&event))
@ -491,7 +488,7 @@ void EngineProcess()
engine->Tick(); engine->Tick();
int drawcap = ui::Engine::Ref().GetDrawingFrequencyLimit(); int drawcap = ui::Engine::Ref().GetDrawingFrequencyLimit();
if (!drawcap || drawingTimer > 1000.f/drawcap) if (!drawcap || drawingTimer > 1e9f / drawcap)
{ {
engine->Draw(); engine->Draw();
drawingTimer = 0; drawingTimer = 0;
@ -506,24 +503,27 @@ void EngineProcess()
blit(engine->g->vid); blit(engine->g->vid);
} }
auto fpsLimit = ui::Engine::Ref().FpsLimit;
int frameTime = SDL_GetTicks() - frameStart; auto now = uint64_t(SDL_GetTicks()) * UINT64_C(1'000'000);
frameTimeAvg = frameTimeAvg * 0.8 + frameTime * 0.2; auto oldFrameStart = frameStart;
float fpsLimit = ui::Engine::Ref().FpsLimit; frameStart = now;
if (fpsLimit > 2) if (fpsLimit > 2)
{ {
double offset = 1000.0 / fpsLimit - frameTimeAvg; auto timeBlockDuration = uint64_t(UINT64_C(1'000'000'000) / fpsLimit);
if(offset > 0) auto oldFrameStartTimeBlock = oldFrameStart / timeBlockDuration;
SDL_Delay(Uint32(offset + 0.5)); auto frameStartTimeBlock = oldFrameStartTimeBlock + 1U;
frameStart = std::max(frameStart, frameStartTimeBlock * timeBlockDuration);
SDL_Delay((frameStart - now) / UINT64_C(1'000'000));
} }
int correctedFrameTime = SDL_GetTicks() - frameStart; auto correctedFrameTime = frameStart - oldFrameStart;
correctedFrameTimeAvg = correctedFrameTimeAvg * 0.95 + correctedFrameTime * 0.05; drawingTimer += correctedFrameTime;
if (frameStart - lastFpsUpdate > 200) 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; lastFpsUpdate = frameStart;
} }
if (frameStart - lastTick > 100) if (frameStart - lastTick > UINT64_C(100'000'000))
{ {
lastTick = frameStart; lastTick = frameStart;
Client::Ref().Tick(); Client::Ref().Tick();