From 09089eeee0bf6022cc7681214e51546d134402ea Mon Sep 17 00:00:00 2001 From: Shawn Hoffman Date: Sun, 17 Jul 2022 20:43:47 -0700 Subject: Common::Timer: use chrono::steady_clock internally --- Source/Core/Common/Timer.cpp | 216 +++++++++++-------------------------------- 1 file changed, 53 insertions(+), 163 deletions(-) (limited to 'Source/Core/Common/Timer.cpp') diff --git a/Source/Core/Common/Timer.cpp b/Source/Core/Common/Timer.cpp index f6d4c983cb..9ac3645841 100644 --- a/Source/Core/Common/Timer.cpp +++ b/Source/Core/Common/Timer.cpp @@ -9,13 +9,14 @@ #ifdef _WIN32 #include -#include +#include #include #include #else #include #endif +#include #include #include "Common/CommonTypes.h" @@ -23,157 +24,67 @@ namespace Common { -u32 Timer::GetTimeMs() +template +static typename Clock::rep time_now() { -#ifdef _WIN32 - return timeGetTime(); -#elif defined __APPLE__ - struct timeval t; - (void)gettimeofday(&t, nullptr); - return ((u32)(t.tv_sec * 1000 + t.tv_usec / 1000)); -#else - struct timespec t; - (void)clock_gettime(CLOCK_MONOTONIC, &t); - return ((u32)(t.tv_sec * 1000 + t.tv_nsec / 1000000)); -#endif + return std::chrono::time_point_cast(Clock::now()).time_since_epoch().count(); } -#ifdef _WIN32 -double GetFreq() +template +static auto steady_time_now() { - LARGE_INTEGER freq; - QueryPerformanceFrequency(&freq); - return 1000000.0 / double(freq.QuadPart); + return time_now(); } -#endif -u64 Timer::GetTimeUs() +u64 Timer::NowUs() { -#ifdef _WIN32 - LARGE_INTEGER time; - static double freq = GetFreq(); - QueryPerformanceCounter(&time); - return u64(double(time.QuadPart) * freq); -#elif defined __APPLE__ - struct timeval t; - (void)gettimeofday(&t, nullptr); - return ((u64)(t.tv_sec * 1000000 + t.tv_usec)); -#else - struct timespec t; - (void)clock_gettime(CLOCK_MONOTONIC, &t); - return ((u64)(t.tv_sec * 1000000 + t.tv_nsec / 1000)); -#endif + return steady_time_now(); } -// -------------------------------------------- -// Initiate, Start, Stop, and Update the time -// -------------------------------------------- - -// Set initial values for the class -Timer::Timer() : m_LastTime(0), m_StartTime(0), m_Running(false) +u64 Timer::NowMs() { - Update(); + return steady_time_now(); } -// Write the starting time void Timer::Start() { - m_StartTime = GetTimeMs(); - m_Running = true; + m_start_ms = NowMs(); + m_end_ms = 0; + m_running = true; } -// Stop the timer -void Timer::Stop() +void Timer::StartWithOffset(u64 offset) { - // Write the final time - m_LastTime = GetTimeMs(); - m_Running = false; + Start(); + if (m_start_ms > offset) + m_start_ms -= offset; } -// Update the last time variable -void Timer::Update() -{ - m_LastTime = GetTimeMs(); - // TODO(ector) - QPF -} - -// ------------------------------------- -// Get time difference and elapsed time -// ------------------------------------- - -// Get the number of milliseconds since the last Update() -u64 Timer::GetTimeDifference() -{ - return GetTimeMs() - m_LastTime; -} - -// Add the time difference since the last Update() to the starting time. -// This is used to compensate for a paused game. -void Timer::AddTimeDifference() +void Timer::Stop() { - m_StartTime += GetTimeDifference(); + m_end_ms = NowMs(); + m_running = false; } -// Get the time elapsed since the Start() -u64 Timer::GetTimeElapsed() +u64 Timer::ElapsedMs() const { // If we have not started yet, return zero - if (m_StartTime == 0) + if (m_start_ms == 0) return 0; - // Return the final timer time if the timer is stopped - if (!m_Running) - return (m_LastTime - m_StartTime); - - return (GetTimeMs() - m_StartTime); -} - -// Get the formatted time elapsed since the Start() -std::string Timer::GetTimeElapsedFormatted() const -{ - // If we have not started yet, return zero - if (m_StartTime == 0) - return "00:00:00:000"; - - // The number of milliseconds since the start. - // Use a different value if the timer is stopped. - u64 Milliseconds; - if (m_Running) - Milliseconds = GetTimeMs() - m_StartTime; + if (m_running) + { + u64 now = NowMs(); + if (m_start_ms >= now) + return 0; + return now - m_start_ms; + } else - Milliseconds = m_LastTime - m_StartTime; - // Seconds - u32 Seconds = (u32)(Milliseconds / 1000); - // Minutes - u32 Minutes = Seconds / 60; - // Hours - u32 Hours = Minutes / 60; - - return fmt::format("{:02}:{:02}:{:02}:{:03}", Hours, Minutes % 60, Seconds % 60, - Milliseconds % 1000); -} - -// Get current time -void Timer::IncreaseResolution() -{ -#ifdef _WIN32 - timeBeginPeriod(1); -#endif -} - -void Timer::RestoreResolution() -{ -#ifdef _WIN32 - timeEndPeriod(1); -#endif -} - -// Get the number of seconds since January 1 1970 -u64 Timer::GetTimeSinceJan1970() -{ - time_t ltime; - time(<ime); - return ((u64)ltime); + { + if (m_start_ms >= m_end_ms) + return 0; + return m_end_ms - m_start_ms; + } } u64 Timer::GetLocalTimeSinceJan1970() @@ -195,41 +106,7 @@ u64 Timer::GetLocalTimeSinceJan1970() return static_cast(sysTime + tzDiff + tzDST); } -// Return the current time formatted as Minutes:Seconds:Milliseconds -// in the form 00:00:000. -std::string Timer::GetTimeFormatted() -{ - time_t sysTime; - time(&sysTime); - - struct tm* gmTime = localtime(&sysTime); - -#ifdef _WIN32 - wchar_t tmp[13]; - wcsftime(tmp, 6, L"%M:%S", gmTime); -#else - char tmp[13]; - strftime(tmp, 6, "%M:%S", gmTime); -#endif - -// Now tack on the milliseconds -#ifdef _WIN32 - struct timeb tp; - (void)::ftime(&tp); - return WStringToUTF8(tmp) + fmt::format(":{:03}", tp.millitm); -#elif defined __APPLE__ - struct timeval t; - (void)gettimeofday(&t, nullptr); - return fmt::format("{}:{:03}", tmp, t.tv_usec / 1000); -#else - struct timespec t; - (void)clock_gettime(CLOCK_MONOTONIC, &t); - return fmt::format("{}:{:03}", tmp, t.tv_nsec / 1000000); -#endif -} - -// Returns a timestamp with decimals for precise time comparisons -double Timer::GetDoubleTime() +double Timer::GetSystemTimeAsDouble() { // FYI: std::chrono::system_clock epoch is not required to be 1970 until c++20. // We will however assume time_t IS unix time. @@ -244,10 +121,9 @@ double Timer::GetDoubleTime() return std::chrono::duration_cast>(since_double_time_epoch).count(); } -// Formats a timestamp from GetDoubleTime() into a date and time string -std::string Timer::GetDateTimeFormatted(double time) +std::string Timer::SystemTimeAsDoubleToString(double time) { - // revert adjustments from GetDoubleTime() to get a normal Unix timestamp again + // revert adjustments from GetSystemTimeAsDouble() to get a normal Unix timestamp again time_t seconds = (time_t)time + DOUBLE_TIME_OFFSET; tm* localTime = localtime(&seconds); @@ -262,4 +138,18 @@ std::string Timer::GetDateTimeFormatted(double time) #endif } +void Timer::IncreaseResolution() +{ +#ifdef _WIN32 + timeBeginPeriod(1); +#endif +} + +void Timer::RestoreResolution() +{ +#ifdef _WIN32 + timeEndPeriod(1); +#endif +} + } // Namespace Common -- cgit v1.2.3 From 3384b1385edba826f56dec3c92c01426e1fcd48d Mon Sep 17 00:00:00 2001 From: Shawn Hoffman Date: Sun, 17 Jul 2022 20:59:31 -0700 Subject: move "double time" code into State from Timer Ideally the statesave format could be changed to just store a u64 in the future --- Source/Core/Common/Timer.cpp | 43 ++----------------------------------------- 1 file changed, 2 insertions(+), 41 deletions(-) (limited to 'Source/Core/Common/Timer.cpp') diff --git a/Source/Core/Common/Timer.cpp b/Source/Core/Common/Timer.cpp index 9ac3645841..ef910f6c27 100644 --- a/Source/Core/Common/Timer.cpp +++ b/Source/Core/Common/Timer.cpp @@ -4,23 +4,16 @@ #include "Common/Timer.h" #include -#include #ifdef _WIN32 -#include - #include -#include -#include +#include +#include #else #include #endif -#include -#include - #include "Common/CommonTypes.h" -#include "Common/StringUtil.h" namespace Common { @@ -106,38 +99,6 @@ u64 Timer::GetLocalTimeSinceJan1970() return static_cast(sysTime + tzDiff + tzDST); } -double Timer::GetSystemTimeAsDouble() -{ - // FYI: std::chrono::system_clock epoch is not required to be 1970 until c++20. - // We will however assume time_t IS unix time. - using Clock = std::chrono::system_clock; - - // TODO: Use this on switch to c++20: - // const auto since_epoch = Clock::now().time_since_epoch(); - const auto unix_epoch = Clock::from_time_t({}); - const auto since_epoch = Clock::now() - unix_epoch; - - const auto since_double_time_epoch = since_epoch - std::chrono::seconds(DOUBLE_TIME_OFFSET); - return std::chrono::duration_cast>(since_double_time_epoch).count(); -} - -std::string Timer::SystemTimeAsDoubleToString(double time) -{ - // revert adjustments from GetSystemTimeAsDouble() to get a normal Unix timestamp again - time_t seconds = (time_t)time + DOUBLE_TIME_OFFSET; - tm* localTime = localtime(&seconds); - -#ifdef _WIN32 - wchar_t tmp[32] = {}; - wcsftime(tmp, std::size(tmp), L"%x %X", localTime); - return WStringToUTF8(tmp); -#else - char tmp[32] = {}; - strftime(tmp, sizeof(tmp), "%x %X", localTime); - return tmp; -#endif -} - void Timer::IncreaseResolution() { #ifdef _WIN32 -- cgit v1.2.3 From b473c3587388e06823c153c4fecc2104bcafedff Mon Sep 17 00:00:00 2001 From: Shawn Hoffman Date: Mon, 18 Jul 2022 00:20:26 -0700 Subject: windows: mark as HighQoS, ensure timer resolution is honored --- Source/Core/Common/Timer.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'Source/Core/Common/Timer.cpp') diff --git a/Source/Core/Common/Timer.cpp b/Source/Core/Common/Timer.cpp index ef910f6c27..3d2a2ed777 100644 --- a/Source/Core/Common/Timer.cpp +++ b/Source/Core/Common/Timer.cpp @@ -102,6 +102,22 @@ u64 Timer::GetLocalTimeSinceJan1970() void Timer::IncreaseResolution() { #ifdef _WIN32 + // Disable execution speed and timer resolution throttling process-wide. + // This mainly will keep Dolphin marked as high performance if it's in the background. The OS + // should make it high performance if it's in the foreground anyway (or for some specific + // threads e.g. audio). + // This is best-effort (i.e. the call may fail on older versions of Windows, where such throttling + // doesn't exist, anyway), and we don't bother reverting once set. + // This adjusts behavior on CPUs with "performance" and "efficiency" cores + PROCESS_POWER_THROTTLING_STATE PowerThrottling{}; + PowerThrottling.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION; + PowerThrottling.ControlMask = + PROCESS_POWER_THROTTLING_EXECUTION_SPEED | PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION; + PowerThrottling.StateMask = 0; + SetProcessInformation(GetCurrentProcess(), ProcessPowerThrottling, &PowerThrottling, + sizeof(PowerThrottling)); + + // Not actually sure how useful this is these days.. :') timeBeginPeriod(1); #endif } -- cgit v1.2.3 From 86da6c98fbd229d87de7cf8b9c7eb94c16923829 Mon Sep 17 00:00:00 2001 From: Shawn Hoffman Date: Mon, 18 Jul 2022 01:10:22 -0700 Subject: msvc: use std::chrono for GetLocalTimeSinceJan1970 --- Source/Core/Common/Timer.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'Source/Core/Common/Timer.cpp') diff --git a/Source/Core/Common/Timer.cpp b/Source/Core/Common/Timer.cpp index 3d2a2ed777..1b80e02880 100644 --- a/Source/Core/Common/Timer.cpp +++ b/Source/Core/Common/Timer.cpp @@ -7,7 +7,6 @@ #ifdef _WIN32 #include -#include #include #else #include @@ -82,6 +81,12 @@ u64 Timer::ElapsedMs() const u64 Timer::GetLocalTimeSinceJan1970() { +#ifdef _MSC_VER + std::chrono::zoned_seconds seconds( + std::chrono::current_zone(), + std::chrono::time_point_cast(std::chrono::system_clock::now())); + return seconds.get_local_time().time_since_epoch().count(); +#else time_t sysTime, tzDiff, tzDST; time(&sysTime); tm* gmTime = localtime(&sysTime); @@ -97,6 +102,7 @@ u64 Timer::GetLocalTimeSinceJan1970() tzDiff = sysTime - mktime(gmTime); return static_cast(sysTime + tzDiff + tzDST); +#endif } void Timer::IncreaseResolution() -- cgit v1.2.3 From fec61f89a3bce9bf857a15ed4789629624e66584 Mon Sep 17 00:00:00 2001 From: Shawn Hoffman Date: Mon, 18 Jul 2022 11:48:20 -0700 Subject: Timer: protect usages of ms timers from rollover --- Source/Core/Common/Timer.cpp | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) (limited to 'Source/Core/Common/Timer.cpp') diff --git a/Source/Core/Common/Timer.cpp b/Source/Core/Common/Timer.cpp index 1b80e02880..fb81164390 100644 --- a/Source/Core/Common/Timer.cpp +++ b/Source/Core/Common/Timer.cpp @@ -48,8 +48,7 @@ void Timer::Start() void Timer::StartWithOffset(u64 offset) { Start(); - if (m_start_ms > offset) - m_start_ms -= offset; + m_start_ms -= offset; } void Timer::Stop() @@ -60,23 +59,10 @@ void Timer::Stop() u64 Timer::ElapsedMs() const { - // If we have not started yet, return zero - if (m_start_ms == 0) - return 0; - - if (m_running) - { - u64 now = NowMs(); - if (m_start_ms >= now) - return 0; - return now - m_start_ms; - } - else - { - if (m_start_ms >= m_end_ms) - return 0; - return m_end_ms - m_start_ms; - } + const u64 end = m_running ? NowMs() : m_end_ms; + // Can handle up to 1 rollover event (underflow produces correct result) + // If Start() has never been called, will return 0 + return end - m_start_ms; } u64 Timer::GetLocalTimeSinceJan1970() -- cgit v1.2.3