Просмотр исходного кода

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.
Morph 3 лет назад
Родитель
Сommit
1ed49f92dd

+ 8 - 0
src/common/CMakeLists.txt

@@ -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 - 0
src/common/windows/timer_resolution.cpp

@@ -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 - 0
src/common/windows/timer_resolution.h

@@ -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