Simplify timer code to scale them without state
This commit is contained in:
parent
3bdd689e03
commit
326a5bb714
|
@ -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(¤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<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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue