2 * $Logfile: /Freespace2/code/Io/Timer.cpp $
7 * Include file for timer stuff
10 * Revision 1.1 2002/05/03 03:28:09 root
14 * 4 5/26/99 3:19p Dave
15 * Fixed release build errors.
17 * 3 5/17/99 6:03p Dave
18 * Added new timing code. Put in code to allow camera speed zooming.
20 * 2 10/07/98 10:53a Dave
23 * 1 10/07/98 10:49a Dave
25 * 14 4/13/98 10:16a John
26 * Switched gettime back to timer_get_milliseconds, which is now thread
29 * 13 4/13/98 10:11a John
30 * Made timer functions thread safe. Made timer_init be called in all
33 * 12 4/13/98 8:09a John
34 * Made timer_get functions thread safe.
36 * 11 12/02/97 3:56p John
37 * fixed a bug with timestamp_until, created one with it rolling over.
39 * 10 9/11/97 7:12p Hoffoss
40 * Added more functionality to training sexp handling code.
42 * 9 8/29/97 4:49p Allender
43 * added mprintf to check for wrap problems with timestamp ticker
45 * 8 8/28/97 1:38p Allender
46 * from John: changes to timer func to detect early rollever
48 * 7 7/29/97 5:30p Lawrance
49 * move gettime() from keyboard module to timer module
51 * 6 7/16/97 4:42p Mike
52 * Make afterburner shake viewer, not ship.
53 * Shake for limited time.
54 * Add timestamp_until() function to timer library.
56 * 5 2/17/97 5:18p John
57 * Added a bunch of RCS headers to a bunch of old files that don't have
71 #include "alphacolors.h"
77 static longlong Timer_last_value, Timer_base;
78 static uint Timer_freq=0;
80 static int Timer_inited = 0;
82 static CRITICAL_SECTION Timer_lock;
91 DeleteCriticalSection( &Timer_lock );
98 if ( !Timer_inited ) {
103 QueryPerformanceFrequency(&tmp);
104 Assert( tmp.HighPart == 0 );
105 Timer_freq = tmp.LowPart;
107 QueryPerformanceCounter((LARGE_INTEGER *)&Timer_base);
108 QueryPerformanceCounter((LARGE_INTEGER *)&Timer_last_value);
110 InitializeCriticalSection(&Timer_lock);
119 // Fills Time_now with the ticks since program start
120 static void timer_get(LARGE_INTEGER * out)
125 EnterCriticalSection(&Timer_lock);
130 QueryPerformanceCounter((LARGE_INTEGER *)&time_tmp);
131 if ( time_tmp < Timer_last_value ) {
132 // The clock has rolled!
133 Timer_base = time_tmp;
134 mprintf(( "TIMER ROLLED!\n" ));
135 // Hack: I'm not accounting for the time before roll occured,
136 // since I'm not sure at what value this timer is going to roll at.
139 Time_now = time_tmp - Timer_base;
140 Timer_last_value = time_tmp;
142 out->QuadPart = Time_now;
144 LeaveCriticalSection(&Timer_lock);
148 fix timer_get_fixed_seconds()
155 LARGE_INTEGER temp_large;
158 Int3(); // Make sure you call timer_init before anything that uses timer functions!
162 timer_get(&temp_large);
164 // Timing in fixed point (16.16) seconds.
165 // Can be used for up to 1000 hours
166 _asm mov edx, temp_large.HighPart
167 _asm mov eax, temp_large.LowPart
169 _asm shld edx, eax, 16 ; Keep 32+11 bits
171 // edx:eax = number of 1.19Mhz pulses elapsed.
172 _asm mov ebx, Timer_freq
174 // Make sure we won't divide overflow. Make time wrap at about 9 hours
176 _asm sub edx, ebx ; subtract until negative...
177 _asm jns sub_again ; ...to prevent divide overflow...
178 _asm add edx, ebx ; ...then add in to get correct value.
180 //eax = fixed point seconds elapsed...
188 fix timer_get_fixed_secondsX()
190 return timer_get_fixed_seconds();
193 fix timer_get_approx_seconds()
195 return timer_get_fixed_seconds();
198 int timer_get_milliseconds()
205 LARGE_INTEGER temp_large;
208 Int3(); // Make sure you call timer_init before anything that uses timer functions!
212 timer_get(&temp_large);
214 temp_large.QuadPart *= (longlong)1000;
216 // Timing in milliseconds.
217 _asm mov edx, temp_large.HighPart
218 _asm mov eax, temp_large.LowPart
220 //_asm shld edx, eax, 16 ; Keep 32+11 bits
222 // edx:eax = number of 1.19Mhz pulses elapsed.
223 _asm mov ebx, Timer_freq
225 // Make sure we won't divide overflow. Make time wrap at about 9 hours
227 _asm sub edx, ebx ; subtract until negative...
228 _asm jns sub_again ; ...to prevent divide overflow...
229 _asm add edx, ebx ; ...then add in to get correct value.
231 //eax = milliseconds elapsed...
239 int timer_get_microseconds()
246 LARGE_INTEGER temp_large;
249 Int3(); // Make sure you call timer_init before anything that uses timer functions!
253 timer_get(&temp_large);
255 temp_large.QuadPart *= (longlong)1000000;
257 // Timing in milliseconds.
258 _asm mov edx, temp_large.HighPart
259 _asm mov eax, temp_large.LowPart
261 //_asm shld edx, eax, 16 ; Keep 32+11 bits
263 // edx:eax = number of 1.19Mhz pulses elapsed.
264 _asm mov ebx, Timer_freq
266 // Make sure we won't divide overflow. Make time wrap at about 9 hours
268 _asm sub edx, ebx ; subtract until negative...
269 _asm jns sub_again ; ...to prevent divide overflow...
270 _asm add edx, ebx ; ...then add in to get correct value.
272 //eax = milliseconds elapsed...
281 // 1 means always return true
282 // 2 and above actually check the time
283 int timestamp_ticker = 2;
285 void timestamp_reset()
287 timestamp_ticker = 2;
290 // Restrict all time values between 0 and MAX_TIME
291 // so we don't have to use UINTs to calculate rollover.
292 // For debugging & testing, you could set this to
293 // something like 1 minute (6000).
294 #define MAX_TIME (INT_MAX/2)
296 void timestamp_inc(float frametime)
298 timestamp_ticker += (int)(frametime*TIMESTAMP_FREQUENCY);
300 if ( timestamp_ticker > MAX_TIME ) {
301 timestamp_ticker = 2; // Roll!
304 if (timestamp_ticker < 2 ) {
305 mprintf(("Whoa!!! timestamp_ticker < 2 -- resetting to 2!!!\n"));
306 timestamp_ticker = 2;
310 int timestamp(int delta_ms )
313 if (delta_ms < 0 ) return 0;
314 if (delta_ms == 0 ) return 1;
315 t2 = timestamp_ticker + delta_ms;
316 if ( t2 > MAX_TIME ) {
318 t2 = delta_ms - (MAX_TIME-timestamp_ticker);
320 if (t2 < 2 ) t2 = 2; // hack??
324 // Returns milliseconds until timestamp will elapse.
325 // Negative value gives milliseconds ago that timestamp elapsed.
326 int timestamp_until(int stamp)
329 // HACK!! This doesn't handle rollover!
330 // (Will it ever happen?)
332 return stamp - timestamp_ticker;
337 delta = stamp - timestamp_ticker;
340 if (delta > UINT_MAX/2)
341 delta = UINT_MAX - delta + 1;
342 else if (delta < - ( (int) (UINT_MAX/2)))
343 delta = UINT_MAX + delta + 1;
349 // alternate timestamp functions. The way these work is you call xtimestamp() to get the
350 // current counter value, and then call
353 return timestamp_ticker;
356 int timestamp_has_time_elapsed(int stamp, int time)
364 if (t <= timestamp_ticker)
365 return 1; // if we are unlucky enough to have it wrap on us, this will assume time has elapsed.
370 // timing functions -------------------------------------------------------------------------------
372 #define MAX_TIMING_EVENTS 15
377 typedef struct timing {
379 int microseconds_total;
385 timing Timing_events[MAX_TIMING_EVENTS];
386 int Timing_event_count = 0;
390 // lookup a timing event
391 int timing_event_lookup(char *event_name)
399 if(event_name == NULL){
403 // look through all events
404 for(idx=0; idx<MAX_TIMING_EVENTS; idx++){
405 if(!stricmp(Timing_events[idx].name, event_name)){
414 // start timing frame stuff
415 void timing_frame_start()
423 Timing_event_count = 0;
424 Timing_frame.start = timer_get_microseconds();
425 for(idx=0; idx<MAX_TIMING_EVENTS; idx++){
426 Timing_events[idx].microseconds_total = 0;
427 strcpy(Timing_events[idx].name, "");
428 Timing_events[idx].ref_count = 0;
433 // done timing the frame
434 void timing_frame_stop()
442 stop_time = timer_get_microseconds();
445 Timing_frame.microseconds_total = stop_time - Timing_frame.start;
449 // get the total frame time in microseconds
450 int timing_frame_total()
455 return Timing_frame.microseconds_total;
459 // time an individual event
460 void timing_event_start(char *event_name)
468 if(event_name == NULL){
472 // try and find the event
473 event = timing_event_lookup(event_name);
475 // if we already have one
477 Assert(Timing_events[event].ref_count == 0);
478 Timing_events[event].start = timer_get_microseconds();
479 Timing_events[event].ref_count++;
481 // if we need to add a new one
483 if(Timing_event_count < MAX_TIMING_EVENTS){
484 strcpy(Timing_events[Timing_event_count].name, event_name);
485 Timing_events[Timing_event_count].start = timer_get_microseconds();
486 Timing_events[Timing_event_count++].ref_count++;
492 // stop timing an event
493 void timing_event_stop(char *event_name)
501 if(event_name == NULL){
505 // try and find the event
506 event = timing_event_lookup(event_name);
508 // if we already have one
510 Assert(Timing_events[event].ref_count == 1);
511 Timing_events[event].microseconds_total += timer_get_microseconds() - Timing_events[event].start;
512 Timing_events[event].ref_count--;
517 // get the total time for an event in microseconds
518 int timing_event_total(char *event_name)
526 if(event_name == NULL){
530 // try and find the event
531 event = timing_event_lookup(event_name);
536 return Timing_events[event].microseconds_total;
540 // get the percentage of total frametime for the event (0.0 to 1.0)
541 float timing_event_pct(char *event_name)
549 if(event_name == NULL){
553 // try and find the event
554 event = timing_event_lookup(event_name);
559 return (float)((double)Timing_events[event].microseconds_total / (double)Timing_frame.microseconds_total);
564 void timing_display(int x, int y)
571 gr_set_color_fast(&Color_bright_blue);
574 gr_printf(x, y, "Total time %f\n", (float)timing_frame_total() / 1000000.0f);
577 // each event percentage
578 for(idx=0; idx<Timing_event_count; idx++){
579 gr_printf(x, y, "%s : %f\n", Timing_events[idx].name, timing_event_pct(Timing_events[idx].name));