From 820c6e0134c9b34b7f9f9631102b2efbb5adcf0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 31 Aug 2026 12:55:14 +0200 Subject: [PATCH] TimeUtil: fix the generic fallback platform's clocks This branch is live on the Switch and the BSDs - ppsspp_config.h only maps LINUX to Android and Linux - and three of its functions didn't do what their callers assume. time_now_raw() is documented and used as nanoseconds, and from_time_raw() scales it by 1/nanos, but this built a double of *seconds* and returned it through the uint64_t return type: the fraction was truncated away and the result was off by a factor of a billion. Return real monotonic nanoseconds, like every other platform branch. time_now_unix_utc() just forwarded to time_now_raw(), which is now explicitly a monotonic clock with no relation to the epoch (and before this, was seconds truncated to a whole number). Read the wall clock. Instant took gettimeofday's tv_usec into nsecs_ and stored tv_sec as the start, while ElapsedNanos() subtracts those from clock_gettime(CLOCK_MONOTONIC) - so it mixed two different clocks *and* two different units, then applied a microsecond borrow to a nanosecond difference. Elapsed times were nonsense and could come out negative. --- Common/TimeUtil.cpp | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/Common/TimeUtil.cpp b/Common/TimeUtil.cpp index e01c3108e8..b508a30c92 100644 --- a/Common/TimeUtil.cpp +++ b/Common/TimeUtil.cpp @@ -238,12 +238,12 @@ double time_now_d() { } uint64_t time_now_raw() { - struct timeval tv; - gettimeofday(&tv, nullptr); - if (start == 0) { - start = tv.tv_sec; - } - return (double)tv.tv_sec + (double)tv.tv_usec * (1.0 / micros); + // Nanoseconds, like the other platforms - from_time_raw() scales by 1/nanos. This used to + // build a double of seconds and return it through the uint64_t, so it both lost the fraction + // and was off by a factor of a billion. + struct timespec tp; + clock_gettime(CLOCK_MONOTONIC, &tp); + return (uint64_t)tp.tv_sec * 1000000000ULL + tp.tv_nsec; } double from_time_raw(uint64_t raw_time) { @@ -257,7 +257,10 @@ double from_time_raw_relative(uint64_t raw_time) { void yield() {} double time_now_unix_utc() { - return time_now_raw(); + // Not time_now_raw() - that's a monotonic clock with no relation to the epoch. + struct timeval tv; + gettimeofday(&tv, nullptr); + return (double)tv.tv_sec + (double)tv.tv_usec * (1.0 / micros); } double time_to_unix_utc(double t) { @@ -267,10 +270,12 @@ double time_to_unix_utc(double t) { } Instant::Instant() { - struct timeval tv; - gettimeofday(&tv, nullptr); - nativeStart_ = tv.tv_sec; - nsecs_ = tv.tv_usec; + // Has to be the same clock, and the same unit, as ElapsedNanos() below: this took the wall + // clock in microseconds while that one subtracts it from a monotonic clock in nanoseconds. + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + nativeStart_ = ts.tv_sec; + nsecs_ = ts.tv_nsec; } int64_t Instant::ElapsedNanos() const { @@ -278,12 +283,12 @@ int64_t Instant::ElapsedNanos() const { clock_gettime(CLOCK_MONOTONIC, &ts); int64_t secs = ts.tv_sec - nativeStart_; - int64_t usecs = ts.tv_nsec - nsecs_; - if (usecs < 0) { + int64_t nsecs = ts.tv_nsec - nsecs_; + if (nsecs < 0) { secs--; - usecs += 1000000; + nsecs += 1000000000; } - return secs * 1000000000 + usecs * 1000; + return secs * 1000000000 + nsecs; } double Instant::ElapsedSeconds() const {