From 9606c3fca9d568dc43711017dcb35a408c0d2883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Sun, 10 Sep 2023 02:09:28 +0200 Subject: [PATCH] timer: teach it about nanoseconds Those changes will alow to change vsync base to more precise time base. In general there is no reason to truncate values returned by system. --- audio/out/ao_audiotrack.c | 2 +- osdep/timer-darwin.c | 10 +++++----- osdep/timer-linux.c | 22 +++++++--------------- osdep/timer-win2.c | 8 ++++---- osdep/timer.c | 39 +++++++++++++++++++++++++++++---------- osdep/timer.h | 12 +++++++++++- 6 files changed, 57 insertions(+), 36 deletions(-) diff --git a/audio/out/ao_audiotrack.c b/audio/out/ao_audiotrack.c index 9feceaec30..3fd68e5783 100644 --- a/audio/out/ao_audiotrack.c +++ b/audio/out/ao_audiotrack.c @@ -372,7 +372,7 @@ static uint32_t AudioTrack_getPlaybackHeadPosition(struct ao *ao) return 0; JNIEnv *env = MP_JNI_GET_ENV(ao); uint32_t pos = 0; - int64_t now = mp_raw_time_us() * 1000; + int64_t now = mp_raw_time_ns(); int state = MP_JNI_CALL_INT(p->audiotrack, AudioTrack.getPlayState); int stable_count = 20; diff --git a/osdep/timer-darwin.c b/osdep/timer-darwin.c index 2e7122eabf..a114d0d727 100644 --- a/osdep/timer-darwin.c +++ b/osdep/timer-darwin.c @@ -26,18 +26,18 @@ #include "common/msg.h" #include "timer.h" -static double timebase_ratio; +static double timebase_ratio_ns; void mp_sleep_us(int64_t us) { - uint64_t deadline = us / 1e6 / timebase_ratio + mach_absolute_time(); + uint64_t deadline = us * 1e3 / timebase_ratio_ns + mach_absolute_time(); mach_wait_until(deadline); } -uint64_t mp_raw_time_us(void) +uint64_t mp_raw_time_ns(void) { - return mach_absolute_time() * timebase_ratio * 1e6; + return mach_absolute_time() * timebase_ratio_ns; } void mp_raw_time_init(void) @@ -45,5 +45,5 @@ void mp_raw_time_init(void) struct mach_timebase_info timebase; mach_timebase_info(&timebase); - timebase_ratio = (double)timebase.numer / (double)timebase.denom * 1e-9; + timebase_ratio_ns = (double)timebase.numer / (double)timebase.denom; } diff --git a/osdep/timer-linux.c b/osdep/timer-linux.c index 281a6013f3..0289233695 100644 --- a/osdep/timer-linux.c +++ b/osdep/timer-linux.c @@ -18,10 +18,8 @@ * License along with mpv. If not, see . */ -#include #include #include -#include #include "timer.h" void mp_sleep_us(int64_t us) @@ -34,22 +32,16 @@ void mp_sleep_us(int64_t us) nanosleep(&ts, NULL); } -#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && defined(CLOCK_MONOTONIC) -uint64_t mp_raw_time_us(void) +uint64_t mp_raw_time_ns(void) { - struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts)) - abort(); - return ts.tv_sec * 1000000LL + ts.tv_nsec / 1000; -} + struct timespec tp = {0}; +#if defined(CLOCK_MONOTONIC_RAW) + clock_gettime(CLOCK_MONOTONIC_RAW, &tp); #else -uint64_t mp_raw_time_us(void) -{ - struct timeval tv; - gettimeofday(&tv,NULL); - return tv.tv_sec * 1000000LL + tv.tv_usec; -} + timespec_get(&tp, TIME_UTC); #endif + return tp.tv_sec * UINT64_C(1000000000) + tp.tv_nsec; +} void mp_raw_time_init(void) { diff --git a/osdep/timer-win2.c b/osdep/timer-win2.c index 72bcca5b4e..dd7a42f0d1 100644 --- a/osdep/timer-win2.c +++ b/osdep/timer-win2.c @@ -66,16 +66,16 @@ void mp_sleep_us(int64_t us) mp_end_hires_timers(hrt); } -uint64_t mp_raw_time_us(void) +uint64_t mp_raw_time_ns(void) { LARGE_INTEGER perf_count; QueryPerformanceCounter(&perf_count); - // Convert QPC units (1/perf_freq seconds) to microseconds. This will work + // Convert QPC units (1/perf_freq seconds) to nanoseconds. This will work // without overflow because the QPC value is guaranteed not to roll-over // within 100 years, so perf_freq must be less than 2.9*10^9. - return perf_count.QuadPart / perf_freq.QuadPart * 1000000 + - perf_count.QuadPart % perf_freq.QuadPart * 1000000 / perf_freq.QuadPart; + return perf_count.QuadPart / perf_freq.QuadPart * UINT64_C(1000000000) + + perf_count.QuadPart % perf_freq.QuadPart * UINT64_C(1000000000) / perf_freq.QuadPart; } void mp_raw_time_init(void) diff --git a/osdep/timer.c b/osdep/timer.c index 6f8d992a1e..58a44043ca 100644 --- a/osdep/timer.c +++ b/osdep/timer.c @@ -34,8 +34,8 @@ static pthread_once_t timer_init_once = PTHREAD_ONCE_INIT; static void do_timer_init(void) { mp_raw_time_init(); - mp_rand_seed(mp_raw_time_us()); - raw_time_offset = mp_raw_time_us(); + mp_rand_seed(mp_raw_time_ns()); + raw_time_offset = mp_raw_time_ns(); // Arbitrary additional offset to avoid confusing relative/absolute times. // Also,we rule that the timer never returns 0 (so default-initialized // time values will be always in the past). @@ -49,7 +49,12 @@ void mp_time_init(void) int64_t mp_time_us(void) { - int64_t r = mp_raw_time_us() - raw_time_offset; + return mp_time_ns() / 1000; +} + +int64_t mp_time_ns(void) +{ + uint64_t r = mp_raw_time_ns() - raw_time_offset; if (r < MP_START_TIME) r = MP_START_TIME; return r; @@ -57,13 +62,13 @@ int64_t mp_time_us(void) double mp_time_sec(void) { - return mp_time_us() / (double)(1000 * 1000); + return mp_time_ns() / 1e9; } int64_t mp_time_us_add(int64_t time_us, double timeout_sec) { assert(time_us > 0); // mp_time_us() returns strictly positive values - double t = MPCLAMP(timeout_sec * (1000 * 1000), -0x1p63, 0x1p63); + double t = MPCLAMP(timeout_sec * 1e6, -0x1p63, 0x1p63); int64_t ti = t == 0x1p63 ? INT64_MAX : (int64_t)t; if (ti > INT64_MAX - time_us) return INT64_MAX; @@ -72,6 +77,18 @@ int64_t mp_time_us_add(int64_t time_us, double timeout_sec) return time_us + ti; } +int64_t mp_time_ns_add(int64_t time_ns, double timeout_sec) +{ + assert(time_ns > 0); // mp_time_ns() returns strictly positive values + double t = MPCLAMP(timeout_sec * 1e9, -0x1p63, 0x1p63); + int64_t ti = t == 0x1p63 ? INT64_MAX : (int64_t)t; + if (ti > INT64_MAX - time_ns) + return INT64_MAX; + if (ti <= -time_ns) + return 1; + return time_ns + ti; +} + static int get_realtime(struct timespec *out_ts) { #if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 @@ -87,16 +104,18 @@ static int get_realtime(struct timespec *out_ts) } struct timespec mp_time_us_to_realtime(int64_t time_us) +{ + return mp_time_ns_to_realtime(MPMIN(INT64_MAX / 1000, time_us) * 1000); +} + +struct timespec mp_time_ns_to_realtime(int64_t time_ns) { struct timespec ts = {0}; if (get_realtime(&ts) != 0) return ts; - int64_t time_ns = MPMIN(INT64_MAX / 1000, time_us) * 1000; - int64_t time_now = mp_time_us() * 1000; - // clamp to 1000 days in the future - int64_t time_rel = MPMIN(time_now - time_ns, + int64_t time_rel = MPMIN(mp_time_ns() - time_ns, 1000 * 24 * 60 * 60 * INT64_C(1000000000)); ts.tv_sec += time_rel / INT64_C(1000000000); ts.tv_nsec += time_rel % INT64_C(1000000000); @@ -111,5 +130,5 @@ struct timespec mp_time_us_to_realtime(int64_t time_us) struct timespec mp_rel_time_to_timespec(double timeout_sec) { - return mp_time_us_to_realtime(mp_time_us_add(mp_time_us(), timeout_sec)); + return mp_time_ns_to_realtime(mp_time_ns_add(mp_time_ns(), timeout_sec)); } diff --git a/osdep/timer.h b/osdep/timer.h index a511812b56..546d8fab41 100644 --- a/osdep/timer.h +++ b/osdep/timer.h @@ -26,13 +26,16 @@ void mp_time_init(void); // Return time in microseconds. Never wraps. Never returns 0 or negative values. int64_t mp_time_us(void); +// Return time in nanoseconds. Never wraps. Never returns 0 or negative values. +int64_t mp_time_ns(void); + // Return time in seconds. Can have down to 1 microsecond resolution, but will // be much worse when casted to float. double mp_time_sec(void); // Provided by OS specific functions (timer-linux.c) void mp_raw_time_init(void); -uint64_t mp_raw_time_us(void); +uint64_t mp_raw_time_ns(void); // Sleep in microseconds. void mp_sleep_us(int64_t us); @@ -54,9 +57,16 @@ void mp_end_hires_timers(int resolution_ms); // Takes care of possible overflows. Never returns a negative or 0 time. int64_t mp_time_us_add(int64_t time_us, double timeout_sec); +// Add a time in seconds to the given time in nanoseconds, and return it. +// Takes care of possible overflows. Never returns a negative or 0 time. +int64_t mp_time_ns_add(int64_t time_ns, double timeout_sec); + // Convert the mp time in microseconds to a timespec using CLOCK_REALTIME. struct timespec mp_time_us_to_realtime(int64_t time_us); +// Convert the mp time in nanoseconds to a timespec using CLOCK_REALTIME. +struct timespec mp_time_ns_to_realtime(int64_t time_ns); + // Convert the relative timeout in seconds to a timespec. // The timespec is absolute, using CLOCK_REALTIME. struct timespec mp_rel_time_to_timespec(double timeout_sec);