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 <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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue