summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/steady_clock.cpp5
-rw-r--r--src/common/wall_clock.cpp73
-rw-r--r--src/common/wall_clock.h54
-rw-r--r--src/common/x64/native_clock.cpp165
-rw-r--r--src/common/x64/native_clock.h56
5 files changed, 94 insertions, 259 deletions
diff --git a/src/common/steady_clock.cpp b/src/common/steady_clock.cpp
index 782859196..9415eed29 100644
--- a/src/common/steady_clock.cpp
+++ b/src/common/steady_clock.cpp
@@ -28,13 +28,12 @@ static s64 GetSystemTimeNS() {
28 // GetSystemTimePreciseAsFileTime returns the file time in 100ns units. 28 // GetSystemTimePreciseAsFileTime returns the file time in 100ns units.
29 static constexpr s64 Multiplier = 100; 29 static constexpr s64 Multiplier = 100;
30 // Convert Windows epoch to Unix epoch. 30 // Convert Windows epoch to Unix epoch.
31 static constexpr s64 WindowsEpochToUnixEpochNS = 0x19DB1DED53E8000LL; 31 static constexpr s64 WindowsEpochToUnixEpoch = 0x19DB1DED53E8000LL;
32 32
33 FILETIME filetime; 33 FILETIME filetime;
34 GetSystemTimePreciseAsFileTime(&filetime); 34 GetSystemTimePreciseAsFileTime(&filetime);
35 return Multiplier * ((static_cast<s64>(filetime.dwHighDateTime) << 32) + 35 return Multiplier * ((static_cast<s64>(filetime.dwHighDateTime) << 32) +
36 static_cast<s64>(filetime.dwLowDateTime)) - 36 static_cast<s64>(filetime.dwLowDateTime) - WindowsEpochToUnixEpoch);
37 WindowsEpochToUnixEpochNS;
38} 37}
39#endif 38#endif
40 39
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
index 817e71d52..ad8db06b0 100644
--- a/src/common/wall_clock.cpp
+++ b/src/common/wall_clock.cpp
@@ -2,88 +2,71 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/steady_clock.h" 4#include "common/steady_clock.h"
5#include "common/uint128.h"
6#include "common/wall_clock.h" 5#include "common/wall_clock.h"
7 6
8#ifdef ARCHITECTURE_x86_64 7#ifdef ARCHITECTURE_x86_64
9#include "common/x64/cpu_detect.h" 8#include "common/x64/cpu_detect.h"
10#include "common/x64/native_clock.h" 9#include "common/x64/native_clock.h"
10#include "common/x64/rdtsc.h"
11#endif 11#endif
12 12
13namespace Common { 13namespace Common {
14 14
15class StandardWallClock final : public WallClock { 15class StandardWallClock final : public WallClock {
16public: 16public:
17 explicit StandardWallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_) 17 explicit StandardWallClock() : start_time{SteadyClock::Now()} {}
18 : WallClock{emulated_cpu_frequency_, emulated_clock_frequency_, false},
19 start_time{SteadyClock::Now()} {}
20 18
21 std::chrono::nanoseconds GetTimeNS() override { 19 std::chrono::nanoseconds GetTimeNS() const override {
22 return SteadyClock::Now() - start_time; 20 return SteadyClock::Now() - start_time;
23 } 21 }
24 22
25 std::chrono::microseconds GetTimeUS() override { 23 std::chrono::microseconds GetTimeUS() const override {
26 return std::chrono::duration_cast<std::chrono::microseconds>(GetTimeNS()); 24 return static_cast<std::chrono::microseconds>(GetHostTicksElapsed() / NsToUsRatio::den);
27 } 25 }
28 26
29 std::chrono::milliseconds GetTimeMS() override { 27 std::chrono::milliseconds GetTimeMS() const override {
30 return std::chrono::duration_cast<std::chrono::milliseconds>(GetTimeNS()); 28 return static_cast<std::chrono::milliseconds>(GetHostTicksElapsed() / NsToMsRatio::den);
31 } 29 }
32 30
33 u64 GetClockCycles() override { 31 u64 GetCNTPCT() const override {
34 const u128 temp = Common::Multiply64Into128(GetTimeNS().count(), emulated_clock_frequency); 32 return GetHostTicksElapsed() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
35 return Common::Divide128On32(temp, NS_RATIO).first;
36 } 33 }
37 34
38 u64 GetCPUCycles() override { 35 u64 GetHostTicksNow() const override {
39 const u128 temp = Common::Multiply64Into128(GetTimeNS().count(), emulated_cpu_frequency); 36 return static_cast<u64>(SteadyClock::Now().time_since_epoch().count());
40 return Common::Divide128On32(temp, NS_RATIO).first;
41 } 37 }
42 38
43 void Pause([[maybe_unused]] bool is_paused) override { 39 u64 GetHostTicksElapsed() const override {
44 // Do nothing in this clock type. 40 return static_cast<u64>(GetTimeNS().count());
41 }
42
43 bool IsNative() const override {
44 return false;
45 } 45 }
46 46
47private: 47private:
48 SteadyClock::time_point start_time; 48 SteadyClock::time_point start_time;
49}; 49};
50 50
51std::unique_ptr<WallClock> CreateOptimalClock() {
51#ifdef ARCHITECTURE_x86_64 52#ifdef ARCHITECTURE_x86_64
52
53std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
54 u64 emulated_clock_frequency) {
55 const auto& caps = GetCPUCaps(); 53 const auto& caps = GetCPUCaps();
56 u64 rtsc_frequency = 0;
57 if (caps.invariant_tsc) {
58 rtsc_frequency = caps.tsc_frequency ? caps.tsc_frequency : EstimateRDTSCFrequency();
59 }
60 54
61 // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than: 55 if (caps.invariant_tsc && caps.tsc_frequency >= WallClock::CNTFRQ) {
62 // - A nanosecond 56 return std::make_unique<X64::NativeClock>(caps.tsc_frequency);
63 // - The emulated CPU frequency
64 // - The emulated clock counter frequency (CNTFRQ)
65 if (rtsc_frequency <= WallClock::NS_RATIO || rtsc_frequency <= emulated_cpu_frequency ||
66 rtsc_frequency <= emulated_clock_frequency) {
67 return std::make_unique<StandardWallClock>(emulated_cpu_frequency,
68 emulated_clock_frequency);
69 } else { 57 } else {
70 return std::make_unique<X64::NativeClock>(emulated_cpu_frequency, emulated_clock_frequency, 58 // Fallback to StandardWallClock if the hardware TSC
71 rtsc_frequency); 59 // - Is not invariant
60 // - Is not more precise than CNTFRQ
61 return std::make_unique<StandardWallClock>();
72 } 62 }
73}
74
75#else 63#else
76 64 return std::make_unique<StandardWallClock>();
77std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
78 u64 emulated_clock_frequency) {
79 return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
80}
81
82#endif 65#endif
66}
83 67
84std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency, 68std::unique_ptr<WallClock> CreateStandardWallClock() {
85 u64 emulated_clock_frequency) { 69 return std::make_unique<StandardWallClock>();
86 return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
87} 70}
88 71
89} // namespace Common 72} // namespace Common
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h
index 157ec5eae..a73e6e644 100644
--- a/src/common/wall_clock.h
+++ b/src/common/wall_clock.h
@@ -5,6 +5,7 @@
5 5
6#include <chrono> 6#include <chrono>
7#include <memory> 7#include <memory>
8#include <ratio>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
10 11
@@ -12,50 +13,43 @@ namespace Common {
12 13
13class WallClock { 14class WallClock {
14public: 15public:
15 static constexpr u64 NS_RATIO = 1'000'000'000; 16 static constexpr u64 CNTFRQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz
16 static constexpr u64 US_RATIO = 1'000'000;
17 static constexpr u64 MS_RATIO = 1'000;
18 17
19 virtual ~WallClock() = default; 18 virtual ~WallClock() = default;
20 19
21 /// Returns current wall time in nanoseconds 20 /// @returns The time in nanoseconds since the construction of this clock.
22 [[nodiscard]] virtual std::chrono::nanoseconds GetTimeNS() = 0; 21 virtual std::chrono::nanoseconds GetTimeNS() const = 0;
23 22
24 /// Returns current wall time in microseconds 23 /// @returns The time in microseconds since the construction of this clock.
25 [[nodiscard]] virtual std::chrono::microseconds GetTimeUS() = 0; 24 virtual std::chrono::microseconds GetTimeUS() const = 0;
26 25
27 /// Returns current wall time in milliseconds 26 /// @returns The time in milliseconds since the construction of this clock.
28 [[nodiscard]] virtual std::chrono::milliseconds GetTimeMS() = 0; 27 virtual std::chrono::milliseconds GetTimeMS() const = 0;
29 28
30 /// Returns current wall time in emulated clock cycles 29 /// @returns The guest CNTPCT ticks since the construction of this clock.
31 [[nodiscard]] virtual u64 GetClockCycles() = 0; 30 virtual u64 GetCNTPCT() const = 0;
32 31
33 /// Returns current wall time in emulated cpu cycles 32 /// @returns The raw host timer ticks since an indeterminate epoch.
34 [[nodiscard]] virtual u64 GetCPUCycles() = 0; 33 virtual u64 GetHostTicksNow() const = 0;
35 34
36 virtual void Pause(bool is_paused) = 0; 35 /// @returns The raw host timer ticks since the construction of this clock.
36 virtual u64 GetHostTicksElapsed() const = 0;
37 37
38 /// Tells if the wall clock, uses the host CPU's hardware clock 38 /// @returns Whether the clock directly uses the host's hardware clock.
39 [[nodiscard]] bool IsNative() const { 39 virtual bool IsNative() const = 0;
40 return is_native;
41 }
42 40
43protected: 41protected:
44 explicit WallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, bool is_native_) 42 using NsRatio = std::nano;
45 : emulated_cpu_frequency{emulated_cpu_frequency_}, 43 using UsRatio = std::micro;
46 emulated_clock_frequency{emulated_clock_frequency_}, is_native{is_native_} {} 44 using MsRatio = std::milli;
47 45
48 u64 emulated_cpu_frequency; 46 using NsToUsRatio = std::ratio_divide<std::nano, std::micro>;
49 u64 emulated_clock_frequency; 47 using NsToMsRatio = std::ratio_divide<std::nano, std::milli>;
50 48 using NsToCNTPCTRatio = std::ratio<CNTFRQ, std::nano::den>;
51private:
52 bool is_native;
53}; 49};
54 50
55[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, 51std::unique_ptr<WallClock> CreateOptimalClock();
56 u64 emulated_clock_frequency);
57 52
58[[nodiscard]] std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency, 53std::unique_ptr<WallClock> CreateStandardWallClock();
59 u64 emulated_clock_frequency);
60 54
61} // namespace Common 55} // namespace Common
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index 277b00662..5d1eb0590 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -1,164 +1,45 @@
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/steady_clock.h"
10#include "common/uint128.h" 4#include "common/uint128.h"
11#include "common/x64/native_clock.h" 5#include "common/x64/native_clock.h"
6#include "common/x64/rdtsc.h"
12 7
13#ifdef _MSC_VER 8namespace Common::X64 {
14#include <intrin.h>
15#endif
16 9
17namespace Common { 10NativeClock::NativeClock(u64 rdtsc_frequency_)
11 : start_ticks{FencedRDTSC()}, rdtsc_frequency{rdtsc_frequency_},
12 ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency)},
13 us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)},
14 ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)},
15 cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)} {}
18 16
19#ifdef _MSC_VER 17std::chrono::nanoseconds NativeClock::GetTimeNS() const {
20__forceinline static u64 FencedRDTSC() { 18 return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_rdtsc_factor)};
21 _mm_lfence();
22 _ReadWriteBarrier();
23 const u64 result = __rdtsc();
24 _mm_lfence();
25 _ReadWriteBarrier();
26 return result;
27}
28#else
29static u64 FencedRDTSC() {
30 u64 eax;
31 u64 edx;
32 asm volatile("lfence\n\t"
33 "rdtsc\n\t"
34 "lfence\n\t"
35 : "=a"(eax), "=d"(edx));
36 return (edx << 32) | eax;
37} 19}
38#endif
39 20
40template <u64 Nearest> 21std::chrono::microseconds NativeClock::GetTimeUS() const {
41static u64 RoundToNearest(u64 value) { 22 return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_rdtsc_factor)};
42 const auto mod = value % Nearest;
43 return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod);
44} 23}
45 24
46u64 EstimateRDTSCFrequency() { 25std::chrono::milliseconds NativeClock::GetTimeMS() const {
47 // Discard the first result measuring the rdtsc. 26 return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_rdtsc_factor)};
48 FencedRDTSC();
49 std::this_thread::sleep_for(std::chrono::milliseconds{1});
50 FencedRDTSC();
51
52 // Get the current time.
53 const auto start_time = Common::RealTimeClock::Now();
54 const u64 tsc_start = FencedRDTSC();
55 // Wait for 250 milliseconds.
56 std::this_thread::sleep_for(std::chrono::milliseconds{250});
57 const auto end_time = Common::RealTimeClock::Now();
58 const u64 tsc_end = FencedRDTSC();
59 // Calculate differences.
60 const u64 timer_diff = static_cast<u64>(
61 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
62 const u64 tsc_diff = tsc_end - tsc_start;
63 const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
64 return RoundToNearest<1000>(tsc_freq);
65} 27}
66 28
67namespace X64 { 29u64 NativeClock::GetCNTPCT() const {
68NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, 30 return MultiplyHigh(GetHostTicksElapsed(), cntpct_rdtsc_factor);
69 u64 rtsc_frequency_)
70 : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
71 rtsc_frequency_} {
72 // Thread to re-adjust the RDTSC frequency after 10 seconds has elapsed.
73 time_sync_thread = std::jthread{[this](std::stop_token token) {
74 // Get the current time.
75 const auto start_time = Common::RealTimeClock::Now();
76 const u64 tsc_start = FencedRDTSC();
77 // Wait for 10 seconds.
78 if (!Common::StoppableTimedWait(token, std::chrono::seconds{10})) {
79 return;
80 }
81 const auto end_time = Common::RealTimeClock::Now();
82 const u64 tsc_end = FencedRDTSC();
83 // Calculate differences.
84 const u64 timer_diff = static_cast<u64>(
85 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
86 const u64 tsc_diff = tsc_end - tsc_start;
87 const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
88 rtsc_frequency = tsc_freq;
89 CalculateAndSetFactors();
90 }};
91
92 time_point.inner.last_measure = FencedRDTSC();
93 time_point.inner.accumulated_ticks = 0U;
94 CalculateAndSetFactors();
95} 31}
96 32
97u64 NativeClock::GetRTSC() { 33u64 NativeClock::GetHostTicksNow() const {
98 TimePoint new_time_point{}; 34 return FencedRDTSC();
99 TimePoint current_time_point{};
100
101 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
102 do {
103 const u64 current_measure = FencedRDTSC();
104 u64 diff = current_measure - current_time_point.inner.last_measure;
105 diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
106 new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
107 ? current_measure
108 : current_time_point.inner.last_measure;
109 new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
110 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
111 current_time_point.pack, current_time_point.pack));
112 return new_time_point.inner.accumulated_ticks;
113} 35}
114 36
115void NativeClock::Pause(bool is_paused) { 37u64 NativeClock::GetHostTicksElapsed() const {
116 if (!is_paused) { 38 return FencedRDTSC() - start_ticks;
117 TimePoint current_time_point{};
118 TimePoint new_time_point{};
119
120 current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
121 do {
122 new_time_point.pack = current_time_point.pack;
123 new_time_point.inner.last_measure = FencedRDTSC();
124 } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
125 current_time_point.pack, current_time_point.pack));
126 }
127}
128
129std::chrono::nanoseconds NativeClock::GetTimeNS() {
130 const u64 rtsc_value = GetRTSC();
131 return std::chrono::nanoseconds{MultiplyHigh(rtsc_value, ns_rtsc_factor)};
132} 39}
133 40
134std::chrono::microseconds NativeClock::GetTimeUS() { 41bool NativeClock::IsNative() const {
135 const u64 rtsc_value = GetRTSC(); 42 return true;
136 return std::chrono::microseconds{MultiplyHigh(rtsc_value, us_rtsc_factor)};
137} 43}
138 44
139std::chrono::milliseconds NativeClock::GetTimeMS() { 45} // namespace Common::X64
140 const u64 rtsc_value = GetRTSC();
141 return std::chrono::milliseconds{MultiplyHigh(rtsc_value, ms_rtsc_factor)};
142}
143
144u64 NativeClock::GetClockCycles() {
145 const u64 rtsc_value = GetRTSC();
146 return MultiplyHigh(rtsc_value, clock_rtsc_factor);
147}
148
149u64 NativeClock::GetCPUCycles() {
150 const u64 rtsc_value = GetRTSC();
151 return MultiplyHigh(rtsc_value, cpu_rtsc_factor);
152}
153
154void NativeClock::CalculateAndSetFactors() {
155 ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
156 us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
157 ms_rtsc_factor = GetFixedPoint64Factor(MS_RATIO, rtsc_frequency);
158 clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency);
159 cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency);
160}
161
162} // namespace X64
163
164} // namespace Common
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h
index 03ca291d8..d6f8626c1 100644
--- a/src/common/x64/native_clock.h
+++ b/src/common/x64/native_clock.h
@@ -3,58 +3,36 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "common/polyfill_thread.h"
7#include "common/wall_clock.h" 6#include "common/wall_clock.h"
8 7
9namespace Common { 8namespace Common::X64 {
10 9
11namespace X64 {
12class NativeClock final : public WallClock { 10class NativeClock final : public WallClock {
13public: 11public:
14 explicit NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_, 12 explicit NativeClock(u64 rdtsc_frequency_);
15 u64 rtsc_frequency_);
16 13
17 std::chrono::nanoseconds GetTimeNS() override; 14 std::chrono::nanoseconds GetTimeNS() const override;
18 15
19 std::chrono::microseconds GetTimeUS() override; 16 std::chrono::microseconds GetTimeUS() const override;
20 17
21 std::chrono::milliseconds GetTimeMS() override; 18 std::chrono::milliseconds GetTimeMS() const override;
22 19
23 u64 GetClockCycles() override; 20 u64 GetCNTPCT() const override;
24 21
25 u64 GetCPUCycles() override; 22 u64 GetHostTicksNow() const override;
26 23
27 void Pause(bool is_paused) override; 24 u64 GetHostTicksElapsed() const override;
28 25
29private: 26 bool IsNative() const override;
30 u64 GetRTSC();
31
32 void CalculateAndSetFactors();
33
34 union alignas(16) TimePoint {
35 TimePoint() : pack{} {}
36 u128 pack{};
37 struct Inner {
38 u64 last_measure{};
39 u64 accumulated_ticks{};
40 } inner;
41 };
42
43 TimePoint time_point;
44 27
45 // factors 28private:
46 u64 clock_rtsc_factor{}; 29 u64 start_ticks;
47 u64 cpu_rtsc_factor{}; 30 u64 rdtsc_frequency;
48 u64 ns_rtsc_factor{};
49 u64 us_rtsc_factor{};
50 u64 ms_rtsc_factor{};
51
52 u64 rtsc_frequency;
53 31
54 std::jthread time_sync_thread; 32 u64 ns_rdtsc_factor;
33 u64 us_rdtsc_factor;
34 u64 ms_rdtsc_factor;
35 u64 cntpct_rdtsc_factor;
55}; 36};
56} // namespace X64
57
58u64 EstimateRDTSCFrequency();
59 37
60} // namespace Common 38} // namespace Common::X64