Merge pull request #2003 from CookiePLMonster/fix-apu-timer
Fix APU timer ticking at wrong frequency
This commit is contained in:
commit
9a773ef7ac
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue