diff options
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/CMakeLists.txt | 11 | ||||
| -rw-r--r-- | src/common/bit_cast.h | 20 | ||||
| -rw-r--r-- | src/common/input.h | 2 | ||||
| -rw-r--r-- | src/common/overflow.h | 22 | ||||
| -rw-r--r-- | src/common/settings.h | 8 | ||||
| -rw-r--r-- | src/common/steady_clock.cpp | 56 | ||||
| -rw-r--r-- | src/common/steady_clock.h | 23 | ||||
| -rw-r--r-- | src/common/wall_clock.cpp | 39 | ||||
| -rw-r--r-- | src/common/wall_clock.h | 3 | ||||
| -rw-r--r-- | src/common/windows/timer_resolution.cpp | 109 | ||||
| -rw-r--r-- | src/common/windows/timer_resolution.h | 38 | ||||
| -rw-r--r-- | src/common/x64/native_clock.cpp | 17 |
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 | ||
| 148 | if (WIN32) | ||
| 149 | target_sources(common PRIVATE | ||
| 150 | windows/timer_resolution.cpp | ||
| 151 | windows/timer_resolution.h | ||
| 152 | ) | ||
| 153 | target_link_libraries(common PRIVATE ntdll) | ||
| 154 | endif() | ||
| 155 | |||
| 145 | if(ARCHITECTURE_x86_64) | 156 | if(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 | ||
| 9 | namespace Common { | 12 | namespace Common { |
| 10 | 13 | ||
| 11 | template <typename To, typename From> | 14 | template <typename To, typename From> |
| 12 | [[nodiscard]] std::enable_if_t<sizeof(To) == sizeof(From) && std::is_trivially_copyable_v<From> && | 15 | constexpr 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); |
| 15 | BitCast(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 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | template <typename T> | ||
| 12 | requires(std::is_integral_v<T> && std::is_signed_v<T>) | ||
| 13 | inline 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 | |||
| 12 | namespace Common { | ||
| 13 | |||
| 14 | #ifdef _WIN32 | ||
| 15 | static s64 WindowsQueryPerformanceFrequency() { | ||
| 16 | LARGE_INTEGER frequency; | ||
| 17 | QueryPerformanceFrequency(&frequency); | ||
| 18 | return frequency.QuadPart; | ||
| 19 | } | ||
| 20 | |||
| 21 | static s64 WindowsQueryPerformanceCounter() { | ||
| 22 | LARGE_INTEGER counter; | ||
| 23 | QueryPerformanceCounter(&counter); | ||
| 24 | return counter.QuadPart; | ||
| 25 | } | ||
| 26 | #endif | ||
| 27 | |||
| 28 | SteadyClock::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 | |||
| 10 | namespace Common { | ||
| 11 | |||
| 12 | struct 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 | ||
| 12 | namespace Common { | 13 | namespace Common { |
| 13 | 14 | ||
| 14 | using base_timer = std::chrono::steady_clock; | ||
| 15 | using base_time_point = std::chrono::time_point<base_timer>; | ||
| 16 | |||
| 17 | class StandardWallClock final : public WallClock { | 15 | class StandardWallClock final : public WallClock { |
| 18 | public: | 16 | public: |
| 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 | ||
| 59 | private: | 47 | private: |
| 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 | ||
| 84 | std::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 | |||
| 8 | extern "C" { | ||
| 9 | // http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtQueryTimerResolution.html | ||
| 10 | NTSYSAPI 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 | ||
| 14 | NTSYSAPI 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 | ||
| 18 | NTSYSAPI 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 | |||
| 30 | namespace Common::Windows { | ||
| 31 | |||
| 32 | namespace { | ||
| 33 | |||
| 34 | using namespace std::chrono; | ||
| 35 | |||
| 36 | constexpr nanoseconds ToNS(ULONG hundred_ns) { | ||
| 37 | return nanoseconds{hundred_ns * 100}; | ||
| 38 | } | ||
| 39 | |||
| 40 | constexpr ULONG ToHundredNS(nanoseconds ns) { | ||
| 41 | return static_cast<ULONG>(ns.count()) / 100; | ||
| 42 | } | ||
| 43 | |||
| 44 | struct TimerResolution { | ||
| 45 | std::chrono::nanoseconds minimum; | ||
| 46 | std::chrono::nanoseconds maximum; | ||
| 47 | std::chrono::nanoseconds current; | ||
| 48 | }; | ||
| 49 | |||
| 50 | TimerResolution 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 | |||
| 63 | void 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 | |||
| 77 | nanoseconds GetMinimumTimerResolution() { | ||
| 78 | return GetTimerResolution().minimum; | ||
| 79 | } | ||
| 80 | |||
| 81 | nanoseconds GetMaximumTimerResolution() { | ||
| 82 | return GetTimerResolution().maximum; | ||
| 83 | } | ||
| 84 | |||
| 85 | nanoseconds GetCurrentTimerResolution() { | ||
| 86 | return GetTimerResolution().current; | ||
| 87 | } | ||
| 88 | |||
| 89 | nanoseconds 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 | |||
| 97 | nanoseconds SetCurrentTimerResolutionToMaximum() { | ||
| 98 | SetHighQoS(); | ||
| 99 | return SetCurrentTimerResolution(GetMaximumTimerResolution()); | ||
| 100 | } | ||
| 101 | |||
| 102 | void 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 | |||
| 8 | namespace Common::Windows { | ||
| 9 | |||
| 10 | /// Returns the minimum (least precise) supported timer resolution in nanoseconds. | ||
| 11 | std::chrono::nanoseconds GetMinimumTimerResolution(); | ||
| 12 | |||
| 13 | /// Returns the maximum (most precise) supported timer resolution in nanoseconds. | ||
| 14 | std::chrono::nanoseconds GetMaximumTimerResolution(); | ||
| 15 | |||
| 16 | /// Returns the current timer resolution in nanoseconds. | ||
| 17 | std::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 | */ | ||
| 26 | std::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 | */ | ||
| 33 | std::chrono::nanoseconds SetCurrentTimerResolutionToMaximum(); | ||
| 34 | |||
| 35 | /// Sleep for one tick of the current timer resolution. | ||
| 36 | void 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 | ||
| 43 | template <u64 Nearest> | ||
| 44 | static u64 RoundToNearest(u64 value) { | ||
| 45 | const auto mod = value % Nearest; | ||
| 46 | return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod); | ||
| 47 | } | ||
| 48 | |||
| 42 | u64 EstimateRDTSCFrequency() { | 49 | u64 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 | ||
| 63 | namespace X64 { | 70 | namespace X64 { |