diff options
Diffstat (limited to '')
| -rw-r--r-- | CMakeLists.txt | 4 | ||||
| -rw-r--r-- | dist/yuzu.manifest | 6 | ||||
| -rw-r--r-- | src/common/CMakeLists.txt | 10 | ||||
| -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 | ||||
| -rw-r--r-- | src/core/core_timing.cpp | 55 | ||||
| -rw-r--r-- | src/core/core_timing.h | 6 | ||||
| -rw-r--r-- | src/core/hardware_properties.h | 8 | ||||
| -rw-r--r-- | src/video_core/gpu.cpp | 2 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 9 | ||||
| -rw-r--r-- | src/yuzu_cmd/yuzu.cpp | 4 |
16 files changed, 324 insertions, 65 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index f26a0c6b8..91ec50bef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -477,8 +477,8 @@ if (APPLE) | |||
| 477 | find_library(COCOA_LIBRARY Cocoa) | 477 | find_library(COCOA_LIBRARY Cocoa) |
| 478 | set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY}) | 478 | set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY}) |
| 479 | elseif (WIN32) | 479 | elseif (WIN32) |
| 480 | # WSAPoll and SHGetKnownFolderPath (AppData/Roaming) didn't exist before WinNT 6.x (Vista) | 480 | # Target Windows 10 |
| 481 | add_definitions(-D_WIN32_WINNT=0x0600 -DWINVER=0x0600) | 481 | add_definitions(-D_WIN32_WINNT=0x0A00 -DWINVER=0x0A00) |
| 482 | set(PLATFORM_LIBRARIES winmm ws2_32 iphlpapi) | 482 | set(PLATFORM_LIBRARIES winmm ws2_32 iphlpapi) |
| 483 | if (MINGW) | 483 | if (MINGW) |
| 484 | # PSAPI is the Process Status API | 484 | # PSAPI is the Process Status API |
diff --git a/dist/yuzu.manifest b/dist/yuzu.manifest index 10a8df9b5..f2c8639a2 100644 --- a/dist/yuzu.manifest +++ b/dist/yuzu.manifest | |||
| @@ -36,12 +36,6 @@ SPDX-License-Identifier: GPL-2.0-or-later | |||
| 36 | <application> | 36 | <application> |
| 37 | <!-- Windows 10 --> | 37 | <!-- Windows 10 --> |
| 38 | <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> | 38 | <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> |
| 39 | <!-- Windows 8.1 --> | ||
| 40 | <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> | ||
| 41 | <!-- Windows 8 --> | ||
| 42 | <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> | ||
| 43 | <!-- Windows 7 --> | ||
| 44 | <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> | ||
| 45 | </application> | 39 | </application> |
| 46 | </compatibility> | 40 | </compatibility> |
| 47 | <trustInfo | 41 | <trustInfo |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 56b247ac4..58ff5f2f3 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -113,6 +113,8 @@ add_library(common STATIC | |||
| 113 | socket_types.h | 113 | socket_types.h |
| 114 | spin_lock.cpp | 114 | spin_lock.cpp |
| 115 | spin_lock.h | 115 | spin_lock.h |
| 116 | steady_clock.cpp | ||
| 117 | steady_clock.h | ||
| 116 | stream.cpp | 118 | stream.cpp |
| 117 | stream.h | 119 | stream.h |
| 118 | string_util.cpp | 120 | string_util.cpp |
| @@ -142,6 +144,14 @@ add_library(common STATIC | |||
| 142 | zstd_compression.h | 144 | zstd_compression.h |
| 143 | ) | 145 | ) |
| 144 | 146 | ||
| 147 | if (WIN32) | ||
| 148 | target_sources(common PRIVATE | ||
| 149 | windows/timer_resolution.cpp | ||
| 150 | windows/timer_resolution.h | ||
| 151 | ) | ||
| 152 | target_link_libraries(common PRIVATE ntdll) | ||
| 153 | endif() | ||
| 154 | |||
| 145 | if(ARCHITECTURE_x86_64) | 155 | if(ARCHITECTURE_x86_64) |
| 146 | target_sources(common | 156 | target_sources(common |
| 147 | PRIVATE | 157 | PRIVATE |
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 { |
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 3a63b52e3..742cfb996 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -6,6 +6,10 @@ | |||
| 6 | #include <string> | 6 | #include <string> |
| 7 | #include <tuple> | 7 | #include <tuple> |
| 8 | 8 | ||
| 9 | #ifdef _WIN32 | ||
| 10 | #include "common/windows/timer_resolution.h" | ||
| 11 | #endif | ||
| 12 | |||
| 9 | #include "common/microprofile.h" | 13 | #include "common/microprofile.h" |
| 10 | #include "core/core_timing.h" | 14 | #include "core/core_timing.h" |
| 11 | #include "core/core_timing_util.h" | 15 | #include "core/core_timing_util.h" |
| @@ -38,7 +42,8 @@ struct CoreTiming::Event { | |||
| 38 | }; | 42 | }; |
| 39 | 43 | ||
| 40 | CoreTiming::CoreTiming() | 44 | CoreTiming::CoreTiming() |
| 41 | : clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {} | 45 | : cpu_clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)}, |
| 46 | event_clock{Common::CreateStandardWallClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {} | ||
| 42 | 47 | ||
| 43 | CoreTiming::~CoreTiming() { | 48 | CoreTiming::~CoreTiming() { |
| 44 | Reset(); | 49 | Reset(); |
| @@ -185,15 +190,15 @@ void CoreTiming::ResetTicks() { | |||
| 185 | } | 190 | } |
| 186 | 191 | ||
| 187 | u64 CoreTiming::GetCPUTicks() const { | 192 | u64 CoreTiming::GetCPUTicks() const { |
| 188 | if (is_multicore) { | 193 | if (is_multicore) [[likely]] { |
| 189 | return clock->GetCPUCycles(); | 194 | return cpu_clock->GetCPUCycles(); |
| 190 | } | 195 | } |
| 191 | return ticks; | 196 | return ticks; |
| 192 | } | 197 | } |
| 193 | 198 | ||
| 194 | u64 CoreTiming::GetClockTicks() const { | 199 | u64 CoreTiming::GetClockTicks() const { |
| 195 | if (is_multicore) { | 200 | if (is_multicore) [[likely]] { |
| 196 | return clock->GetClockCycles(); | 201 | return cpu_clock->GetClockCycles(); |
| 197 | } | 202 | } |
| 198 | return CpuCyclesToClockCycles(ticks); | 203 | return CpuCyclesToClockCycles(ticks); |
| 199 | } | 204 | } |
| @@ -252,21 +257,20 @@ void CoreTiming::ThreadLoop() { | |||
| 252 | const auto next_time = Advance(); | 257 | const auto next_time = Advance(); |
| 253 | if (next_time) { | 258 | if (next_time) { |
| 254 | // There are more events left in the queue, wait until the next event. | 259 | // There are more events left in the queue, wait until the next event. |
| 255 | const auto wait_time = *next_time - GetGlobalTimeNs().count(); | 260 | auto wait_time = *next_time - GetGlobalTimeNs().count(); |
| 256 | if (wait_time > 0) { | 261 | if (wait_time > 0) { |
| 257 | #ifdef _WIN32 | 262 | #ifdef _WIN32 |
| 258 | // Assume a timer resolution of 1ms. | 263 | const auto timer_resolution_ns = |
| 259 | static constexpr s64 TimerResolutionNS = 1000000; | 264 | Common::Windows::GetCurrentTimerResolution().count(); |
| 260 | 265 | ||
| 261 | // Sleep in discrete intervals of the timer resolution, and spin the rest. | 266 | while (!paused && !event.IsSet() && wait_time > 0) { |
| 262 | const auto sleep_time = wait_time - (wait_time % TimerResolutionNS); | 267 | wait_time = *next_time - GetGlobalTimeNs().count(); |
| 263 | if (sleep_time > 0) { | ||
| 264 | event.WaitFor(std::chrono::nanoseconds(sleep_time)); | ||
| 265 | } | ||
| 266 | 268 | ||
| 267 | while (!paused && !event.IsSet() && GetGlobalTimeNs().count() < *next_time) { | 269 | if (wait_time >= timer_resolution_ns) { |
| 268 | // Yield to reduce thread starvation. | 270 | Common::Windows::SleepForOneTick(); |
| 269 | std::this_thread::yield(); | 271 | } else { |
| 272 | std::this_thread::yield(); | ||
| 273 | } | ||
| 270 | } | 274 | } |
| 271 | 275 | ||
| 272 | if (event.IsSet()) { | 276 | if (event.IsSet()) { |
| @@ -285,9 +289,9 @@ void CoreTiming::ThreadLoop() { | |||
| 285 | } | 289 | } |
| 286 | 290 | ||
| 287 | paused_set = true; | 291 | paused_set = true; |
| 288 | clock->Pause(true); | 292 | event_clock->Pause(true); |
| 289 | pause_event.Wait(); | 293 | pause_event.Wait(); |
| 290 | clock->Pause(false); | 294 | event_clock->Pause(false); |
| 291 | } | 295 | } |
| 292 | } | 296 | } |
| 293 | 297 | ||
| @@ -303,16 +307,23 @@ void CoreTiming::Reset() { | |||
| 303 | has_started = false; | 307 | has_started = false; |
| 304 | } | 308 | } |
| 305 | 309 | ||
| 310 | std::chrono::nanoseconds CoreTiming::GetCPUTimeNs() const { | ||
| 311 | if (is_multicore) [[likely]] { | ||
| 312 | return cpu_clock->GetTimeNS(); | ||
| 313 | } | ||
| 314 | return CyclesToNs(ticks); | ||
| 315 | } | ||
| 316 | |||
| 306 | std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { | 317 | std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { |
| 307 | if (is_multicore) { | 318 | if (is_multicore) [[likely]] { |
| 308 | return clock->GetTimeNS(); | 319 | return event_clock->GetTimeNS(); |
| 309 | } | 320 | } |
| 310 | return CyclesToNs(ticks); | 321 | return CyclesToNs(ticks); |
| 311 | } | 322 | } |
| 312 | 323 | ||
| 313 | std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { | 324 | std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { |
| 314 | if (is_multicore) { | 325 | if (is_multicore) [[likely]] { |
| 315 | return clock->GetTimeUS(); | 326 | return event_clock->GetTimeUS(); |
| 316 | } | 327 | } |
| 317 | return CyclesToUs(ticks); | 328 | return CyclesToUs(ticks); |
| 318 | } | 329 | } |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index da366637b..4b89c0c39 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -122,6 +122,9 @@ public: | |||
| 122 | /// Returns current time in emulated in Clock cycles | 122 | /// Returns current time in emulated in Clock cycles |
| 123 | u64 GetClockTicks() const; | 123 | u64 GetClockTicks() const; |
| 124 | 124 | ||
| 125 | /// Returns current time in nanoseconds. | ||
| 126 | std::chrono::nanoseconds GetCPUTimeNs() const; | ||
| 127 | |||
| 125 | /// Returns current time in microseconds. | 128 | /// Returns current time in microseconds. |
| 126 | std::chrono::microseconds GetGlobalTimeUs() const; | 129 | std::chrono::microseconds GetGlobalTimeUs() const; |
| 127 | 130 | ||
| @@ -139,7 +142,8 @@ private: | |||
| 139 | 142 | ||
| 140 | void Reset(); | 143 | void Reset(); |
| 141 | 144 | ||
| 142 | std::unique_ptr<Common::WallClock> clock; | 145 | std::unique_ptr<Common::WallClock> cpu_clock; |
| 146 | std::unique_ptr<Common::WallClock> event_clock; | ||
| 143 | 147 | ||
| 144 | s64 global_timer = 0; | 148 | s64 global_timer = 0; |
| 145 | 149 | ||
diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h index 45567b840..191c28bb4 100644 --- a/src/core/hardware_properties.h +++ b/src/core/hardware_properties.h | |||
| @@ -13,11 +13,9 @@ namespace Core { | |||
| 13 | 13 | ||
| 14 | namespace Hardware { | 14 | namespace Hardware { |
| 15 | 15 | ||
| 16 | // The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz | 16 | constexpr u64 BASE_CLOCK_RATE = 1'020'000'000; // Default CPU Frequency = 1020 MHz |
| 17 | // The exact value used is of course unverified. | 17 | constexpr u64 CNTFREQ = 19'200'000; // CNTPCT_EL0 Frequency = 19.2 MHz |
| 18 | constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch cpu frequency is 1020MHz un/docked | 18 | constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores |
| 19 | constexpr u64 CNTFREQ = 19200000; // Switch's hardware clock speed | ||
| 20 | constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores | ||
| 21 | 19 | ||
| 22 | // Virtual to Physical core map. | 20 | // Virtual to Physical core map. |
| 23 | constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{ | 21 | constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{ |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 7024a19cf..2e7f9c5ed 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -197,7 +197,7 @@ struct GPU::Impl { | |||
| 197 | constexpr u64 gpu_ticks_num = 384; | 197 | constexpr u64 gpu_ticks_num = 384; |
| 198 | constexpr u64 gpu_ticks_den = 625; | 198 | constexpr u64 gpu_ticks_den = 625; |
| 199 | 199 | ||
| 200 | u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count(); | 200 | u64 nanoseconds = system.CoreTiming().GetCPUTimeNs().count(); |
| 201 | if (Settings::values.use_fast_gpu_time.GetValue()) { | 201 | if (Settings::values.use_fast_gpu_time.GetValue()) { |
| 202 | nanoseconds /= 256; | 202 | nanoseconds /= 256; |
| 203 | } | 203 | } |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f233b065e..c092507f4 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -91,6 +91,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 91 | #include "common/microprofile.h" | 91 | #include "common/microprofile.h" |
| 92 | #include "common/scm_rev.h" | 92 | #include "common/scm_rev.h" |
| 93 | #include "common/scope_exit.h" | 93 | #include "common/scope_exit.h" |
| 94 | #ifdef _WIN32 | ||
| 95 | #include "common/windows/timer_resolution.h" | ||
| 96 | #endif | ||
| 94 | #ifdef ARCHITECTURE_x86_64 | 97 | #ifdef ARCHITECTURE_x86_64 |
| 95 | #include "common/x64/cpu_detect.h" | 98 | #include "common/x64/cpu_detect.h" |
| 96 | #endif | 99 | #endif |
| @@ -377,6 +380,12 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan | |||
| 377 | LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", | 380 | LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", |
| 378 | Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); | 381 | Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); |
| 379 | LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB}); | 382 | LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB}); |
| 383 | #ifdef _WIN32 | ||
| 384 | LOG_INFO(Frontend, "Host Timer Resolution: {:.4f} ms", | ||
| 385 | std::chrono::duration_cast<std::chrono::duration<f64, std::milli>>( | ||
| 386 | Common::Windows::SetCurrentTimerResolutionToMaximum()) | ||
| 387 | .count()); | ||
| 388 | #endif | ||
| 380 | UpdateWindowTitle(); | 389 | UpdateWindowTitle(); |
| 381 | 390 | ||
| 382 | show(); | 391 | show(); |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 77edd58ca..5f39ece32 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -42,6 +42,8 @@ | |||
| 42 | #include <windows.h> | 42 | #include <windows.h> |
| 43 | 43 | ||
| 44 | #include <shellapi.h> | 44 | #include <shellapi.h> |
| 45 | |||
| 46 | #include "common/windows/timer_resolution.h" | ||
| 45 | #endif | 47 | #endif |
| 46 | 48 | ||
| 47 | #undef _UNICODE | 49 | #undef _UNICODE |
| @@ -314,6 +316,8 @@ int main(int argc, char** argv) { | |||
| 314 | 316 | ||
| 315 | #ifdef _WIN32 | 317 | #ifdef _WIN32 |
| 316 | LocalFree(argv_w); | 318 | LocalFree(argv_w); |
| 319 | |||
| 320 | Common::Windows::SetCurrentTimerResolutionToMaximum(); | ||
| 317 | #endif | 321 | #endif |
| 318 | 322 | ||
| 319 | MicroProfileOnThreadCreate("EmuThread"); | 323 | MicroProfileOnThreadCreate("EmuThread"); |