Merge pull request #2003 from CookiePLMonster/fix-apu-timer

Fix APU timer ticking at wrong frequency
This commit is contained in:
Luke Usher 2020-10-25 01:41:56 +01:00 committed by GitHub
commit 9a773ef7ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 66 additions and 58 deletions

View File

@ -170,3 +170,37 @@ void Timer_Init()
#error "Unsupported OS"
#endif
}
// ******************************************************************
void ScaledPerformanceCounter::Reset(uint32_t frequency)
{
std::lock_guard<std::mutex> lock(m_mutex);
m_frequencyFactor = Muldiv64(HostClockFrequency, SCALE_S_IN_NS, frequency);
LARGE_INTEGER tsc;
QueryPerformanceCounter(&tsc);
m_lastQPC = tsc.QuadPart;
m_currentCount = 0;
m_currentRemainder = 0;
}
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

@ -29,6 +29,7 @@
#define TIMER_H
#include <atomic>
#include <mutex>
#define SCALE_S_IN_NS 1000000000
#define SCALE_MS_IN_NS 1000000
@ -63,4 +64,23 @@ void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms);
uint64_t GetTime_NS(TimerObject* Timer);
void Timer_Init();
// 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

@ -32,6 +32,7 @@
#include <core\kernel\exports\xboxkrnl.h>
#include <dsound.h>
#include "DirectSoundGlobal.hpp" // Global variables
#include <common/Timer.h>
#include "Logging.h"
#include "DirectSoundLogging.hpp"
@ -51,27 +52,17 @@
// Temporary APU Timer Functions
// TODO: Move these to LLE APUDevice once we have one!
#define APU_TIMER_FREQUENCY 48000
LARGE_INTEGER APUInitialPerformanceCounter;
double NativeToXboxAPU_FactorForPerformanceFrequency = 0;
static constexpr uint32_t APU_TIMER_FREQUENCY = 48000;
static ScaledPerformanceCounter ApuCounter;
void ResetApuTimer()
{
// Measure current host performance counter and frequency
QueryPerformanceCounter(&APUInitialPerformanceCounter);
NativeToXboxAPU_FactorForPerformanceFrequency = (double)APU_TIMER_FREQUENCY / APUInitialPerformanceCounter.QuadPart;
ApuCounter.Reset(APU_TIMER_FREQUENCY);
}
uint32_t GetAPUTime()
{
::LARGE_INTEGER PerformanceCounter;
QueryPerformanceCounter(&PerformanceCounter);
// Re-Base on the time DirectSoundCreate was called
PerformanceCounter.QuadPart -= APUInitialPerformanceCounter.QuadPart;
// Apply a delta to make it appear to tick at 48khz
PerformanceCounter.QuadPart = (ULONGLONG)(NativeToXboxAPU_FactorForPerformanceFrequency * PerformanceCounter.QuadPart);
return (DWORD)PerformanceCounter.QuadPart;
return static_cast<uint32_t>(ApuCounter.Tick());
}

View File

@ -326,47 +326,14 @@ 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 constexpr uint32_t SEC_TO_NSEC = 1000000000; // For seconds -> nanoseconds conversions
static uint64_t NativeToXbox_FactorForRdtsc = 0, NativeToXbox_FactorForAcpi = 0;
// State for CxbxGetPerformanceCounter - concurrent access should be next to non-existent, but secure against it anyway
static std::mutex RdtscLock, AcpiLock;
static LARGE_INTEGER LastRdtscQPC, LastAcpiQPC;
static ULONGLONG CurrentRdtsc = 0, CurrentAcpi = 0;
static ULONGLONG CurrentRdtscRemainder = 0, CurrentAcpiRemainder = 0;
static ScaledPerformanceCounter TscCounter, AcpiCounter;
ULONGLONG CxbxGetPerformanceCounter(bool acpi)
{
if (acpi == false && NativeToXbox_FactorForRdtsc != 0) {
std::lock_guard<std::mutex> lock(RdtscLock);
LARGE_INTEGER tsc;
QueryPerformanceCounter(&tsc);
LARGE_INTEGER lastTsc = std::exchange(LastRdtscQPC, tsc);
tsc.QuadPart -= lastTsc.QuadPart;
tsc.QuadPart *= SEC_TO_NSEC;
tsc.QuadPart += CurrentRdtscRemainder;
ULONGLONG quotient = tsc.QuadPart / NativeToXbox_FactorForRdtsc;
ULONGLONG remainder = tsc.QuadPart % NativeToXbox_FactorForRdtsc;
CurrentRdtscRemainder = remainder;
return CurrentRdtsc += quotient;
} else if (acpi == true && NativeToXbox_FactorForAcpi != 0) {
std::lock_guard<std::mutex> lock(AcpiLock);
LARGE_INTEGER tsc;
QueryPerformanceCounter(&tsc);
LARGE_INTEGER lastTsc = std::exchange(LastAcpiQPC, tsc);
tsc.QuadPart -= lastTsc.QuadPart;
tsc.QuadPart *= SEC_TO_NSEC;
tsc.QuadPart += CurrentAcpiRemainder;
ULONGLONG quotient = tsc.QuadPart / NativeToXbox_FactorForAcpi;
ULONGLONG remainder = tsc.QuadPart % NativeToXbox_FactorForAcpi;
CurrentAcpiRemainder = remainder;
return CurrentAcpi += quotient;
if (acpi == false) {
return TscCounter.Tick();
} else if (acpi == true) {
return AcpiCounter.Tick();
}
LARGE_INTEGER tsc;
@ -376,12 +343,8 @@ ULONGLONG CxbxGetPerformanceCounter(bool acpi)
void CxbxInitPerformanceCounters()
{
NativeToXbox_FactorForRdtsc = Muldiv64(HostClockFrequency, SEC_TO_NSEC, XBOX_TSC_FREQUENCY);
NativeToXbox_FactorForAcpi = Muldiv64(HostClockFrequency, SEC_TO_NSEC, XBOX_ACPI_FREQUENCY);
LARGE_INTEGER tsc;
QueryPerformanceCounter(&tsc);
LastRdtscQPC = LastAcpiQPC = tsc;
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)