aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/common/windows
diff options
context:
space:
mode:
authorMorph <[email protected]>2023-03-01 19:27:10 -0500
committerMorph <[email protected]>2023-03-05 01:41:28 -0500
commit1ed49f92dd56289e6e31a967e602c65ccedd4ff1 (patch)
treea151df116022445b7606614e31e9b3a3b8225152 /src/common/windows
parentbd09c825218aac9255643b9aa7c75f5ba156038c (diff)
downloadyuzu-mainline-1ed49f92dd56289e6e31a967e602c65ccedd4ff1.tar.gz
yuzu-mainline-1ed49f92dd56289e6e31a967e602c65ccedd4ff1.zip
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.
Diffstat (limited to 'src/common/windows')
-rw-r--r--src/common/windows/timer_resolution.cpp87
-rw-r--r--src/common/windows/timer_resolution.h38
2 files changed, 125 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..6c2063a4c
--- /dev/null
+++ b/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
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 @@
+// 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