Merge pull request #2286 from CookiePLMonster/simplify-timers

Simplify timers
This commit is contained in:
Luke Usher 2021-10-16 16:23:57 +01:00 committed by GitHub
commit 94f02583ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 22 additions and 80 deletions

View File

@ -76,8 +76,8 @@ void SleepPrecise(std::chrono::steady_clock::time_point targetTime)
// Vector storing all the timers created
static std::vector<TimerObject*> 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<LARGE_INTEGER*>(&HostQPCFrequency));
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&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<std::mutex> lock(m_mutex);
LARGE_INTEGER currentQPC;
QueryPerformanceCounter(&currentQPC);
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<std::mutex> 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;
}

View File

@ -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

View File

@ -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<uint32_t>(ApuCounter.Tick());
return static_cast<int32_t>(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;

View File

@ -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<ULONGLONG>(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();

View File

@ -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