summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt11
-rw-r--r--src/common/bit_cast.h20
-rw-r--r--src/common/input.h2
-rw-r--r--src/common/overflow.h22
-rw-r--r--src/common/settings.h8
-rw-r--r--src/common/steady_clock.cpp56
-rw-r--r--src/common/steady_clock.h23
-rw-r--r--src/common/wall_clock.cpp39
-rw-r--r--src/common/wall_clock.h3
-rw-r--r--src/common/windows/timer_resolution.cpp109
-rw-r--r--src/common/windows/timer_resolution.h38
-rw-r--r--src/common/x64/native_clock.cpp17
12 files changed, 306 insertions, 42 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 56b247ac4..61ab68864 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -91,6 +91,7 @@ add_library(common STATIC
91 multi_level_page_table.h 91 multi_level_page_table.h
92 nvidia_flags.cpp 92 nvidia_flags.cpp
93 nvidia_flags.h 93 nvidia_flags.h
94 overflow.h
94 page_table.cpp 95 page_table.cpp
95 page_table.h 96 page_table.h
96 param_package.cpp 97 param_package.cpp
@@ -113,6 +114,8 @@ add_library(common STATIC
113 socket_types.h 114 socket_types.h
114 spin_lock.cpp 115 spin_lock.cpp
115 spin_lock.h 116 spin_lock.h
117 steady_clock.cpp
118 steady_clock.h
116 stream.cpp 119 stream.cpp
117 stream.h 120 stream.h
118 string_util.cpp 121 string_util.cpp
@@ -142,6 +145,14 @@ add_library(common STATIC
142 zstd_compression.h 145 zstd_compression.h
143) 146)
144 147
148if (WIN32)
149 target_sources(common PRIVATE
150 windows/timer_resolution.cpp
151 windows/timer_resolution.h
152 )
153 target_link_libraries(common PRIVATE ntdll)
154endif()
155
145if(ARCHITECTURE_x86_64) 156if(ARCHITECTURE_x86_64)
146 target_sources(common 157 target_sources(common
147 PRIVATE 158 PRIVATE
diff --git a/src/common/bit_cast.h b/src/common/bit_cast.h
index 535148b4d..c6110c542 100644
--- a/src/common/bit_cast.h
+++ b/src/common/bit_cast.h
@@ -3,19 +3,21 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <cstring> 6#include <version>
7#include <type_traits> 7
8#ifdef __cpp_lib_bit_cast
9#include <bit>
10#endif
8 11
9namespace Common { 12namespace Common {
10 13
11template <typename To, typename From> 14template <typename To, typename From>
12[[nodiscard]] std::enable_if_t<sizeof(To) == sizeof(From) && std::is_trivially_copyable_v<From> && 15constexpr inline To BitCast(const From& from) {
13 std::is_trivially_copyable_v<To>, 16#ifdef __cpp_lib_bit_cast
14 To> 17 return std::bit_cast<To>(from);
15BitCast(const From& src) noexcept { 18#else
16 To dst; 19 return __builtin_bit_cast(To, from);
17 std::memcpy(&dst, &src, sizeof(To)); 20#endif
18 return dst;
19} 21}
20 22
21} // namespace Common 23} // namespace Common
diff --git a/src/common/input.h b/src/common/input.h
index b5748a6c8..98e934685 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -46,7 +46,7 @@ enum class PollingMode {
46 // Constant polling of buttons, analogs and motion data 46 // Constant polling of buttons, analogs and motion data
47 Active, 47 Active,
48 // Only update on button change, digital analogs 48 // Only update on button change, digital analogs
49 Pasive, 49 Passive,
50 // Enable near field communication polling 50 // Enable near field communication polling
51 NFC, 51 NFC,
52 // Enable infrared camera polling 52 // Enable infrared camera polling
diff --git a/src/common/overflow.h b/src/common/overflow.h
new file mode 100644
index 000000000..44d8e7e73
--- /dev/null
+++ b/src/common/overflow.h
@@ -0,0 +1,22 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <type_traits>
7#include "bit_cast.h"
8
9namespace Common {
10
11template <typename T>
12 requires(std::is_integral_v<T> && std::is_signed_v<T>)
13inline T WrappingAdd(T lhs, T rhs) {
14 using U = std::make_unsigned_t<T>;
15
16 U lhs_u = BitCast<U>(lhs);
17 U rhs_u = BitCast<U>(rhs);
18
19 return BitCast<T>(lhs_u + rhs_u);
20}
21
22} // namespace Common
diff --git a/src/common/settings.h b/src/common/settings.h
index 56ee4e28d..b77a1580a 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -128,7 +128,7 @@ public:
128 /** 128 /**
129 * Sets a default value, label, and setting value. 129 * Sets a default value, label, and setting value.
130 * 130 *
131 * @param default_val Intial value of the setting, and default value of the setting 131 * @param default_val Initial value of the setting, and default value of the setting
132 * @param name Label for the setting 132 * @param name Label for the setting
133 */ 133 */
134 explicit Setting(const Type& default_val, const std::string& name) 134 explicit Setting(const Type& default_val, const std::string& name)
@@ -139,7 +139,7 @@ public:
139 /** 139 /**
140 * Sets a default value, minimum value, maximum value, and label. 140 * Sets a default value, minimum value, maximum value, and label.
141 * 141 *
142 * @param default_val Intial value of the setting, and default value of the setting 142 * @param default_val Initial value of the setting, and default value of the setting
143 * @param min_val Sets the minimum allowed value of the setting 143 * @param min_val Sets the minimum allowed value of the setting
144 * @param max_val Sets the maximum allowed value of the setting 144 * @param max_val Sets the maximum allowed value of the setting
145 * @param name Label for the setting 145 * @param name Label for the setting
@@ -231,7 +231,7 @@ public:
231 /** 231 /**
232 * Sets a default value, label, and setting value. 232 * Sets a default value, label, and setting value.
233 * 233 *
234 * @param default_val Intial value of the setting, and default value of the setting 234 * @param default_val Initial value of the setting, and default value of the setting
235 * @param name Label for the setting 235 * @param name Label for the setting
236 */ 236 */
237 explicit SwitchableSetting(const Type& default_val, const std::string& name) 237 explicit SwitchableSetting(const Type& default_val, const std::string& name)
@@ -242,7 +242,7 @@ public:
242 /** 242 /**
243 * Sets a default value, minimum value, maximum value, and label. 243 * Sets a default value, minimum value, maximum value, and label.
244 * 244 *
245 * @param default_val Intial value of the setting, and default value of the setting 245 * @param default_val Initial value of the setting, and default value of the setting
246 * @param min_val Sets the minimum allowed value of the setting 246 * @param min_val Sets the minimum allowed value of the setting
247 * @param max_val Sets the maximum allowed value of the setting 247 * @param max_val Sets the maximum allowed value of the setting
248 * @param name Label for the setting 248 * @param name Label for the setting
diff --git a/src/common/steady_clock.cpp b/src/common/steady_clock.cpp
new file mode 100644
index 000000000..0d5908aa7
--- /dev/null
+++ b/src/common/steady_clock.cpp
@@ -0,0 +1,56 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#if defined(_WIN32)
5#include <windows.h>
6#else
7#include <time.h>
8#endif
9
10#include "common/steady_clock.h"
11
12namespace Common {
13
14#ifdef _WIN32
15static s64 WindowsQueryPerformanceFrequency() {
16 LARGE_INTEGER frequency;
17 QueryPerformanceFrequency(&frequency);
18 return frequency.QuadPart;
19}
20
21static s64 WindowsQueryPerformanceCounter() {
22 LARGE_INTEGER counter;
23 QueryPerformanceCounter(&counter);
24 return counter.QuadPart;
25}
26#endif
27
28SteadyClock::time_point SteadyClock::Now() noexcept {
29#if defined(_WIN32)
30 static const auto freq = WindowsQueryPerformanceFrequency();
31 const auto counter = WindowsQueryPerformanceCounter();
32
33 // 10 MHz is a very common QPC frequency on modern PCs.
34 // Optimizing for this specific frequency can double the performance of
35 // this function by avoiding the expensive frequency conversion path.
36 static constexpr s64 TenMHz = 10'000'000;
37
38 if (freq == TenMHz) [[likely]] {
39 static_assert(period::den % TenMHz == 0);
40 static constexpr s64 Multiplier = period::den / TenMHz;
41 return time_point{duration{counter * Multiplier}};
42 }
43
44 const auto whole = (counter / freq) * period::den;
45 const auto part = (counter % freq) * period::den / freq;
46 return time_point{duration{whole + part}};
47#elif defined(__APPLE__)
48 return time_point{duration{clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW)}};
49#else
50 timespec ts;
51 clock_gettime(CLOCK_MONOTONIC, &ts);
52 return time_point{std::chrono::seconds{ts.tv_sec} + std::chrono::nanoseconds{ts.tv_nsec}};
53#endif
54}
55
56}; // namespace Common
diff --git a/src/common/steady_clock.h b/src/common/steady_clock.h
new file mode 100644
index 000000000..9497cf865
--- /dev/null
+++ b/src/common/steady_clock.h
@@ -0,0 +1,23 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <chrono>
7
8#include "common/common_types.h"
9
10namespace Common {
11
12struct SteadyClock {
13 using rep = s64;
14 using period = std::nano;
15 using duration = std::chrono::nanoseconds;
16 using time_point = std::chrono::time_point<SteadyClock>;
17
18 static constexpr bool is_steady = true;
19
20 [[nodiscard]] static time_point Now() noexcept;
21};
22
23} // namespace Common
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
index ae07f2811..817e71d52 100644
--- a/src/common/wall_clock.cpp
+++ b/src/common/wall_clock.cpp
@@ -1,6 +1,7 @@
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 "common/steady_clock.h"
4#include "common/uint128.h" 5#include "common/uint128.h"
5#include "common/wall_clock.h" 6#include "common/wall_clock.h"
6 7
@@ -11,45 +12,32 @@
11 12
12namespace Common { 13namespace Common {
13 14
14using base_timer = std::chrono::steady_clock;
15using base_time_point = std::chrono::time_point<base_timer>;
16
17class StandardWallClock final : public WallClock { 15class StandardWallClock final : public WallClock {
18public: 16public:
19 explicit StandardWallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_) 17 explicit StandardWallClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequency_)
20 : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, false) { 18 : WallClock{emulated_cpu_frequency_, emulated_clock_frequency_, false},
21 start_time = base_timer::now(); 19 start_time{SteadyClock::Now()} {}
22 }
23 20
24 std::chrono::nanoseconds GetTimeNS() override { 21 std::chrono::nanoseconds GetTimeNS() override {
25 base_time_point current = base_timer::now(); 22 return SteadyClock::Now() - start_time;
26 auto elapsed = current - start_time;
27 return std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed);
28 } 23 }
29 24
30 std::chrono::microseconds GetTimeUS() override { 25 std::chrono::microseconds GetTimeUS() override {
31 base_time_point current = base_timer::now(); 26 return std::chrono::duration_cast<std::chrono::microseconds>(GetTimeNS());
32 auto elapsed = current - start_time;
33 return std::chrono::duration_cast<std::chrono::microseconds>(elapsed);
34 } 27 }
35 28
36 std::chrono::milliseconds GetTimeMS() override { 29 std::chrono::milliseconds GetTimeMS() override {
37 base_time_point current = base_timer::now(); 30 return std::chrono::duration_cast<std::chrono::milliseconds>(GetTimeNS());
38 auto elapsed = current - start_time;
39 return std::chrono::duration_cast<std::chrono::milliseconds>(elapsed);
40 } 31 }
41 32
42 u64 GetClockCycles() override { 33 u64 GetClockCycles() override {
43 std::chrono::nanoseconds time_now = GetTimeNS(); 34 const u128 temp = Common::Multiply64Into128(GetTimeNS().count(), emulated_clock_frequency);
44 const u128 temporary = 35 return Common::Divide128On32(temp, NS_RATIO).first;
45 Common::Multiply64Into128(time_now.count(), emulated_clock_frequency);
46 return Common::Divide128On32(temporary, 1000000000).first;
47 } 36 }
48 37
49 u64 GetCPUCycles() override { 38 u64 GetCPUCycles() override {
50 std::chrono::nanoseconds time_now = GetTimeNS(); 39 const u128 temp = Common::Multiply64Into128(GetTimeNS().count(), emulated_cpu_frequency);
51 const u128 temporary = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency); 40 return Common::Divide128On32(temp, NS_RATIO).first;
52 return Common::Divide128On32(temporary, 1000000000).first;
53 } 41 }
54 42
55 void Pause([[maybe_unused]] bool is_paused) override { 43 void Pause([[maybe_unused]] bool is_paused) override {
@@ -57,7 +45,7 @@ public:
57 } 45 }
58 46
59private: 47private:
60 base_time_point start_time; 48 SteadyClock::time_point start_time;
61}; 49};
62 50
63#ifdef ARCHITECTURE_x86_64 51#ifdef ARCHITECTURE_x86_64
@@ -93,4 +81,9 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
93 81
94#endif 82#endif
95 83
84std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency,
85 u64 emulated_clock_frequency) {
86 return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
87}
88
96} // namespace Common 89} // namespace Common
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h
index 828a523a8..157ec5eae 100644
--- a/src/common/wall_clock.h
+++ b/src/common/wall_clock.h
@@ -55,4 +55,7 @@ private:
55[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, 55[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
56 u64 emulated_clock_frequency); 56 u64 emulated_clock_frequency);
57 57
58[[nodiscard]] std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency,
59 u64 emulated_clock_frequency);
60
58} // namespace Common 61} // namespace Common
diff --git a/src/common/windows/timer_resolution.cpp b/src/common/windows/timer_resolution.cpp
new file mode 100644
index 000000000..29c6e5c7e
--- /dev/null
+++ b/src/common/windows/timer_resolution.cpp
@@ -0,0 +1,109 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <windows.h>
5
6#include "common/windows/timer_resolution.h"
7
8extern "C" {
9// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtQueryTimerResolution.html
10NTSYSAPI LONG NTAPI NtQueryTimerResolution(PULONG MinimumResolution, PULONG MaximumResolution,
11 PULONG CurrentResolution);
12
13// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtSetTimerResolution.html
14NTSYSAPI LONG NTAPI NtSetTimerResolution(ULONG DesiredResolution, BOOLEAN SetResolution,
15 PULONG CurrentResolution);
16
17// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FThread%2FNtDelayExecution.html
18NTSYSAPI LONG NTAPI NtDelayExecution(BOOLEAN Alertable, PLARGE_INTEGER DelayInterval);
19}
20
21// Defines for compatibility with older Windows 10 SDKs.
22
23#ifndef PROCESS_POWER_THROTTLING_EXECUTION_SPEED
24#define PROCESS_POWER_THROTTLING_EXECUTION_SPEED 0x1
25#endif
26#ifndef PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION
27#define PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION 0x4
28#endif
29
30namespace Common::Windows {
31
32namespace {
33
34using namespace std::chrono;
35
36constexpr nanoseconds ToNS(ULONG hundred_ns) {
37 return nanoseconds{hundred_ns * 100};
38}
39
40constexpr ULONG ToHundredNS(nanoseconds ns) {
41 return static_cast<ULONG>(ns.count()) / 100;
42}
43
44struct TimerResolution {
45 std::chrono::nanoseconds minimum;
46 std::chrono::nanoseconds maximum;
47 std::chrono::nanoseconds current;
48};
49
50TimerResolution GetTimerResolution() {
51 ULONG MinimumTimerResolution;
52 ULONG MaximumTimerResolution;
53 ULONG CurrentTimerResolution;
54 NtQueryTimerResolution(&MinimumTimerResolution, &MaximumTimerResolution,
55 &CurrentTimerResolution);
56 return {
57 .minimum{ToNS(MinimumTimerResolution)},
58 .maximum{ToNS(MaximumTimerResolution)},
59 .current{ToNS(CurrentTimerResolution)},
60 };
61}
62
63void SetHighQoS() {
64 // https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service
65 PROCESS_POWER_THROTTLING_STATE PowerThrottling{
66 .Version{PROCESS_POWER_THROTTLING_CURRENT_VERSION},
67 .ControlMask{PROCESS_POWER_THROTTLING_EXECUTION_SPEED |
68 PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION},
69 .StateMask{},
70 };
71 SetProcessInformation(GetCurrentProcess(), ProcessPowerThrottling, &PowerThrottling,
72 sizeof(PROCESS_POWER_THROTTLING_STATE));
73}
74
75} // Anonymous namespace
76
77nanoseconds GetMinimumTimerResolution() {
78 return GetTimerResolution().minimum;
79}
80
81nanoseconds GetMaximumTimerResolution() {
82 return GetTimerResolution().maximum;
83}
84
85nanoseconds GetCurrentTimerResolution() {
86 return GetTimerResolution().current;
87}
88
89nanoseconds SetCurrentTimerResolution(nanoseconds timer_resolution) {
90 // Set the timer resolution, and return the current timer resolution.
91 const auto DesiredTimerResolution = ToHundredNS(timer_resolution);
92 ULONG CurrentTimerResolution;
93 NtSetTimerResolution(DesiredTimerResolution, TRUE, &CurrentTimerResolution);
94 return ToNS(CurrentTimerResolution);
95}
96
97nanoseconds SetCurrentTimerResolutionToMaximum() {
98 SetHighQoS();
99 return SetCurrentTimerResolution(GetMaximumTimerResolution());
100}
101
102void SleepForOneTick() {
103 LARGE_INTEGER DelayInterval{
104 .QuadPart{-1},
105 };
106 NtDelayExecution(FALSE, &DelayInterval);
107}
108
109} // namespace Common::Windows
diff --git a/src/common/windows/timer_resolution.h b/src/common/windows/timer_resolution.h
new file mode 100644
index 000000000..e1e50a62d
--- /dev/null
+++ b/src/common/windows/timer_resolution.h
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <chrono>
7
8namespace Common::Windows {
9
10/// Returns the minimum (least precise) supported timer resolution in nanoseconds.
11std::chrono::nanoseconds GetMinimumTimerResolution();
12
13/// Returns the maximum (most precise) supported timer resolution in nanoseconds.
14std::chrono::nanoseconds GetMaximumTimerResolution();
15
16/// Returns the current timer resolution in nanoseconds.
17std::chrono::nanoseconds GetCurrentTimerResolution();
18
19/**
20 * Sets the current timer resolution.
21 *
22 * @param timer_resolution Timer resolution in nanoseconds.
23 *
24 * @returns The current timer resolution.
25 */
26std::chrono::nanoseconds SetCurrentTimerResolution(std::chrono::nanoseconds timer_resolution);
27
28/**
29 * Sets the current timer resolution to the maximum supported timer resolution.
30 *
31 * @returns The current timer resolution.
32 */
33std::chrono::nanoseconds SetCurrentTimerResolutionToMaximum();
34
35/// Sleep for one tick of the current timer resolution.
36void SleepForOneTick();
37
38} // namespace Common::Windows
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index 8b08332ab..bc1a973b0 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -6,6 +6,7 @@
6#include <thread> 6#include <thread>
7 7
8#include "common/atomic_ops.h" 8#include "common/atomic_ops.h"
9#include "common/steady_clock.h"
9#include "common/uint128.h" 10#include "common/uint128.h"
10#include "common/x64/native_clock.h" 11#include "common/x64/native_clock.h"
11 12
@@ -39,6 +40,12 @@ static u64 FencedRDTSC() {
39} 40}
40#endif 41#endif
41 42
43template <u64 Nearest>
44static u64 RoundToNearest(u64 value) {
45 const auto mod = value % Nearest;
46 return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod);
47}
48
42u64 EstimateRDTSCFrequency() { 49u64 EstimateRDTSCFrequency() {
43 // Discard the first result measuring the rdtsc. 50 // Discard the first result measuring the rdtsc.
44 FencedRDTSC(); 51 FencedRDTSC();
@@ -46,18 +53,18 @@ u64 EstimateRDTSCFrequency() {
46 FencedRDTSC(); 53 FencedRDTSC();
47 54
48 // Get the current time. 55 // Get the current time.
49 const auto start_time = std::chrono::steady_clock::now(); 56 const auto start_time = Common::SteadyClock::Now();
50 const u64 tsc_start = FencedRDTSC(); 57 const u64 tsc_start = FencedRDTSC();
51 // Wait for 200 milliseconds. 58 // Wait for 250 milliseconds.
52 std::this_thread::sleep_for(std::chrono::milliseconds{200}); 59 std::this_thread::sleep_for(std::chrono::milliseconds{250});
53 const auto end_time = std::chrono::steady_clock::now(); 60 const auto end_time = Common::SteadyClock::Now();
54 const u64 tsc_end = FencedRDTSC(); 61 const u64 tsc_end = FencedRDTSC();
55 // Calculate differences. 62 // Calculate differences.
56 const u64 timer_diff = static_cast<u64>( 63 const u64 timer_diff = static_cast<u64>(
57 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); 64 std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
58 const u64 tsc_diff = tsc_end - tsc_start; 65 const u64 tsc_diff = tsc_end - tsc_start;
59 const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); 66 const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
60 return tsc_freq; 67 return RoundToNearest<1000>(tsc_freq);
61} 68}
62 69
63namespace X64 { 70namespace X64 {