Browse Source
common: Implement a method to change the Windows timer resolution
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.nce_cpp
3 changed files with 133 additions and 0 deletions
-
8src/common/CMakeLists.txt
-
87src/common/windows/timer_resolution.cpp
-
38src/common/windows/timer_resolution.h
@ -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
|
||||
@ -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 |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue