From da253d3718af2f359eb8ba1549c4d162c4b0dd57 Mon Sep 17 00:00:00 2001 From: Mark Olsen Date: Mon, 6 Mar 2023 08:16:04 +0000 Subject: [PATCH] Fixes to the framelimiter to make it more accurate. This change improves the accuracy of the framelimiter so it yields the expected result. Before this change, it was impossible to actually achieve the default maximum framerate of 120 FPS. Please note that the Windows code does not have a sleep function with a high enough granularity to take advantage of the improved precision, so Windows builds will still have an imprecise FPS cap. --- common/framelimit.cpp | 13 +++++++------ common/mssleep.h | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/common/framelimit.cpp b/common/framelimit.cpp index 8a202af5..64105dcd 100644 --- a/common/framelimit.cpp +++ b/common/framelimit.cpp @@ -48,12 +48,13 @@ void Frame_Limiter(FrameLimitFlags flags) #else auto frame_end = std::chrono::steady_clock::now(); #endif - int64_t _ms_per_tick = 1000 / Settings.Video.FrameLimit; - auto remaining = - _ms_per_tick - std::chrono::duration_cast(frame_end - frame_start).count(); - if (remaining > 0) { - ms_sleep(unsigned(remaining)); + unsigned int min_frame_time = 1000000 / Settings.Video.FrameLimit; + auto cur_frame_time = std::chrono::duration_cast(frame_end - frame_start).count(); + if (cur_frame_time < min_frame_time) { + frame_start += std::chrono::microseconds{min_frame_time}; + us_sleep(min_frame_time - cur_frame_time); + } else { + frame_start = frame_end; } - frame_start = std::chrono::steady_clock::now(); } } diff --git a/common/mssleep.h b/common/mssleep.h index ef0dc26d..9f7c8a66 100644 --- a/common/mssleep.h +++ b/common/mssleep.h @@ -16,6 +16,21 @@ #include #endif +/** + * Yield the current thread for at least us microseconds. + */ +static inline void us_sleep(unsigned us) +{ +#ifdef _WIN32 + Sleep((us + 999) / 1000); +#else + struct timespec ts; + ts.tv_sec = us / 1000000; + ts.tv_nsec = (us % 1000000) * 1000; + nanosleep(&ts, NULL); +#endif +} + /** * Yield the current thread for at least ms milliseconds. */