Use timers with microsecond precision

SDL only provides millisecond precision. Use system timers to get a
better precision.
This commit is contained in:
Romain Vimont 2021-12-06 23:33:59 +01:00
parent ddb9396743
commit 682a691173
2 changed files with 51 additions and 10 deletions

View file

@ -1,16 +1,55 @@
#include "tick.h"
#include <SDL2/SDL_timer.h>
#include <assert.h>
#include <time.h>
#ifdef _WIN32
# include <windows.h>
#endif
sc_tick
sc_tick_now(void) {
// SDL_GetTicks() resolution is in milliseconds, but sc_tick are expressed
// in microseconds to store PTS without precision loss.
//
// As an alternative, SDL_GetPerformanceCounter() and
// SDL_GetPerformanceFrequency() could be used, but:
// - the conversions (avoiding overflow) are expansive, since the
// frequency is not known at compile time;
// - in practice, we don't need more precision for now.
return (sc_tick) SDL_GetTicks() * 1000;
#ifndef _WIN32
// Maximum sc_tick precision (microsecond)
struct timespec ts;
int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
if (ret) {
abort();
}
return SC_TICK_FROM_SEC(ts.tv_sec) + SC_TICK_FROM_NS(ts.tv_nsec);
#else
LARGE_INTEGER c;
// On systems that run Windows XP or later, the function will always
// succeed and will thus never return zero.
// <https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter>
// <https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancefrequency>
BOOL ok = QueryPerformanceCounter(&c);
assert(ok);
(void) ok;
LONGLONG counter = c.QuadPart;
static LONGLONG frequency;
if (!frequency) {
// Initialize on first call
LARGE_INTEGER f;
ok = QueryPerformanceFrequency(&f);
assert(ok);
frequency = f.QuadPart;
assert(frequency);
}
if (frequency % SC_TICK_FREQ == 0) {
// Expected case (typically frequency = 10000000, i.e. 100ns precision)
sc_tick div = frequency / SC_TICK_FREQ;
return SC_TICK_FROM_US(counter / div);
}
// Split the division to avoid overflow
sc_tick secs = SC_TICK_FROM_SEC(counter / frequency);
sc_tick subsec = SC_TICK_FREQ * (counter % frequency) / frequency;
return secs + subsec;
#endif
}

View file

@ -10,9 +10,11 @@ typedef int64_t sc_tick;
#define SC_TICK_FREQ 1000000 // microsecond
// To be adapted if SC_TICK_FREQ changes
#define SC_TICK_TO_NS(tick) ((tick) * 1000)
#define SC_TICK_TO_US(tick) (tick)
#define SC_TICK_TO_MS(tick) ((tick) / 1000)
#define SC_TICK_TO_SEC(tick) ((tick) / 1000000)
#define SC_TICK_FROM_NS(ns) ((ns) / 1000)
#define SC_TICK_FROM_US(us) (us)
#define SC_TICK_FROM_MS(ms) ((ms) * 1000)
#define SC_TICK_FROM_SEC(sec) ((sec) * 1000000)