diff options
Diffstat (limited to 'src/common/x64/native_clock.cpp')
| -rw-r--r-- | src/common/x64/native_clock.cpp | 138 |
1 files changed, 26 insertions, 112 deletions
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 8b08332ab..7d2a26bd9 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp | |||
| @@ -1,136 +1,50 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <array> | ||
| 5 | #include <chrono> | ||
| 6 | #include <thread> | ||
| 7 | |||
| 8 | #include "common/atomic_ops.h" | ||
| 9 | #include "common/uint128.h" | 4 | #include "common/uint128.h" |
| 10 | #include "common/x64/native_clock.h" | 5 | #include "common/x64/native_clock.h" |
| 6 | #include "common/x64/rdtsc.h" | ||
| 11 | 7 | ||
| 12 | #ifdef _MSC_VER | 8 | namespace Common::X64 { |
| 13 | #include <intrin.h> | ||
| 14 | #endif | ||
| 15 | |||
| 16 | namespace Common { | ||
| 17 | |||
| 18 | #ifdef _MSC_VER | ||
| 19 | __forceinline static u64 FencedRDTSC() { | ||
| 20 | _mm_lfence(); | ||
| 21 | _ReadWriteBarrier(); | ||
| 22 | const u64 result = __rdtsc(); | ||
| 23 | _mm_lfence(); | ||
| 24 | _ReadWriteBarrier(); | ||
| 25 | return result; | ||
| 26 | } | ||
| 27 | #else | ||
| 28 | static u64 FencedRDTSC() { | ||
| 29 | u64 result; | ||
| 30 | asm volatile("lfence\n\t" | ||
| 31 | "rdtsc\n\t" | ||
| 32 | "shl $32, %%rdx\n\t" | ||
| 33 | "or %%rdx, %0\n\t" | ||
| 34 | "lfence" | ||
| 35 | : "=a"(result) | ||
| 36 | : | ||
| 37 | : "rdx", "memory", "cc"); | ||
| 38 | return result; | ||
| 39 | } | ||
| 40 | #endif | ||
| 41 | |||
| 42 | u64 EstimateRDTSCFrequency() { | ||
| 43 | // Discard the first result measuring the rdtsc. | ||
| 44 | FencedRDTSC(); | ||
| 45 | std::this_thread::sleep_for(std::chrono::milliseconds{1}); | ||
| 46 | FencedRDTSC(); | ||
| 47 | 9 | ||
| 48 | // Get the current time. | 10 | NativeClock::NativeClock(u64 rdtsc_frequency_) |
| 49 | const auto start_time = std::chrono::steady_clock::now(); | 11 | : start_ticks{FencedRDTSC()}, rdtsc_frequency{rdtsc_frequency_}, |
| 50 | const u64 tsc_start = FencedRDTSC(); | 12 | ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency)}, |
| 51 | // Wait for 200 milliseconds. | 13 | us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)}, |
| 52 | std::this_thread::sleep_for(std::chrono::milliseconds{200}); | 14 | ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)}, |
| 53 | const auto end_time = std::chrono::steady_clock::now(); | 15 | cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)}, |
| 54 | const u64 tsc_end = FencedRDTSC(); | 16 | gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {} |
| 55 | // Calculate differences. | ||
| 56 | const u64 timer_diff = static_cast<u64>( | ||
| 57 | std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); | ||
| 58 | const u64 tsc_diff = tsc_end - tsc_start; | ||
| 59 | const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); | ||
| 60 | return tsc_freq; | ||
| 61 | } | ||
| 62 | 17 | ||
| 63 | namespace X64 { | 18 | std::chrono::nanoseconds NativeClock::GetTimeNS() const { |
| 64 | NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, | 19 | return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_rdtsc_factor)}; |
| 65 | u64 rtsc_frequency_) | ||
| 66 | : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{ | ||
| 67 | rtsc_frequency_} { | ||
| 68 | time_point.inner.last_measure = FencedRDTSC(); | ||
| 69 | time_point.inner.accumulated_ticks = 0U; | ||
| 70 | ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency); | ||
| 71 | us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency); | ||
| 72 | ms_rtsc_factor = GetFixedPoint64Factor(MS_RATIO, rtsc_frequency); | ||
| 73 | clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency); | ||
| 74 | cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency); | ||
| 75 | } | 20 | } |
| 76 | 21 | ||
| 77 | u64 NativeClock::GetRTSC() { | 22 | std::chrono::microseconds NativeClock::GetTimeUS() const { |
| 78 | TimePoint new_time_point{}; | 23 | return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_rdtsc_factor)}; |
| 79 | TimePoint current_time_point{}; | ||
| 80 | |||
| 81 | current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); | ||
| 82 | do { | ||
| 83 | const u64 current_measure = FencedRDTSC(); | ||
| 84 | u64 diff = current_measure - current_time_point.inner.last_measure; | ||
| 85 | diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0) | ||
| 86 | new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure | ||
| 87 | ? current_measure | ||
| 88 | : current_time_point.inner.last_measure; | ||
| 89 | new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff; | ||
| 90 | } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, | ||
| 91 | current_time_point.pack, current_time_point.pack)); | ||
| 92 | return new_time_point.inner.accumulated_ticks; | ||
| 93 | } | 24 | } |
| 94 | 25 | ||
| 95 | void NativeClock::Pause(bool is_paused) { | 26 | std::chrono::milliseconds NativeClock::GetTimeMS() const { |
| 96 | if (!is_paused) { | 27 | return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_rdtsc_factor)}; |
| 97 | TimePoint current_time_point{}; | ||
| 98 | TimePoint new_time_point{}; | ||
| 99 | |||
| 100 | current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); | ||
| 101 | do { | ||
| 102 | new_time_point.pack = current_time_point.pack; | ||
| 103 | new_time_point.inner.last_measure = FencedRDTSC(); | ||
| 104 | } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, | ||
| 105 | current_time_point.pack, current_time_point.pack)); | ||
| 106 | } | ||
| 107 | } | 28 | } |
| 108 | 29 | ||
| 109 | std::chrono::nanoseconds NativeClock::GetTimeNS() { | 30 | u64 NativeClock::GetCNTPCT() const { |
| 110 | const u64 rtsc_value = GetRTSC(); | 31 | return MultiplyHigh(GetHostTicksElapsed(), cntpct_rdtsc_factor); |
| 111 | return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)}; | ||
| 112 | } | 32 | } |
| 113 | 33 | ||
| 114 | std::chrono::microseconds NativeClock::GetTimeUS() { | 34 | u64 NativeClock::GetGPUTick() const { |
| 115 | const u64 rtsc_value = GetRTSC(); | 35 | return MultiplyHigh(GetHostTicksElapsed(), gputick_rdtsc_factor); |
| 116 | return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)}; | ||
| 117 | } | 36 | } |
| 118 | 37 | ||
| 119 | std::chrono::milliseconds NativeClock::GetTimeMS() { | 38 | u64 NativeClock::GetHostTicksNow() const { |
| 120 | const u64 rtsc_value = GetRTSC(); | 39 | return FencedRDTSC(); |
| 121 | return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)}; | ||
| 122 | } | 40 | } |
| 123 | 41 | ||
| 124 | u64 NativeClock::GetClockCycles() { | 42 | u64 NativeClock::GetHostTicksElapsed() const { |
| 125 | const u64 rtsc_value = GetRTSC(); | 43 | return FencedRDTSC() - start_ticks; |
| 126 | return MultiplyHigh(rtsc_value, clock_rtsc_factor); | ||
| 127 | } | 44 | } |
| 128 | 45 | ||
| 129 | u64 NativeClock::GetCPUCycles() { | 46 | bool NativeClock::IsNative() const { |
| 130 | const u64 rtsc_value = GetRTSC(); | 47 | return true; |
| 131 | return MultiplyHigh(rtsc_value, cpu_rtsc_factor); | ||
| 132 | } | 48 | } |
| 133 | 49 | ||
| 134 | } // namespace X64 | 50 | } // namespace Common::X64 |
| 135 | |||
| 136 | } // namespace Common | ||