Files
Xenia-Canary/src/xenia/base/clock_x64.cc
chss95cs@gmail.com 7cc364dcb8 squash reallocs in command buffers by using large prealloced buffer, directly use virtual memory with it so os allocs on demand
mark raw clock functions as noinline, the way msvc was inlining them and ordering the branches meant that rdtsc would often be speculatively executed
add alternative clock impl for win, instead of using queryperformancecounter we grab systemtime from kusershared. it does not have the same precision as queryperformancecounter, we only have 100 nanosecond precision, but we round to milliseconds so it never made sense to use the performance counter in the first place
stubbed out the "guest clock mutex"... (the entirety of clock.cc needs a rewrite)
added some helpers for minf/maxf without the nan handling behavior
2022-08-14 13:42:08 -07:00

111 lines
3.7 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/clock.h"
#include "xenia/base/platform.h"
#if XE_ARCH_AMD64 && XE_CLOCK_RAW_AVAILABLE
#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" msg \
"\n" \
"Set the cvar 'clock_source_raw' to 'false'.");
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.
XE_NOINLINE
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
return 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;
}
XE_NOINLINE
uint64_t Clock::host_tick_count_raw() { return xe_cpu_rdtsc(); }
} // namespace xe
#endif