[Base] Optional raw Clock source.
New cvar clock_source_raw allows to use the cpu cycle counter itself as an alternative time source, if system timing resolution is to low and causes problems.
This commit is contained in:
105
src/xenia/base/clock_x64.cc
Normal file
105
src/xenia/base/clock_x64.cc
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
#if XE_ARCH_AMD64
|
||||
|
||||
#include "xenia/base/clock.h"
|
||||
#include "xenia/base/logging.h"
|
||||
|
||||
// Wrap all these different cpu compiler intrinsics.
|
||||
// So no inline assembler here and the compiler will remove the clutter.
|
||||
#if XE_COMPILER_MSVC
|
||||
#define xe_cpu_cpuid(level, eax, ebx, ecx, edx) \
|
||||
{ \
|
||||
int __xe_cpuid_registers_[4]; \
|
||||
__cpuid(__xe_cpuid_registers_, (level)); \
|
||||
(eax) = static_cast<uint32_t>(__xe_cpuid_registers_[0]); \
|
||||
(ebx) = static_cast<uint32_t>(__xe_cpuid_registers_[1]); \
|
||||
(ecx) = static_cast<uint32_t>(__xe_cpuid_registers_[2]); \
|
||||
(edx) = static_cast<uint32_t>(__xe_cpuid_registers_[3]); \
|
||||
}
|
||||
#define xe_cpu_rdtsc() __rdtsc()
|
||||
#elif XE_COMPILER_CLANG || XE_COMPILER_GNUC
|
||||
#include <cpuid.h>
|
||||
#define xe_cpu_cpuid(level, eax, ebx, ecx, edx) \
|
||||
__cpuid((level), (eax), (ebx), (ecx), (edx));
|
||||
#define xe_cpu_rdtsc() __rdtsc()
|
||||
#else
|
||||
#error "No cpu instruction wrappers for current compiler implemented."
|
||||
#endif
|
||||
|
||||
#define CLOCK_FATAL(msg) \
|
||||
xe::FatalError( \
|
||||
"The raw clock source is not supported on your CPU. \n" \
|
||||
"%s \n" \
|
||||
"Set the cvar 'clock_source_raw' to 'false'.", \
|
||||
(msg));
|
||||
|
||||
namespace xe {
|
||||
// Getting the TSC frequency can be a bit tricky. This method here only works on
|
||||
// Intel as it seems. There is no easy way to get the frequency outside of ring0
|
||||
// on AMD, so we fail gracefully if not possible.
|
||||
uint64_t Clock::host_tick_frequency_raw() {
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
|
||||
// 00H Get max supported cpuid level.
|
||||
xe_cpu_cpuid(0x0, eax, ebx, ecx, edx);
|
||||
auto max_cpuid = eax;
|
||||
// 80000000H Get max extended cpuid level
|
||||
xe_cpu_cpuid(0x80000000, eax, ebx, ecx, edx);
|
||||
auto max_cpuid_ex = eax;
|
||||
|
||||
// 80000007H Get extended power feature info
|
||||
if (max_cpuid_ex >= 0x80000007) {
|
||||
xe_cpu_cpuid(0x80000007, eax, ebx, ecx, edx);
|
||||
// Invariant TSC bit at position 8
|
||||
auto tsc_invariant = edx & (1 << 8);
|
||||
// If the TSC is not invariant it will change its frequency with power
|
||||
// states and across cores.
|
||||
if (!tsc_invariant) {
|
||||
CLOCK_FATAL("The CPU has no invariant TSC.");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
CLOCK_FATAL("Unclear if the CPU has an invariant TSC.")
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (max_cpuid >= 0x15) {
|
||||
// 15H Get TSC/Crystal ratio and Crystal Hz.
|
||||
xe_cpu_cpuid(0x15, eax, ebx, ecx, edx);
|
||||
uint64_t ratio_num = ebx;
|
||||
uint64_t ratio_den = eax;
|
||||
uint64_t cryst_freq = ecx;
|
||||
// For some CPUs, Crystal frequency is not reported.
|
||||
if (ratio_num && ratio_den && cryst_freq) {
|
||||
// If it is, calculate the TSC frequency
|
||||
auto tsc_freq = cryst_freq * ratio_num / ratio_den;
|
||||
}
|
||||
}
|
||||
|
||||
if (max_cpuid >= 0x16) {
|
||||
// 16H Get CPU base frequency MHz in EAX.
|
||||
xe_cpu_cpuid(0x16, eax, ebx, ecx, edx);
|
||||
uint64_t cpu_base_freq = static_cast<uint64_t>(eax) * 1000000;
|
||||
assert(cpu_base_freq);
|
||||
return cpu_base_freq;
|
||||
}
|
||||
|
||||
CLOCK_FATAL("The clock frequency could not be determined.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t Clock::host_tick_count_raw() { return xe_cpu_rdtsc(); }
|
||||
|
||||
} // namespace xe
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user