summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJMC47 <JMC4789@gmail.com>2025-08-28 13:58:26 -0400
committerGitHub <noreply@github.com>2025-08-28 13:58:26 -0400
commite0c72cd963bbde29f01fa33b06c22f7dff871e5d (patch)
tree3cbd60189a9aa5aa6174ac3ad7cb91027b6b481a
parentdce27fb58a9f071c707ad31085611cc3d72bad33 (diff)
parent81ebf45c9ba652083446929eea551dc8ba2f05be (diff)
Merge pull request #13825 from jordan-woyak/dont-count-playtime-while-suspended
Common/Timer: Add a SteadyAwakeClock class to make play time tracking ignore time while suspended.
-rw-r--r--Source/Core/Common/Timer.cpp34
-rw-r--r--Source/Core/Common/Timer.h15
-rw-r--r--Source/Core/Core/HW/CPU.cpp4
3 files changed, 51 insertions, 2 deletions
diff --git a/Source/Core/Common/Timer.cpp b/Source/Core/Common/Timer.cpp
index 0a23080c6f..0e260b833c 100644
--- a/Source/Core/Common/Timer.cpp
+++ b/Source/Core/Common/Timer.cpp
@@ -5,6 +5,7 @@
#include <chrono>
#include <thread>
+#include "Common/CommonFuncs.h"
#ifdef _WIN32
#include <Windows.h>
@@ -194,4 +195,37 @@ void PrecisionTimer::SleepUntil(Clock::time_point target)
}
}
+// Results are appropriately slewed on Linux, but not on Windows, macOS, or FreeBSD.
+// Clocks with that functionality seem to not be available there.
+auto SteadyAwakeClock::now() -> time_point
+{
+#if defined(_WIN32)
+ // The count is system time "in units of 100 nanoseconds".
+ using InterruptDuration = std::chrono::duration<ULONGLONG, std::ratio<100, std::nano::den>::type>;
+
+ ULONGLONG interrupt_time{};
+ if (!QueryUnbiasedInterruptTime(&interrupt_time))
+ ERROR_LOG_FMT(COMMON, "QueryUnbiasedInterruptTime");
+
+ return time_point{InterruptDuration{interrupt_time}};
+#else
+ // Note that Linux's CLOCK_MONOTONIC "does not count time that the system is suspended".
+ // This is in contrast to the behavior on macOS and FreeBSD.
+ static constexpr auto clock_id =
+#if defined(__linux__)
+ CLOCK_MONOTONIC;
+#elif defined(__APPLE__)
+ CLOCK_UPTIME_RAW;
+#else
+ CLOCK_UPTIME;
+#endif
+
+ timespec ts{};
+ if (clock_gettime(clock_id, &ts) != 0)
+ ERROR_LOG_FMT(COMMON, "clock_gettime: {}", LastStrerrorString());
+
+ return time_point{std::chrono::seconds{ts.tv_sec} + std::chrono::nanoseconds{ts.tv_nsec}};
+#endif
+}
+
} // Namespace Common
diff --git a/Source/Core/Common/Timer.h b/Source/Core/Common/Timer.h
index d49a599015..3f5a51ad7f 100644
--- a/Source/Core/Common/Timer.h
+++ b/Source/Core/Common/Timer.h
@@ -50,4 +50,19 @@ private:
#endif
};
+// Similar to std::chrono::steady_clock except this clock
+// specifically does *not* count time while the system is suspended.
+class SteadyAwakeClock
+{
+public:
+ using rep = s64;
+ using period = std::nano;
+ using duration = std::chrono::duration<rep, period>;
+ using time_point = std::chrono::time_point<SteadyAwakeClock>;
+
+ static constexpr bool is_steady = true;
+
+ static time_point now();
+};
+
} // Namespace Common
diff --git a/Source/Core/Core/HW/CPU.cpp b/Source/Core/Core/HW/CPU.cpp
index 568e3b3894..82b0cb9c4b 100644
--- a/Source/Core/Core/HW/CPU.cpp
+++ b/Source/Core/Core/HW/CPU.cpp
@@ -72,8 +72,8 @@ void CPUManager::StartTimePlayedTimer()
{
Common::SetCurrentThreadName("Play Time Tracker");
- // Steady clock for greater accuracy of timing
- std::chrono::steady_clock timer;
+ // Use a clock that will appropriately ignore suspended system time.
+ Common::SteadyAwakeClock timer;
auto prev_time = timer.now();
while (true)