// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include #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(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