From a20e8eced5e679bcef201579e128cdb616f00baf Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 29 Jan 2025 10:56:40 -0600 Subject: [PATCH] store win32 timer start & frequency in terms of nanoseconds --- src/sys_win32.c | 57 +++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/src/sys_win32.c b/src/sys_win32.c index d1fe06b3..e0675725 100644 --- a/src/sys_win32.c +++ b/src/sys_win32.c @@ -98,8 +98,9 @@ struct win32_window { GLOBAL struct { SYSTEM_INFO info; - LARGE_INTEGER timer_frequency; - LARGE_INTEGER timer_start; + i64 timer_frequency_s; + i64 timer_frequency_ns; + i64 timer_start_ns; i32 scheduler_period_ms; DWORD thread_tls_index; u32 main_thread_id; @@ -235,21 +236,12 @@ INTERNAL struct sys_datetime win32_time_to_sys_time(SYSTEMTIME st) }; } -/* prevent 64-bit overflow when computing relative timestamp - * https://github.com/floooh/sokol/blob/d4ac122f36d7659a18b312fd4fa2317fb9e06a63/sokol_time.h#L203 - */ -INTERNAL i64 _win32_i64_muldiv(i64 value, i64 numer, i64 denom) { - i64 q = value / denom; - i64 r = value % denom; - return q * numer + r * numer / denom; -} - struct sys_timestamp sys_timestamp_prog(void) { struct sys_timestamp ts; - LARGE_INTEGER time; - QueryPerformanceCounter(&time); - ts.v = (u64)_win32_i64_muldiv(time.QuadPart - G.timer_start.QuadPart, 1000000000, G.timer_frequency.QuadPart); + LARGE_INTEGER qpc; + QueryPerformanceCounter(&qpc); + ts.v = (qpc.QuadPart * G.timer_frequency_ns) - G.timer_start_ns; return ts; } @@ -1951,24 +1943,24 @@ INTERNAL void win32_precise_sleep_timer(f64 seconds, HANDLE timer) /* TODO: Does the high frequency timer even require setting / scaling of * timeBeginPeriod/scheduler_period_ms? There isn't much documentation. */ - i64 qpc_per_second = G.timer_frequency.QuadPart; + i64 qpc_per_second = G.timer_frequency_s; i32 scheduler_period_ms = G.scheduler_period_ms; LARGE_INTEGER qpc; QueryPerformanceCounter(&qpc); - INT64 target_qpc = (INT64)(qpc.QuadPart + seconds * qpc_per_second); + i64 target_qpc = (i64)(qpc.QuadPart + seconds * qpc_per_second); /* TODO: Maybe increase tolerance for higher precision but more power usage */ - //const double tolerance = 0.001200 * scheduler_period_ms; - const double tolerance = 0.000520 * scheduler_period_ms; - //const double tolerance = 1 * scheduler_period_ms; + //const f64 tolerance = 0.001200 * scheduler_period_ms; + const f64 tolerance = 0.000520 * scheduler_period_ms; + //const f64 tolerance = 1 * scheduler_period_ms; - INT64 max_ticks = (INT64)scheduler_period_ms * 9500; + i64 max_ticks = (i64)scheduler_period_ms * 9500; while (true) { __profscope(win32_sleep_part); /* Break sleep up into parts that are lower than scheduler period */ - double remaining_seconds = (double)(target_qpc - qpc.QuadPart) / (double)qpc_per_second; - INT64 sleep_ticks = (INT64)((remaining_seconds - tolerance) * 10000000); + f64 remaining_seconds = (f64)(target_qpc - qpc.QuadPart) / (f64)qpc_per_second; + i64 sleep_ticks = (i64)((remaining_seconds - tolerance) * 10000000); if (sleep_ticks <= 0) { break; } @@ -1992,7 +1984,7 @@ INTERNAL void win32_precise_sleep_timer(f64 seconds, HANDLE timer) INTERNAL void win32_precise_sleep_legacy(f64 seconds) { __prof; - i64 qpc_per_second = G.timer_frequency.QuadPart; + i64 qpc_per_second = G.timer_frequency_s; i32 scheduler_period_ms = G.scheduler_period_ms; LARGE_INTEGER qpc; @@ -2080,13 +2072,18 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance, /* Query system info */ GetSystemInfo(&G.info); - QueryPerformanceFrequency(&G.timer_frequency); - QueryPerformanceCounter(&G.timer_start); - { - TIMECAPS caps; - timeGetDevCaps(&caps, sizeof caps); - G.scheduler_period_ms = (i32)caps.wPeriodMin; - } + + LARGE_INTEGER qpf; + QueryPerformanceFrequency(&qpf); + G.timer_frequency_s = qpf.QuadPart; + G.timer_frequency_ns = 1000000000 / qpf.QuadPart; + + LARGE_INTEGER qpc; + QueryPerformanceCounter(&qpc); + G.timer_start_ns = qpc.QuadPart * G.timer_frequency_ns; + + TIMECAPS caps; + timeGetDevCaps(&caps, sizeof(caps)); /* Set up timing period */ timeBeginPeriod(G.scheduler_period_ms);