common: Implement a method to change the Windows timer resolution
This utilizes undocumented NtDll functions to change the current timer resolution from the default of 1ms.
This commit is contained in:
		| @@ -144,6 +144,14 @@ add_library(common STATIC | ||||
|     zstd_compression.h | ||||
| ) | ||||
|  | ||||
| if (WIN32) | ||||
|   target_sources(common PRIVATE | ||||
|     windows/timer_resolution.cpp | ||||
|     windows/timer_resolution.h | ||||
|   ) | ||||
|   target_link_libraries(common PRIVATE ntdll) | ||||
| endif() | ||||
|  | ||||
| if(ARCHITECTURE_x86_64) | ||||
|     target_sources(common | ||||
|         PRIVATE | ||||
|   | ||||
							
								
								
									
										87
									
								
								src/common/windows/timer_resolution.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/common/windows/timer_resolution.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include <windows.h> | ||||
|  | ||||
| #include "common/windows/timer_resolution.h" | ||||
|  | ||||
| extern "C" { | ||||
| // http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtQueryTimerResolution.html | ||||
| NTSYSAPI LONG NTAPI NtQueryTimerResolution(PULONG MinimumResolution, PULONG MaximumResolution, | ||||
|                                            PULONG CurrentResolution); | ||||
|  | ||||
| // http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtSetTimerResolution.html | ||||
| NTSYSAPI LONG NTAPI NtSetTimerResolution(ULONG DesiredResolution, BOOLEAN SetResolution, | ||||
|                                          PULONG CurrentResolution); | ||||
|  | ||||
| // http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FThread%2FNtDelayExecution.html | ||||
| NTSYSAPI LONG NTAPI NtDelayExecution(BOOLEAN Alertable, PLARGE_INTEGER DelayInterval); | ||||
| } | ||||
|  | ||||
| namespace Common::Windows { | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| using namespace std::chrono; | ||||
|  | ||||
| constexpr nanoseconds ToNS(ULONG hundred_ns) { | ||||
|     return nanoseconds{hundred_ns * 100}; | ||||
| } | ||||
|  | ||||
| constexpr ULONG ToHundredNS(nanoseconds ns) { | ||||
|     return static_cast<ULONG>(ns.count()) / 100; | ||||
| } | ||||
|  | ||||
| struct TimerResolution { | ||||
|     std::chrono::nanoseconds minimum; | ||||
|     std::chrono::nanoseconds maximum; | ||||
|     std::chrono::nanoseconds current; | ||||
| }; | ||||
|  | ||||
| TimerResolution GetTimerResolution() { | ||||
|     ULONG MinimumTimerResolution; | ||||
|     ULONG MaximumTimerResolution; | ||||
|     ULONG CurrentTimerResolution; | ||||
|     NtQueryTimerResolution(&MinimumTimerResolution, &MaximumTimerResolution, | ||||
|                            &CurrentTimerResolution); | ||||
|     return { | ||||
|         .minimum{ToNS(MinimumTimerResolution)}, | ||||
|         .maximum{ToNS(MaximumTimerResolution)}, | ||||
|         .current{ToNS(CurrentTimerResolution)}, | ||||
|     }; | ||||
| } | ||||
|  | ||||
| } // Anonymous namespace | ||||
|  | ||||
| nanoseconds GetMinimumTimerResolution() { | ||||
|     return GetTimerResolution().minimum; | ||||
| } | ||||
|  | ||||
| nanoseconds GetMaximumTimerResolution() { | ||||
|     return GetTimerResolution().maximum; | ||||
| } | ||||
|  | ||||
| nanoseconds GetCurrentTimerResolution() { | ||||
|     return GetTimerResolution().current; | ||||
| } | ||||
|  | ||||
| nanoseconds SetCurrentTimerResolution(nanoseconds timer_resolution) { | ||||
|     // Set the timer resolution, and return the current timer resolution. | ||||
|     const auto DesiredTimerResolution = ToHundredNS(timer_resolution); | ||||
|     ULONG CurrentTimerResolution; | ||||
|     NtSetTimerResolution(DesiredTimerResolution, TRUE, &CurrentTimerResolution); | ||||
|     return ToNS(CurrentTimerResolution); | ||||
| } | ||||
|  | ||||
| nanoseconds SetCurrentTimerResolutionToMaximum() { | ||||
|     return SetCurrentTimerResolution(GetMaximumTimerResolution()); | ||||
| } | ||||
|  | ||||
| void SleepForOneTick() { | ||||
|     LARGE_INTEGER DelayInterval{ | ||||
|         .QuadPart{-1}, | ||||
|     }; | ||||
|     NtDelayExecution(FALSE, &DelayInterval); | ||||
| } | ||||
|  | ||||
| } // namespace Common::Windows | ||||
							
								
								
									
										38
									
								
								src/common/windows/timer_resolution.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/common/windows/timer_resolution.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <chrono> | ||||
|  | ||||
| namespace Common::Windows { | ||||
|  | ||||
| /// Returns the minimum (least precise) supported timer resolution in nanoseconds. | ||||
| std::chrono::nanoseconds GetMinimumTimerResolution(); | ||||
|  | ||||
| /// Returns the maximum (most precise) supported timer resolution in nanoseconds. | ||||
| std::chrono::nanoseconds GetMaximumTimerResolution(); | ||||
|  | ||||
| /// Returns the current timer resolution in nanoseconds. | ||||
| std::chrono::nanoseconds GetCurrentTimerResolution(); | ||||
|  | ||||
| /** | ||||
|  * Sets the current timer resolution. | ||||
|  * | ||||
|  * @param timer_resolution Timer resolution in nanoseconds. | ||||
|  * | ||||
|  * @returns The current timer resolution. | ||||
|  */ | ||||
| std::chrono::nanoseconds SetCurrentTimerResolution(std::chrono::nanoseconds timer_resolution); | ||||
|  | ||||
| /** | ||||
|  * Sets the current timer resolution to the maximum supported timer resolution. | ||||
|  * | ||||
|  * @returns The current timer resolution. | ||||
|  */ | ||||
| std::chrono::nanoseconds SetCurrentTimerResolutionToMaximum(); | ||||
|  | ||||
| /// Sleep for one tick of the current timer resolution. | ||||
| void SleepForOneTick(); | ||||
|  | ||||
| } // namespace Common::Windows | ||||
		Reference in New Issue
	
	Block a user