From 8a046832eb3704d7cc6835e7c39a8e611635ef6a Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 6 Apr 2025 20:25:58 -0600 Subject: [PATCH] Improve frame limiter on Windows --- Runtime/CMain.cpp | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/Runtime/CMain.cpp b/Runtime/CMain.cpp index 7fc8b7677..e2acea5f4 100644 --- a/Runtime/CMain.cpp +++ b/Runtime/CMain.cpp @@ -81,29 +81,35 @@ private: } #if _WIN32 - bool m_initialized; - double m_countPerNs; - void NanoSleep(const duration_t duration) { - if (!m_initialized) { + static bool initialized = false; + static double countPerNs; + static size_t numSleeps = 0; + + // QueryPerformanceFrequency's result is constant, but calling it occasionally + // appears to stabilize QueryPerformanceCounter. Without it, the game drifts + // from 60hz to 144hz. (Cursed, but I suspect it's NVIDIA/G-SYNC related) + if (!initialized || numSleeps++ % 1000 == 0) { LARGE_INTEGER freq; - QueryPerformanceFrequency(&freq); - m_countPerNs = static_cast(freq.QuadPart) / 1000000000.0; - m_initialized = true; + if (QueryPerformanceFrequency(&freq) == 0) { + spdlog::warn("QueryPerformanceFrequency failed: {}", GetLastError()); + return; + } + countPerNs = static_cast(freq.QuadPart) / 1e9; + initialized = true; + numSleeps = 0; } - DWORD ms = std::chrono::duration_cast(duration).count(); - auto tickCount = static_cast(static_cast(duration.count()) * m_countPerNs); - LARGE_INTEGER count; - QueryPerformanceCounter(&count); - if (ms > 10) { - // Adjust for Sleep overhead - ::Sleep(ms - 10); + LARGE_INTEGER start, current; + QueryPerformanceCounter(&start); + LONGLONG ticksToWait = static_cast(duration.count() * countPerNs); + if (DWORD ms = std::chrono::duration_cast(duration).count(); ms > 1) { + ::Sleep(ms - 1); } - auto end = count.QuadPart + tickCount; do { - QueryPerformanceCounter(&count); - } while (count.QuadPart < end); + QueryPerformanceCounter(¤t); + _mm_pause(); // Yield CPU + } while (current.QuadPart - start.QuadPart < ticksToWait); } #else void NanoSleep(const duration_t duration) { std::this_thread::sleep_for(duration); }