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