diff options
Diffstat (limited to 'src/common/windows')
| -rw-r--r-- | src/common/windows/timer_resolution.cpp | 109 | ||||
| -rw-r--r-- | src/common/windows/timer_resolution.h | 38 |
2 files changed, 147 insertions, 0 deletions
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 | ||