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.
This commit is contained in:
parent
bd09c82521
commit
1ed49f92dd
3 changed files with 133 additions and 0 deletions
|
@ -144,6 +144,14 @@ add_library(common STATIC
|
||||||
zstd_compression.h
|
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)
|
if(ARCHITECTURE_x86_64)
|
||||||
target_sources(common
|
target_sources(common
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
|
87
src/common/windows/timer_resolution.cpp
Normal file
87
src/common/windows/timer_resolution.cpp
Normal file
|
@ -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
src/common/windows/timer_resolution.h
Normal file
38
src/common/windows/timer_resolution.h
Normal file
|
@ -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
|
Loading…
Reference in a new issue