// Copyright 2020 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include #ifdef _MSC_VER #include #else #include #endif #include "common/x64/native_clock.h" namespace Common { #ifdef _MSC_VER namespace { struct uint128 { u64 low; u64 high; }; u64 umuldiv64(u64 a, u64 b, u64 d) { uint128 r{}; r.low = _umul128(a, b, &r.high); u64 remainder; return _udiv128(r.high, r.low, d, &remainder); } } // namespace #else namespace { u64 umuldiv64(u64 a, u64 b, u64 d) { const u64 diva = a / d; const u64 moda = a % d; const u64 divb = b / d; const u64 modb = b % d; return diva * b + moda * divb + moda * modb / d; } } // namespace #endif u64 EstimateRDTSCFrequency() { const auto milli_10 = std::chrono::milliseconds{10}; // get current time _mm_mfence(); const u64 tscStart = __rdtsc(); const auto startTime = std::chrono::high_resolution_clock::now(); // wait roughly 3 seconds while (true) { auto milli = std::chrono::duration_cast( std::chrono::high_resolution_clock::now() - startTime); if (milli.count() >= 3000) break; std::this_thread::sleep_for(milli_10); } const auto endTime = std::chrono::high_resolution_clock::now(); _mm_mfence(); const u64 tscEnd = __rdtsc(); // calculate difference const u64 timer_diff = std::chrono::duration_cast(endTime - startTime).count(); const u64 tsc_diff = tscEnd - tscStart; const u64 tsc_freq = umuldiv64(tsc_diff, 1000000000ULL, timer_diff); return tsc_freq; } namespace X64 { NativeClock::NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, u64 rtsc_frequency) : WallClock(emulated_cpu_frequency, emulated_clock_frequency, true), rtsc_frequency{ rtsc_frequency} { _mm_mfence(); last_measure = __rdtsc(); accumulated_ticks = 0U; } u64 NativeClock::GetRTSC() { rtsc_serialize.lock(); _mm_mfence(); const u64 current_measure = __rdtsc(); u64 diff = current_measure - last_measure; diff = diff & ~static_cast(static_cast(diff) >> 63); // max(diff, 0) if (current_measure > last_measure) { last_measure = current_measure; } accumulated_ticks += diff; rtsc_serialize.unlock(); return accumulated_ticks; } std::chrono::nanoseconds NativeClock::GetTimeNS() { const u64 rtsc_value = GetRTSC(); return std::chrono::nanoseconds{umuldiv64(rtsc_value, 1000000000, rtsc_frequency)}; } std::chrono::microseconds NativeClock::GetTimeUS() { const u64 rtsc_value = GetRTSC(); return std::chrono::microseconds{umuldiv64(rtsc_value, 1000000, rtsc_frequency)}; } std::chrono::milliseconds NativeClock::GetTimeMS() { const u64 rtsc_value = GetRTSC(); return std::chrono::milliseconds{umuldiv64(rtsc_value, 1000, rtsc_frequency)}; } u64 NativeClock::GetClockCycles() { const u64 rtsc_value = GetRTSC(); return umuldiv64(rtsc_value, emulated_clock_frequency, rtsc_frequency); } u64 NativeClock::GetCPUCycles() { const u64 rtsc_value = GetRTSC(); return umuldiv64(rtsc_value, emulated_cpu_frequency, rtsc_frequency); } } // namespace X64 } // namespace Common