mirror of
https://github.com/The-Powder-Toy/The-Powder-Toy.git
synced 2025-01-16 22:08:28 +01:00
Fix draw cap math
Floating point nonsense made the common case of draw cap = fps cap sometimes skip frames. This commit essentially duplicates the tick scheduling logic over to draw scheduling, with the exception that the draw schedule's "how much time to sleep" output goes nowhere; instead, we explicitly check whether we should be drawing a frame every tick.
This commit is contained in:
parent
c645269c86
commit
3b70324b78
@ -27,9 +27,46 @@ bool mouseDown = false;
|
|||||||
bool calculatedInitialMouse = false;
|
bool calculatedInitialMouse = false;
|
||||||
bool hasMouseMoved = false;
|
bool hasMouseMoved = false;
|
||||||
double correctedFrameTimeAvg = 0;
|
double correctedFrameTimeAvg = 0;
|
||||||
uint64_t drawingTimer = 0;
|
|
||||||
uint64_t frameStart = 0;
|
class FrameSchedule
|
||||||
uint64_t oldFrameStart = 0;
|
{
|
||||||
|
uint64_t startNs = 0;
|
||||||
|
uint64_t oldStartNs = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void SetNow(uint64_t nowNs)
|
||||||
|
{
|
||||||
|
oldStartNs = startNs;
|
||||||
|
startNs = nowNs;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t GetNow() const
|
||||||
|
{
|
||||||
|
return startNs;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t GetFrameTime() const
|
||||||
|
{
|
||||||
|
return startNs - oldStartNs;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Arm(float fps)
|
||||||
|
{
|
||||||
|
auto oldNowNs = startNs;
|
||||||
|
auto timeBlockDurationNs = uint64_t(UINT64_C(1'000'000'000) / fps);
|
||||||
|
auto oldStartTimeBlock = oldStartNs / timeBlockDurationNs;
|
||||||
|
auto startTimeBlock = oldStartTimeBlock + 1U;
|
||||||
|
startNs = std::max(startNs, startTimeBlock * timeBlockDurationNs);
|
||||||
|
return startNs - oldNowNs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasElapsed(uint64_t nowNs) const
|
||||||
|
{
|
||||||
|
return nowNs >= startNs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static FrameSchedule tickSchedule;
|
||||||
|
static FrameSchedule drawSchedule;
|
||||||
|
|
||||||
void StartTextInput()
|
void StartTextInput()
|
||||||
{
|
{
|
||||||
@ -396,17 +433,16 @@ static void EventProcess(const SDL_Event &event)
|
|||||||
void EngineProcess()
|
void EngineProcess()
|
||||||
{
|
{
|
||||||
auto &engine = ui::Engine::Ref();
|
auto &engine = ui::Engine::Ref();
|
||||||
auto correctedFrameTime = frameStart - oldFrameStart;
|
auto correctedFrameTime = tickSchedule.GetFrameTime();
|
||||||
drawingTimer += correctedFrameTime;
|
|
||||||
correctedFrameTimeAvg = correctedFrameTimeAvg + (correctedFrameTime - correctedFrameTimeAvg) * 0.05;
|
correctedFrameTimeAvg = correctedFrameTimeAvg + (correctedFrameTime - correctedFrameTimeAvg) * 0.05;
|
||||||
if (correctedFrameTime && frameStart - lastFpsUpdate > UINT64_C(200'000'000))
|
if (correctedFrameTime && tickSchedule.GetNow() - lastFpsUpdate > UINT64_C(200'000'000))
|
||||||
{
|
{
|
||||||
engine.SetFps(1e9f / correctedFrameTimeAvg);
|
engine.SetFps(1e9f / correctedFrameTimeAvg);
|
||||||
lastFpsUpdate = frameStart;
|
lastFpsUpdate = tickSchedule.GetNow();
|
||||||
}
|
}
|
||||||
if (frameStart - lastTick > UINT64_C(100'000'000))
|
if (tickSchedule.GetNow() - lastTick > UINT64_C(100'000'000))
|
||||||
{
|
{
|
||||||
lastTick = frameStart;
|
lastTick = tickSchedule.GetNow();
|
||||||
TickClient();
|
TickClient();
|
||||||
}
|
}
|
||||||
if (showLargeScreenDialog)
|
if (showLargeScreenDialog)
|
||||||
@ -423,24 +459,28 @@ void EngineProcess()
|
|||||||
|
|
||||||
engine.Tick();
|
engine.Tick();
|
||||||
|
|
||||||
auto fpsLimit = ui::Engine::Ref().GetFpsLimit();
|
|
||||||
int drawcap = ui::Engine::Ref().GetDrawingFrequencyLimit();
|
|
||||||
if (!drawcap || drawingTimer > 1e9f / drawcap)
|
|
||||||
{
|
{
|
||||||
engine.Draw();
|
int drawcap = ui::Engine::Ref().GetDrawingFrequencyLimit();
|
||||||
drawingTimer = 0;
|
auto nowNs = uint64_t(SDL_GetTicks()) * UINT64_C(1'000'000);
|
||||||
SDLSetScreen();
|
if (!drawcap || drawSchedule.HasElapsed(nowNs))
|
||||||
blit(engine.g->Data());
|
{
|
||||||
|
engine.Draw();
|
||||||
|
drawSchedule.SetNow(nowNs);
|
||||||
|
if (drawcap)
|
||||||
|
{
|
||||||
|
drawSchedule.Arm(float(drawcap));
|
||||||
|
}
|
||||||
|
SDLSetScreen();
|
||||||
|
blit(engine.g->Data());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
auto now = uint64_t(SDL_GetTicks()) * UINT64_C(1'000'000);
|
|
||||||
oldFrameStart = frameStart;
|
|
||||||
frameStart = now;
|
|
||||||
if (auto *fpsLimitExplicit = std::get_if<FpsLimitExplicit>(&fpsLimit))
|
|
||||||
{
|
{
|
||||||
auto timeBlockDuration = uint64_t(UINT64_C(1'000'000'000) / fpsLimitExplicit->value);
|
auto fpsLimit = ui::Engine::Ref().GetFpsLimit();
|
||||||
auto oldFrameStartTimeBlock = oldFrameStart / timeBlockDuration;
|
auto nowNs = uint64_t(SDL_GetTicks()) * UINT64_C(1'000'000);
|
||||||
auto frameStartTimeBlock = oldFrameStartTimeBlock + 1U;
|
tickSchedule.SetNow(nowNs);
|
||||||
frameStart = std::max(frameStart, frameStartTimeBlock * timeBlockDuration);
|
if (auto *fpsLimitExplicit = std::get_if<FpsLimitExplicit>(&fpsLimit))
|
||||||
SDL_Delay((frameStart - now) / UINT64_C(1'000'000));
|
{
|
||||||
|
SDL_Delay(tickSchedule.Arm(fpsLimitExplicit->value) / UINT64_C(1'000'000));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user