win32: increase hires timer resolution

timeBeginPeriod() only allows setting minimum timer resolution
to 1 ms. However, modern x86 platforms support a minimum timer
resolution of 0.5 ms. Use NtSetTimerResolution() instead for
the increased resolution, which can be set with MPV_HRT_RES.

Additionally, change the units of mp_start_hires_timers(),
mp_end_hires_timer(), MPV_HRT_RES, and MPV_HRT_MAX to nanoseconds,
in accordance with other functions used in timer.h.
This commit is contained in:
nanahi 2023-10-26 15:13:12 -04:00 committed by Kacper Michajłow
parent bfd016d101
commit aff376e066
5 changed files with 40 additions and 21 deletions

View File

@ -497,6 +497,7 @@ if features['win32-desktop']
cc.find_library('dwmapi'),
cc.find_library('gdi32'),
cc.find_library('imm32'),
cc.find_library('ntdll'),
cc.find_library('ole32'),
cc.find_library('uuid'),
cc.find_library('uxtheme'),

View File

@ -115,7 +115,7 @@ static inline int mp_cond_timedwait(mp_cond *cond, mp_mutex *mutex, int64_t time
timeout = MPCLAMP(timeout, 0, MP_TIME_MS_TO_NS(INFINITE)) / MP_TIME_MS_TO_NS(1);
int ret = 0;
int hrt = mp_start_hires_timers(timeout);
int64_t hrt = mp_start_hires_timers(MP_TIME_MS_TO_NS(timeout));
BOOL bRet;
if (mutex->use_cs) {

View File

@ -16,6 +16,8 @@
*/
#include <windows.h>
#include <winternl.h>
#include <ntstatus.h>
#include <sys/time.h>
#include <mmsystem.h>
#include <stdlib.h>
@ -27,16 +29,23 @@
static LARGE_INTEGER perf_freq;
// ms values
static int hires_max = 50;
static int hires_res = 1;
static int64_t hires_max = MP_TIME_MS_TO_NS(50);
static int64_t hires_res = MP_TIME_MS_TO_NS(1);
int mp_start_hires_timers(int wait_ms)
// NtSetTimerResolution allows setting the timer resolution to less than 1 ms.
// Resolutions are specified in 100-ns units.
// If Set is TRUE, set the RequestedResolution. Otherwise, return to the previous resolution.
NTSTATUS NTAPI NtSetTimerResolution(ULONG RequestedResolution, BOOLEAN Set, PULONG ActualResolution);
// Acquire the valid timer resolution range.
NTSTATUS NTAPI NtQueryTimerResolution(PULONG MinimumResolution, PULONG MaximumResolution, PULONG ActualResolution);
int64_t mp_start_hires_timers(int64_t wait_ns)
{
#if !HAVE_UWP
// policy: request hires_res ms resolution if wait < hires_max ms
if (wait_ms > 0 && wait_ms <= hires_max &&
timeBeginPeriod(hires_res) == TIMERR_NOERROR)
ULONG actual_res = 0;
// policy: request hires_res resolution if wait < hires_max ns
if (wait_ns > 0 && wait_ns <= hires_max &&
NtSetTimerResolution(hires_res / 100, TRUE, &actual_res) == STATUS_SUCCESS)
{
return hires_res;
}
@ -44,11 +53,12 @@ int mp_start_hires_timers(int wait_ms)
return 0;
}
void mp_end_hires_timers(int res_ms)
void mp_end_hires_timers(int64_t res_ns)
{
#if !HAVE_UWP
if (res_ms > 0)
timeEndPeriod(res_ms);
ULONG actual_res = 0;
if (res_ns > 0)
NtSetTimerResolution(res_ns / 100, FALSE, &actual_res);
#endif
}
@ -57,7 +67,7 @@ void mp_sleep_ns(int64_t ns)
if (ns < 0)
return;
int hrt = mp_start_hires_timers(ns < 1e6 ? 1 : ns / 1e6);
int64_t hrt = mp_start_hires_timers(ns);
#ifndef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
#define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x2
@ -107,21 +117,28 @@ void mp_raw_time_init(void)
QueryPerformanceFrequency(&perf_freq);
#if !HAVE_UWP
ULONG min_res, max_res, actual_res;
if (NtQueryTimerResolution(&min_res, &max_res, &actual_res) != STATUS_SUCCESS) {
min_res = 156250;
max_res = 10000;
}
// allow (undocumented) control of all the High Res Timers parameters,
// for easier experimentation and diagnostic of bug reports.
const char *v;
char *end;
// 1..1000 ms max timetout for hires (used in "perwait" mode)
if ((v = getenv("MPV_HRT_MAX"))) {
int hmax = atoi(v);
if (hmax >= 1 && hmax <= 1000)
int64_t hmax = strtoll(v, &end, 10);
if (*end == '\0' && hmax >= MP_TIME_MS_TO_NS(1) && hmax <= MP_TIME_MS_TO_NS(1000))
hires_max = hmax;
}
// 1..15 ms hires resolution (not used in "never" mode)
// hires resolution clamped by the available resolution range (not used in "never" mode)
if ((v = getenv("MPV_HRT_RES"))) {
int res = atoi(v);
if (res >= 1 && res <= 15)
int64_t res = strtoll(v, &end, 10);
if (*end == '\0' && res >= max_res * INT64_C(100) && res <= min_res * INT64_C(100))
hires_res = res;
}
@ -134,8 +151,8 @@ void mp_raw_time_init(void)
} else if (!strcmp(v, "never")) {
hires_max = 0;
} else { // "always" or unknown value
mp_start_hires_timers(hires_res);
hires_max = 0;
timeBeginPeriod(hires_res);
}
#endif
}

View File

@ -39,11 +39,11 @@ uint64_t mp_raw_time_ns(void);
void mp_sleep_ns(int64_t ns);
#ifdef _WIN32
// returns: timer resolution in ms if needed and started successfully, else 0
int mp_start_hires_timers(int wait_ms);
// returns: timer resolution in ns if needed and started successfully, else 0
int64_t mp_start_hires_timers(int64_t wait_ns);
// call unconditionally with the return value of mp_start_hires_timers
void mp_end_hires_timers(int resolution_ms);
void mp_end_hires_timers(int64_t resolution_ns);
#endif /* _WIN32 */
// Converts time units to nanoseconds (int64_t)

View File

@ -52,6 +52,7 @@ endif
if features['win32-desktop']
test_utils_deps += cc.find_library('imm32')
test_utils_deps += cc.find_library('ntdll')
test_utils_deps += cc.find_library('winmm')
endif
test_utils_objects = libmpv.extract_objects(test_utils_files)