2006-04-12 01:59:08 +02:00
|
|
|
/**
|
|
|
|
* =========================================================================
|
|
|
|
* File : timer.cpp
|
|
|
|
* Project : 0 A.D.
|
|
|
|
* Description : platform-independent high resolution timer and
|
|
|
|
* : FPS measuring code.
|
|
|
|
* =========================================================================
|
|
|
|
*/
|
|
|
|
|
2007-05-07 18:33:24 +02:00
|
|
|
// license: GPL; see lib/license.txt
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-05-08 03:11:51 +02:00
|
|
|
#include "precompiled.h"
|
2007-01-01 22:25:47 +01:00
|
|
|
#include "timer.h"
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2006-06-22 20:26:08 +02:00
|
|
|
#include <numeric>
|
|
|
|
#include <math.h>
|
2007-09-03 01:38:58 +02:00
|
|
|
#include <float.h>
|
2006-06-22 20:26:08 +02:00
|
|
|
#include <stdarg.h>
|
|
|
|
|
2007-01-01 22:25:47 +01:00
|
|
|
#include "lib/posix/posix_time.h"
|
2004-06-14 15:29:23 +02:00
|
|
|
#include "adts.h"
|
2007-05-26 19:56:38 +02:00
|
|
|
#include "module_init.h"
|
2006-06-22 20:26:08 +02:00
|
|
|
#include "lib/sysdep/cpu.h"
|
2007-05-26 18:57:39 +02:00
|
|
|
#if OS_WIN
|
|
|
|
#include "lib/sysdep/win/whrt/whrt.h"
|
|
|
|
#endif
|
2007-12-20 21:09:19 +01:00
|
|
|
#if OS_UNIX
|
|
|
|
# include <unistd.h>
|
|
|
|
#endif
|
|
|
|
#if ARCH_IA32 && CONFIG_TIMER_ALLOW_RDTSC
|
2007-05-09 23:01:11 +02:00
|
|
|
# include "lib/sysdep/ia32/ia32.h" // ia32_rdtsc
|
2007-04-30 16:35:19 +02:00
|
|
|
#endif
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2007-12-20 21:09:19 +01:00
|
|
|
#if OS_UNIX || OS_WIN
|
|
|
|
# define HAVE_GETTIMEOFDAY 1
|
|
|
|
#else
|
|
|
|
# define HAVE_GETTIMEOFDAY 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) || OS_WIN
|
|
|
|
# define HAVE_CLOCK_GETTIME 1
|
|
|
|
#else
|
|
|
|
# define HAVE_CLOCK_GETTIME 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// rationale for wrapping gettimeofday and clock_gettime, instead of just
|
|
|
|
// emulating them where not available: allows returning higher-resolution
|
|
|
|
// timer values than their us / ns interface, via double [seconds].
|
|
|
|
// they're also not guaranteed to be monotonic.
|
|
|
|
|
|
|
|
#if HAVE_GETTIMEOFDAY
|
2007-05-26 18:57:39 +02:00
|
|
|
static struct timespec start;
|
2007-12-20 21:09:19 +01:00
|
|
|
#elif HAVE_CLOCK_GETTIME
|
2007-05-26 18:57:39 +02:00
|
|
|
static struct timeval start;
|
|
|
|
#endif
|
|
|
|
|
2007-05-26 19:56:38 +02:00
|
|
|
static void LatchStartTime()
|
2007-05-26 18:57:39 +02:00
|
|
|
{
|
|
|
|
#if HAVE_CLOCK_GETTIME
|
|
|
|
(void)clock_gettime(CLOCK_REALTIME, &start);
|
|
|
|
#elif HAVE_GETTIMEOFDAY
|
|
|
|
gettimeofday(&start, 0);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
double get_time()
|
|
|
|
{
|
|
|
|
double t;
|
|
|
|
|
2007-06-13 01:29:27 +02:00
|
|
|
#if OS_WIN
|
|
|
|
t = whrt_Time();
|
|
|
|
#elif HAVE_CLOCK_GETTIME
|
2007-05-26 18:57:39 +02:00
|
|
|
struct timespec cur;
|
|
|
|
(void)clock_gettime(CLOCK_REALTIME, &cur);
|
|
|
|
t = (cur.tv_sec - start.tv_sec) + (cur.tv_nsec - start.tv_nsec)*1e-9;
|
2005-08-09 18:23:19 +02:00
|
|
|
#elif HAVE_GETTIMEOFDAY
|
2004-03-03 00:56:51 +01:00
|
|
|
struct timeval cur;
|
|
|
|
gettimeofday(&cur, 0);
|
2004-06-02 17:31:55 +02:00
|
|
|
t = (cur.tv_sec - start.tv_sec) + (cur.tv_usec - start.tv_usec)*1e-6;
|
2004-03-03 00:56:51 +01:00
|
|
|
#else
|
2006-07-26 16:04:52 +02:00
|
|
|
# error "get_time: add timer implementation for this platform!"
|
2004-03-03 00:56:51 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// make sure time is monotonic (never goes backwards)
|
2005-01-23 18:48:32 +01:00
|
|
|
static double t_last = 0.0;
|
2004-06-22 18:05:44 +02:00
|
|
|
if(t < t_last)
|
2007-05-16 17:56:47 +02:00
|
|
|
t = t_last+DBL_EPSILON;
|
2004-06-22 18:05:44 +02:00
|
|
|
t_last = t;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-10-18 01:35:16 +02:00
|
|
|
// return resolution (expressed in [s]) of the time source underlying
|
|
|
|
// get_time.
|
2004-03-03 00:56:51 +01:00
|
|
|
double timer_res()
|
|
|
|
{
|
2004-06-24 02:41:29 +02:00
|
|
|
// may take a while to determine, so cache it
|
2005-01-23 18:48:32 +01:00
|
|
|
static double cached_res = 0.0;
|
2004-06-24 02:41:29 +02:00
|
|
|
if(cached_res != 0.0)
|
|
|
|
return cached_res;
|
|
|
|
|
2005-01-23 18:48:32 +01:00
|
|
|
double res = 0.0;
|
2004-06-24 02:41:29 +02:00
|
|
|
|
2005-08-09 18:23:19 +02:00
|
|
|
#if HAVE_CLOCK_GETTIME
|
2004-06-24 02:41:29 +02:00
|
|
|
struct timespec ts;
|
2005-01-23 18:48:32 +01:00
|
|
|
if(clock_getres(CLOCK_REALTIME, &ts) == 0)
|
|
|
|
res = ts.tv_nsec * 1e-9;
|
2007-05-26 18:57:39 +02:00
|
|
|
#elif OS_WIN
|
|
|
|
res = whrt_Resolution();
|
2004-03-03 00:56:51 +01:00
|
|
|
#else
|
2004-06-24 02:41:29 +02:00
|
|
|
const double t0 = get_time();
|
|
|
|
double t1, t2;
|
2004-06-22 18:05:44 +02:00
|
|
|
do t1 = get_time(); while(t1 == t0);
|
|
|
|
do t2 = get_time(); while(t2 == t1);
|
2004-06-24 02:41:29 +02:00
|
|
|
res = t2-t1;
|
2004-03-03 00:56:51 +01:00
|
|
|
#endif
|
2004-06-24 02:41:29 +02:00
|
|
|
|
|
|
|
cached_res = res;
|
|
|
|
return res;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-10-10 19:33:34 +02:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// cumulative timer API, useful for profiling.
|
|
|
|
// this supplements in-game profiling by providing low-overhead,
|
|
|
|
// high resolution time accounting.
|
|
|
|
|
2005-10-18 01:35:16 +02:00
|
|
|
// intrusive linked-list of all clients. a fixed-size limit would be
|
|
|
|
// acceptable (since timers are added manually), but the list is easy
|
|
|
|
// to implement and only has the drawback of exposing TimerClient to users.
|
|
|
|
//
|
|
|
|
// do not use std::list et al. for this! we must be callable at any time,
|
|
|
|
// especially before NLSO ctors run or before heap init.
|
2005-10-10 19:33:34 +02:00
|
|
|
static uint num_clients;
|
2005-10-17 01:16:08 +02:00
|
|
|
static TimerClient* clients;
|
2005-10-10 19:33:34 +02:00
|
|
|
|
|
|
|
|
2005-10-18 01:35:16 +02:00
|
|
|
// make the given TimerClient (usually instantiated as static data)
|
|
|
|
// ready for use. returns its address for TIMER_ADD_CLIENT's convenience.
|
|
|
|
// this client's total (added to by timer_bill_client) will be
|
|
|
|
// displayed by timer_display_client_totals.
|
2005-10-10 19:33:34 +02:00
|
|
|
// notes:
|
|
|
|
// - may be called at any time;
|
2005-10-17 01:16:08 +02:00
|
|
|
// - always succeeds (there's no fixed limit);
|
2005-10-10 19:33:34 +02:00
|
|
|
// - free() is not needed nor possible.
|
2005-10-17 01:16:08 +02:00
|
|
|
// - description must remain valid until exit; a string literal is safest.
|
|
|
|
TimerClient* timer_add_client(TimerClient* tc, const char* description)
|
2005-10-10 19:33:34 +02:00
|
|
|
{
|
2005-10-17 01:16:08 +02:00
|
|
|
tc->sum = 0.0;
|
|
|
|
tc->description = description;
|
|
|
|
|
|
|
|
// insert at front of list
|
|
|
|
tc->next = clients;
|
|
|
|
clients = tc;
|
|
|
|
num_clients++;
|
|
|
|
|
2005-10-10 19:33:34 +02:00
|
|
|
return tc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-12-14 00:17:50 +01:00
|
|
|
// add <dt> to the client's total.
|
|
|
|
void timer_bill_client(TimerClient* tc, TimerUnit dt)
|
2005-10-10 19:33:34 +02:00
|
|
|
{
|
|
|
|
tc->sum += dt;
|
2005-10-19 22:26:53 +02:00
|
|
|
tc->num_calls++;
|
2005-10-10 19:33:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// display all clients' totals; does not reset them.
|
|
|
|
// typically called at exit.
|
|
|
|
void timer_display_client_totals()
|
|
|
|
{
|
|
|
|
debug_printf("TIMER TOTALS (%d clients)\n", num_clients);
|
|
|
|
debug_printf("-----------------------------------------------------\n");
|
2005-10-17 01:16:08 +02:00
|
|
|
|
|
|
|
while(clients)
|
2005-10-10 19:33:34 +02:00
|
|
|
{
|
2005-10-17 01:16:08 +02:00
|
|
|
// (make sure list and count are consistent)
|
|
|
|
debug_assert(num_clients != 0);
|
|
|
|
TimerClient* tc = clients;
|
|
|
|
clients = tc->next;
|
|
|
|
num_clients--;
|
|
|
|
|
2007-12-20 21:09:19 +01:00
|
|
|
const double sum = Timer::ToSeconds(tc->sum);
|
2005-12-14 00:17:50 +01:00
|
|
|
|
2005-10-10 19:33:34 +02:00
|
|
|
// determine scale factor for pretty display
|
|
|
|
double scale = 1e6;
|
|
|
|
const char* unit = "us";
|
|
|
|
if(sum > 1.0)
|
|
|
|
scale = 1, unit = "s";
|
|
|
|
else if(sum > 1e-3)
|
|
|
|
scale = 1e3, unit = "ms";
|
|
|
|
|
2005-10-19 22:26:53 +02:00
|
|
|
debug_printf(" %s: %g %s (%dx)\n", tc->description, sum*scale, unit, tc->num_calls);
|
2005-10-10 19:33:34 +02:00
|
|
|
}
|
2005-10-17 01:16:08 +02:00
|
|
|
|
2005-10-10 19:33:34 +02:00
|
|
|
debug_printf("-----------------------------------------------------\n");
|
|
|
|
}
|
2007-04-30 16:35:19 +02:00
|
|
|
|
|
|
|
|
2007-12-20 21:09:19 +01:00
|
|
|
#if ARCH_IA32 && CONFIG_TIMER_ALLOW_RDTSC
|
2007-04-30 16:35:19 +02:00
|
|
|
|
|
|
|
TimerRdtsc::unit TimerRdtsc::get_timestamp() const
|
|
|
|
{
|
|
|
|
return ia32_rdtsc();
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
2007-05-26 19:56:38 +02:00
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
static ModuleInitState initState;
|
|
|
|
|
|
|
|
void timer_Init()
|
|
|
|
{
|
|
|
|
if(!ModuleShouldInitialize(&initState))
|
|
|
|
return;
|
|
|
|
|
|
|
|
LatchStartTime();
|
|
|
|
}
|
|
|
|
|
|
|
|
void timer_Shutdown()
|
|
|
|
{
|
|
|
|
if(!ModuleShouldShutdown(&initState))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// nothing to do
|
|
|
|
}
|