diff --git a/src/common/Timer.cpp b/src/common/Timer.cpp index 86b705d3d..0cbfc8e4e 100644 --- a/src/common/Timer.cpp +++ b/src/common/Timer.cpp @@ -76,8 +76,8 @@ void SleepPrecise(std::chrono::steady_clock::time_point targetTime) // Vector storing all the timers created static std::vector TimerList; -// The frequency of the high resolution clock of the host -uint64_t HostClockFrequency; +// The frequency of the high resolution clock of the host, and the start time +int64_t HostQPCFrequency, HostQPCStartTime; // Lock to acquire when accessing TimerList std::mutex TimerMtx; @@ -86,9 +86,7 @@ std::mutex TimerMtx; uint64_t GetTime_NS(TimerObject* Timer) { #ifdef _WIN32 - LARGE_INTEGER li; - QueryPerformanceCounter(&li); - uint64_t Ret = Muldiv64(li.QuadPart, SCALE_S_IN_NS, (uint32_t)HostClockFrequency); + uint64_t Ret = Timer_GetScaledPerformanceCounter(SCALE_S_IN_NS); #elif __linux__ static struct timespec ts; clock_gettime(CLOCK_MONOTONIC_RAW, &ts); @@ -194,9 +192,8 @@ void Timer_Start(TimerObject* Timer, uint64_t Expire_MS) void Timer_Init() { #ifdef _WIN32 - LARGE_INTEGER freq; - QueryPerformanceFrequency(&freq); - HostClockFrequency = freq.QuadPart; + QueryPerformanceFrequency(reinterpret_cast(&HostQPCFrequency)); + QueryPerformanceCounter(reinterpret_cast(&HostQPCStartTime)); #elif __linux__ ClockFrequency = 0; #else @@ -204,36 +201,17 @@ void Timer_Init() #endif } -// ****************************************************************** - -void ScaledPerformanceCounter::Reset(uint32_t frequency) +int64_t Timer_GetScaledPerformanceCounter(int64_t Period) { - std::lock_guard lock(m_mutex); + LARGE_INTEGER currentQPC; + QueryPerformanceCounter(¤tQPC); - m_frequencyFactor = Muldiv64(HostClockFrequency, SCALE_S_IN_NS, frequency); + // Scale frequency with overflow avoidance, like in std::chrono + // https://github.com/microsoft/STL/blob/6d2f8b0ed88ea6cba26cc2151f47f678442c1663/stl/inc/chrono#L703 + const int64_t currentTime = currentQPC.QuadPart - HostQPCStartTime; + const int64_t whole = (currentTime / HostQPCFrequency) * Period; + const int64_t part = (currentTime % HostQPCFrequency) * Period / HostQPCFrequency; - LARGE_INTEGER tsc; - QueryPerformanceCounter(&tsc); - m_lastQPC = tsc.QuadPart; - - m_currentCount = 0; - m_currentRemainder = 0; + return whole + part; } -uint64_t ScaledPerformanceCounter::Tick() -{ - std::lock_guard lock(m_mutex); - - LARGE_INTEGER qpc; - QueryPerformanceCounter(&qpc); - - int64_t lastQpc = std::exchange(m_lastQPC, qpc.QuadPart); - qpc.QuadPart -= lastQpc; - qpc.QuadPart *= SCALE_S_IN_NS; - qpc.QuadPart += m_currentRemainder; - uint64_t quotient = qpc.QuadPart / m_frequencyFactor; - uint64_t remainder = qpc.QuadPart % m_frequencyFactor; - - m_currentRemainder = remainder; - return m_currentCount += quotient; -} diff --git a/src/common/Timer.h b/src/common/Timer.h index bc0f7494c..b99bb93d9 100644 --- a/src/common/Timer.h +++ b/src/common/Timer.h @@ -54,7 +54,7 @@ typedef struct _TimerObject } TimerObject; -extern uint64_t HostClockFrequency; +extern int64_t HostQPCFrequency; /* Timer exported functions */ TimerObject* Timer_Create(TimerCB Callback, void* Arg, std::string Name, bool IsXboxTimer); @@ -64,25 +64,8 @@ void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms); uint64_t GetTime_NS(TimerObject* Timer); void Timer_Init(); +int64_t Timer_GetScaledPerformanceCounter(int64_t Period); + void SleepPrecise(std::chrono::steady_clock::time_point targetTime); -// A stateful replacement for QueryPerformanceCounter, ticking at an arbitrary frequency -// Thread-safe and designed to avoid overflows at all cost -class ScaledPerformanceCounter -{ -public: - ScaledPerformanceCounter() = default; - void Reset(uint32_t frequency); - uint64_t Tick(); - -private: - std::mutex m_mutex; - - uint64_t m_frequencyFactor; - int64_t m_lastQPC; - - uint64_t m_currentCount; - uint64_t m_currentRemainder; -}; - #endif diff --git a/src/core/hle/DSOUND/DirectSound/DirectSound.cpp b/src/core/hle/DSOUND/DirectSound/DirectSound.cpp index 1e8d2d80b..f0e44fedd 100644 --- a/src/core/hle/DSOUND/DirectSound/DirectSound.cpp +++ b/src/core/hle/DSOUND/DirectSound/DirectSound.cpp @@ -53,16 +53,10 @@ // TODO: Move these to LLE APUDevice once we have one! static constexpr uint32_t APU_TIMER_FREQUENCY = 48000; -static ScaledPerformanceCounter ApuCounter; - -void ResetApuTimer() -{ - ApuCounter.Reset(APU_TIMER_FREQUENCY); -} uint32_t GetAPUTime() { - return static_cast(ApuCounter.Tick()); + return static_cast(Timer_GetScaledPerformanceCounter(APU_TIMER_FREQUENCY)); } @@ -135,8 +129,6 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreate) dsound_thread = std::thread(dsound_thread_worker, nullptr); } - ResetApuTimer(); - // Set this flag when this function is called g_bDSoundCreateCalled = TRUE; diff --git a/src/core/kernel/exports/EmuKrnlKe.cpp b/src/core/kernel/exports/EmuKrnlKe.cpp index 0ef47775a..f0f6fcd63 100644 --- a/src/core/kernel/exports/EmuKrnlKe.cpp +++ b/src/core/kernel/exports/EmuKrnlKe.cpp @@ -327,26 +327,15 @@ void InitDpcThread() static constexpr uint32_t XBOX_TSC_FREQUENCY = 733333333; // Xbox Time Stamp Counter Frequency = 733333333 (CPU Clock) static constexpr uint32_t XBOX_ACPI_FREQUENCY = 3375000; // Xbox ACPI frequency (3.375 mhz) -static ScaledPerformanceCounter TscCounter, AcpiCounter; ULONGLONG CxbxGetPerformanceCounter(bool acpi) { - if (acpi == false) { - return TscCounter.Tick(); - } else if (acpi == true) { - return AcpiCounter.Tick(); - } - - LARGE_INTEGER tsc; - QueryPerformanceCounter(&tsc); - return static_cast(tsc.QuadPart); + const int64_t period = acpi ? XBOX_ACPI_FREQUENCY : XBOX_TSC_FREQUENCY; + return Timer_GetScaledPerformanceCounter(period); } void CxbxInitPerformanceCounters() { - TscCounter.Reset(XBOX_TSC_FREQUENCY); - AcpiCounter.Reset(XBOX_ACPI_FREQUENCY); - // Let's initialize the Dpc handling thread too, // here for now (should be called by our caller) InitDpcThread(); diff --git a/src/core/kernel/init/CxbxKrnl.cpp b/src/core/kernel/init/CxbxKrnl.cpp index 3889de9ae..723068cf9 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -391,8 +391,8 @@ static void CxbxKrnlClockThread(void* pVoid) LastTicks = CurrentTicks.QuadPart; Error += (Delta * SCALE_S_IN_US); - Microseconds = Error / HostClockFrequency; - Error -= (Microseconds * HostClockFrequency); + Microseconds = Error / HostQPCFrequency; + Error -= (Microseconds * HostQPCFrequency); UnaccountedMicroseconds += Microseconds; IncrementScaling = (unsigned int)(UnaccountedMicroseconds / 1000); // -> 1 ms = 1000us -> time between two xbox clock interrupts