Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/lib/timer.cpp
/* Copyright (C) 2019 Wildfire Games. | /* Copyright (C) 2020 Wildfire Games. | ||||
* | * | ||||
* Permission is hereby granted, free of charge, to any person obtaining | * Permission is hereby granted, free of charge, to any person obtaining | ||||
* a copy of this software and associated documentation files (the | * a copy of this software and associated documentation files (the | ||||
* "Software"), to deal in the Software without restriction, including | * "Software"), to deal in the Software without restriction, including | ||||
* without limitation the rights to use, copy, modify, merge, publish, | * without limitation the rights to use, copy, modify, merge, publish, | ||||
* distribute, sublicense, and/or sell copies of the Software, and to | * distribute, sublicense, and/or sell copies of the Software, and to | ||||
* permit persons to whom the Software is furnished to do so, subject to | * permit persons to whom the Software is furnished to do so, subject to | ||||
* the following conditions: | * the following conditions: | ||||
Show All 24 Lines | |||||
#include <numeric> | #include <numeric> | ||||
#include <sstream> // std::stringstream | #include <sstream> // std::stringstream | ||||
#include "lib/module_init.h" | #include "lib/module_init.h" | ||||
#include "lib/posix/posix_time.h" | #include "lib/posix/posix_time.h" | ||||
#include "lib/sysdep/cpu.h" | #include "lib/sysdep/cpu.h" | ||||
#if OS_WIN | #if OS_WIN | ||||
# include "lib/sysdep/os/win/whrt/whrt.h" | #include "lib/sysdep/os/win/win.h" | ||||
#endif | #endif | ||||
#if OS_UNIX | #if OS_UNIX | ||||
# include <unistd.h> | # include <unistd.h> | ||||
#endif | #endif | ||||
#if OS_UNIX || OS_WIN | #if OS_UNIX || OS_WIN | ||||
# define HAVE_GETTIMEOFDAY 1 | # define HAVE_GETTIMEOFDAY 1 | ||||
#else | #else | ||||
# define HAVE_GETTIMEOFDAY 0 | # define HAVE_GETTIMEOFDAY 0 | ||||
#endif | #endif | ||||
#if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) || OS_WIN | #if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) | ||||
# define HAVE_CLOCK_GETTIME 1 | # define HAVE_CLOCK_GETTIME 1 | ||||
#else | #else | ||||
# define HAVE_CLOCK_GETTIME 0 | # define HAVE_CLOCK_GETTIME 0 | ||||
#endif | #endif | ||||
// rationale for wrapping gettimeofday and clock_gettime, instead of just | // rationale for wrapping gettimeofday and clock_gettime, instead of just | ||||
// emulating them where not available: allows returning higher-resolution | // emulating them where not available: allows returning higher-resolution | ||||
// timer values than their us / ns interface, via double [seconds]. | // timer values than their us / ns interface, via double [seconds]. | ||||
// they're also not guaranteed to be monotonic. | // they're also not guaranteed to be monotonic. | ||||
#if HAVE_CLOCK_GETTIME | #if OS_WIN | ||||
static LARGE_INTEGER start; | |||||
#elif HAVE_CLOCK_GETTIME | |||||
static struct timespec start; | static struct timespec start; | ||||
#elif HAVE_GETTIMEOFDAY | #elif HAVE_GETTIMEOFDAY | ||||
static struct timeval start; | static struct timeval start; | ||||
#endif | #endif | ||||
//----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||||
// timer API | // timer API | ||||
void timer_LatchStartTime() | void timer_LatchStartTime() | ||||
{ | { | ||||
#if OS_WIN | #if OS_WIN | ||||
// whrt_Time starts at zero, nothing needs to be done. | ENSURE(QueryPerformanceCounter(&start)); | ||||
#elif HAVE_CLOCK_GETTIME | #elif HAVE_CLOCK_GETTIME | ||||
(void)clock_gettime(CLOCK_REALTIME, &start); | (void)clock_gettime(CLOCK_REALTIME, &start); | ||||
#elif HAVE_GETTIMEOFDAY | #elif HAVE_GETTIMEOFDAY | ||||
gettimeofday(&start, 0); | gettimeofday(&start, 0); | ||||
#endif | #endif | ||||
} | } | ||||
static std::mutex ensure_monotonic_mutex; | static std::mutex ensure_monotonic_mutex; | ||||
// NB: does not guarantee strict monotonicity - callers must avoid | // NB: does not guarantee strict monotonicity - callers must avoid | ||||
// dividing by the difference of two equal times. | // dividing by the difference of two equal times. | ||||
static void EnsureMonotonic(double& newTime) | static void EnsureMonotonic(double& newTime) | ||||
{ | { | ||||
std::lock_guard<std::mutex> lock(ensure_monotonic_mutex); | std::lock_guard<std::mutex> lock(ensure_monotonic_mutex); | ||||
static double maxTime; | static double maxTime; | ||||
maxTime = std::max(maxTime, newTime); | maxTime = std::max(maxTime, newTime); | ||||
newTime = maxTime; | newTime = maxTime; | ||||
} | } | ||||
// Cached because the default implementation may take several milliseconds. | |||||
static double resolution; | |||||
double timer_Time() | double timer_Time() | ||||
{ | { | ||||
double t; | double t; | ||||
#if OS_WIN | #if OS_WIN | ||||
t = whrt_Time(); | LARGE_INTEGER now; | ||||
ENSURE(QueryPerformanceCounter(&now)); | |||||
t = static_cast<double>(now.QuadPart - start.QuadPart) * resolution; | |||||
#elif HAVE_CLOCK_GETTIME | #elif HAVE_CLOCK_GETTIME | ||||
ENSURE(start.tv_sec || start.tv_nsec); // must have called timer_LatchStartTime first | ENSURE(start.tv_sec || start.tv_nsec); // must have called timer_LatchStartTime first | ||||
struct timespec cur; | struct timespec cur; | ||||
(void)clock_gettime(CLOCK_REALTIME, &cur); | (void)clock_gettime(CLOCK_REALTIME, &cur); | ||||
t = (cur.tv_sec - start.tv_sec) + (cur.tv_nsec - start.tv_nsec)*1e-9; | t = (cur.tv_sec - start.tv_sec) + (cur.tv_nsec - start.tv_nsec)*1e-9; | ||||
#elif HAVE_GETTIMEOFDAY | #elif HAVE_GETTIMEOFDAY | ||||
ENSURE(start.tv_sec || start.tv_usec); // must have called timer_LatchStartTime first | ENSURE(start.tv_sec || start.tv_usec); // must have called timer_LatchStartTime first | ||||
struct timeval cur; | struct timeval cur; | ||||
gettimeofday(&cur, 0); | gettimeofday(&cur, 0); | ||||
t = (cur.tv_sec - start.tv_sec) + (cur.tv_usec - start.tv_usec)*1e-6; | t = (cur.tv_sec - start.tv_sec) + (cur.tv_usec - start.tv_usec)*1e-6; | ||||
#else | #else | ||||
# error "timer_Time: add timer implementation for this platform!" | # error "timer_Time: add timer implementation for this platform!" | ||||
#endif | #endif | ||||
EnsureMonotonic(t); | EnsureMonotonic(t); | ||||
return t; | return t; | ||||
} | } | ||||
// cached because the default implementation may take several milliseconds | |||||
static double resolution; | |||||
static Status InitResolution() | static Status InitResolution() | ||||
{ | { | ||||
#if OS_WIN | #if OS_WIN | ||||
resolution = whrt_Resolution(); | LARGE_INTEGER frequency; | ||||
ENSURE(QueryPerformanceFrequency(&frequency)); | |||||
resolution = 1.0 / static_cast<double>(frequency.QuadPart); | |||||
#elif HAVE_CLOCK_GETTIME | #elif HAVE_CLOCK_GETTIME | ||||
struct timespec ts; | struct timespec ts; | ||||
if(clock_getres(CLOCK_REALTIME, &ts) == 0) | if(clock_getres(CLOCK_REALTIME, &ts) == 0) | ||||
resolution = ts.tv_nsec * 1e-9; | resolution = ts.tv_nsec * 1e-9; | ||||
#else | #else | ||||
const double t0 = timer_Time(); | const double t0 = timer_Time(); | ||||
double t1, t2; | double t1, t2; | ||||
do t1 = timer_Time(); while(t1 == t0); | do t1 = timer_Time(); while(t1 == t0); | ||||
▲ Show 20 Lines • Show All 92 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator