Use timers with microsecond precision
SDL only provides millisecond precision. Use system timers to get a better precision.
This commit is contained in:
parent
ddb9396743
commit
682a691173
2 changed files with 51 additions and 10 deletions
|
@ -1,16 +1,55 @@
|
||||||
#include "tick.h"
|
#include "tick.h"
|
||||||
|
|
||||||
#include <SDL2/SDL_timer.h>
|
#include <assert.h>
|
||||||
|
#include <time.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
sc_tick
|
sc_tick
|
||||||
sc_tick_now(void) {
|
sc_tick_now(void) {
|
||||||
// SDL_GetTicks() resolution is in milliseconds, but sc_tick are expressed
|
#ifndef _WIN32
|
||||||
// in microseconds to store PTS without precision loss.
|
// Maximum sc_tick precision (microsecond)
|
||||||
//
|
struct timespec ts;
|
||||||
// As an alternative, SDL_GetPerformanceCounter() and
|
int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
// SDL_GetPerformanceFrequency() could be used, but:
|
if (ret) {
|
||||||
// - the conversions (avoiding overflow) are expansive, since the
|
abort();
|
||||||
// frequency is not known at compile time;
|
}
|
||||||
// - in practice, we don't need more precision for now.
|
|
||||||
return (sc_tick) SDL_GetTicks() * 1000;
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,11 @@ typedef int64_t sc_tick;
|
||||||
#define SC_TICK_FREQ 1000000 // microsecond
|
#define SC_TICK_FREQ 1000000 // microsecond
|
||||||
|
|
||||||
// To be adapted if SC_TICK_FREQ changes
|
// 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_US(tick) (tick)
|
||||||
#define SC_TICK_TO_MS(tick) ((tick) / 1000)
|
#define SC_TICK_TO_MS(tick) ((tick) / 1000)
|
||||||
#define SC_TICK_TO_SEC(tick) ((tick) / 1000000)
|
#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_US(us) (us)
|
||||||
#define SC_TICK_FROM_MS(ms) ((ms) * 1000)
|
#define SC_TICK_FROM_MS(ms) ((ms) * 1000)
|
||||||
#define SC_TICK_FROM_SEC(sec) ((sec) * 1000000)
|
#define SC_TICK_FROM_SEC(sec) ((sec) * 1000000)
|
||||||
|
|
Loading…
Reference in a new issue