From 4fac50d2e57d7764b1559dec1d468422b0392c3f Mon Sep 17 00:00:00 2001 From: Sais Date: Sat, 25 Jul 2026 02:20:03 -0400 Subject: [PATCH] Back QueryPerformanceCounter with a monotonic clock The linux shim sampled gettimeofday(), which reports CLOCK_REALTIME and can be slewed or stepped backwards by time synchronisation. Clock::update() subtracts successive samples to derive frame time, so a backwards step produced a negative elapsed time, which AlterScheduler then reported as "no forward time movement" once per affected frame. Use clock_gettime(CLOCK_MONOTONIC) and keep microsecond units so the value still matches QueryPerformanceFrequency(). Falls back to gettimeofday() if the call fails. All getFrameStartTimeMs() consumers compare values relatively, so nothing depends on the previous epoch-based origin. --- .../src/linux/PlatformGlue.cpp | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/engine/shared/library/sharedFoundation/src/linux/PlatformGlue.cpp b/engine/shared/library/sharedFoundation/src/linux/PlatformGlue.cpp index 7937a2a7..9577df85 100755 --- a/engine/shared/library/sharedFoundation/src/linux/PlatformGlue.cpp +++ b/engine/shared/library/sharedFoundation/src/linux/PlatformGlue.cpp @@ -27,10 +27,23 @@ char* _itoa(int value, char* stringOut, int radix) bool QueryPerformanceCounter(__int64* time) { - struct timeval tv; - gettimeofday(&tv, nullptr); - *time = static_cast(tv.tv_sec); - *time = (*time * 1000000) + static_cast(tv.tv_usec); + // Must be monotonic. gettimeofday() reports CLOCK_REALTIME, which NTP can + // slew or step backwards; callers subtract successive samples to derive + // frame time, so a backwards step produced negative elapsed times (see + // AlterScheduler "no forward time movement"). CLOCK_MONOTONIC never goes + // backwards. Units stay microseconds to match QueryPerformanceFrequency(). + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) + { + struct timeval tv; + gettimeofday(&tv, nullptr); + *time = static_cast(tv.tv_sec); + *time = (*time * 1000000) + static_cast(tv.tv_usec); + return TRUE; + } + + *time = static_cast(ts.tv_sec); + *time = (*time * 1000000) + static_cast(ts.tv_nsec / 1000); return TRUE; }