2 * $Logfile: /Freespace2/code/Io/Timer.cpp $
7 * Include file for timer stuff
10 * Revision 1.3 2002/05/28 17:26:57 theoddone33
11 * Fill in some timer and palette setting stubs. Still no display
13 * Revision 1.2 2002/05/07 03:16:46 theoddone33
14 * The Great Newline Fix
16 * Revision 1.1.1.1 2002/05/03 03:28:09 root
20 * 4 5/26/99 3:19p Dave
21 * Fixed release build errors.
23 * 3 5/17/99 6:03p Dave
24 * Added new timing code. Put in code to allow camera speed zooming.
26 * 2 10/07/98 10:53a Dave
29 * 1 10/07/98 10:49a Dave
31 * 14 4/13/98 10:16a John
32 * Switched gettime back to timer_get_milliseconds, which is now thread
35 * 13 4/13/98 10:11a John
36 * Made timer functions thread safe. Made timer_init be called in all
39 * 12 4/13/98 8:09a John
40 * Made timer_get functions thread safe.
42 * 11 12/02/97 3:56p John
43 * fixed a bug with timestamp_until, created one with it rolling over.
45 * 10 9/11/97 7:12p Hoffoss
46 * Added more functionality to training sexp handling code.
48 * 9 8/29/97 4:49p Allender
49 * added mprintf to check for wrap problems with timestamp ticker
51 * 8 8/28/97 1:38p Allender
52 * from John: changes to timer func to detect early rollever
54 * 7 7/29/97 5:30p Lawrance
55 * move gettime() from keyboard module to timer module
57 * 6 7/16/97 4:42p Mike
58 * Make afterburner shake viewer, not ship.
59 * Shake for limited time.
60 * Add timestamp_until() function to timer library.
62 * 5 2/17/97 5:18p John
63 * Added a bunch of RCS headers to a bunch of old files that don't have
77 #include "alphacolors.h"
83 static longlong Timer_last_value, Timer_base;
84 static uint Timer_freq=0;
86 static int Timer_inited = 0;
88 static CRITICAL_SECTION Timer_lock;
97 DeleteCriticalSection( &Timer_lock );
104 if ( !Timer_inited ) {
106 SDL_InitSubSystem(SDL_INIT_TIMER);
109 QueryPerformanceFrequency(&tmp);
110 Assert( tmp.HighPart == 0 );
111 Timer_freq = tmp.LowPart;
113 QueryPerformanceCounter((LARGE_INTEGER *)&Timer_base);
114 QueryPerformanceCounter((LARGE_INTEGER *)&Timer_last_value);
116 InitializeCriticalSection(&Timer_lock);
125 // Fills Time_now with the ticks since program start
126 static void timer_get(LARGE_INTEGER * out)
131 EnterCriticalSection(&Timer_lock);
136 QueryPerformanceCounter((LARGE_INTEGER *)&time_tmp);
137 if ( time_tmp < Timer_last_value ) {
138 // The clock has rolled!
139 Timer_base = time_tmp;
140 mprintf(( "TIMER ROLLED!\n" ));
141 // Hack: I'm not accounting for the time before roll occured,
142 // since I'm not sure at what value this timer is going to roll at.
145 Time_now = time_tmp - Timer_base;
146 Timer_last_value = time_tmp;
148 out->QuadPart = Time_now;
150 LeaveCriticalSection(&Timer_lock);
154 fix timer_get_fixed_seconds()
161 LARGE_INTEGER temp_large;
164 Int3(); // Make sure you call timer_init before anything that uses timer functions!
168 timer_get(&temp_large);
170 // Timing in fixed point (16.16) seconds.
171 // Can be used for up to 1000 hours
172 _asm mov edx, temp_large.HighPart
173 _asm mov eax, temp_large.LowPart
175 _asm shld edx, eax, 16 ; Keep 32+11 bits
177 // edx:eax = number of 1.19Mhz pulses elapsed.
178 _asm mov ebx, Timer_freq
180 // Make sure we won't divide overflow. Make time wrap at about 9 hours
182 _asm sub edx, ebx ; subtract until negative...
183 _asm jns sub_again ; ...to prevent divide overflow...
184 _asm add edx, ebx ; ...then add in to get correct value.
186 //eax = fixed point seconds elapsed...
194 fix timer_get_fixed_secondsX()
196 return timer_get_fixed_seconds();
199 fix timer_get_approx_seconds()
201 return timer_get_fixed_seconds();
204 int timer_get_milliseconds()
207 return SDL_GetTicks();
210 LARGE_INTEGER temp_large;
213 Int3(); // Make sure you call timer_init before anything that uses timer functions!
217 timer_get(&temp_large);
219 temp_large.QuadPart *= (longlong)1000;
221 // Timing in milliseconds.
222 _asm mov edx, temp_large.HighPart
223 _asm mov eax, temp_large.LowPart
225 //_asm shld edx, eax, 16 ; Keep 32+11 bits
227 // edx:eax = number of 1.19Mhz pulses elapsed.
228 _asm mov ebx, Timer_freq
230 // Make sure we won't divide overflow. Make time wrap at about 9 hours
232 _asm sub edx, ebx ; subtract until negative...
233 _asm jns sub_again ; ...to prevent divide overflow...
234 _asm add edx, ebx ; ...then add in to get correct value.
236 //eax = milliseconds elapsed...
244 int timer_get_microseconds()
251 LARGE_INTEGER temp_large;
254 Int3(); // Make sure you call timer_init before anything that uses timer functions!
258 timer_get(&temp_large);
260 temp_large.QuadPart *= (longlong)1000000;
262 // Timing in milliseconds.
263 _asm mov edx, temp_large.HighPart
264 _asm mov eax, temp_large.LowPart
266 //_asm shld edx, eax, 16 ; Keep 32+11 bits
268 // edx:eax = number of 1.19Mhz pulses elapsed.
269 _asm mov ebx, Timer_freq
271 // Make sure we won't divide overflow. Make time wrap at about 9 hours
273 _asm sub edx, ebx ; subtract until negative...
274 _asm jns sub_again ; ...to prevent divide overflow...
275 _asm add edx, ebx ; ...then add in to get correct value.
277 //eax = milliseconds elapsed...
286 // 1 means always return true
287 // 2 and above actually check the time
288 int timestamp_ticker = 2;
290 void timestamp_reset()
292 timestamp_ticker = 2;
295 // Restrict all time values between 0 and MAX_TIME
296 // so we don't have to use UINTs to calculate rollover.
297 // For debugging & testing, you could set this to
298 // something like 1 minute (6000).
299 #define MAX_TIME (INT_MAX/2)
301 void timestamp_inc(float frametime)
303 timestamp_ticker += (int)(frametime*TIMESTAMP_FREQUENCY);
305 if ( timestamp_ticker > MAX_TIME ) {
306 timestamp_ticker = 2; // Roll!
309 if (timestamp_ticker < 2 ) {
310 mprintf(("Whoa!!! timestamp_ticker < 2 -- resetting to 2!!!\n"));
311 timestamp_ticker = 2;
315 int timestamp(int delta_ms )
318 if (delta_ms < 0 ) return 0;
319 if (delta_ms == 0 ) return 1;
320 t2 = timestamp_ticker + delta_ms;
321 if ( t2 > MAX_TIME ) {
323 t2 = delta_ms - (MAX_TIME-timestamp_ticker);
325 if (t2 < 2 ) t2 = 2; // hack??
329 // Returns milliseconds until timestamp will elapse.
330 // Negative value gives milliseconds ago that timestamp elapsed.
331 int timestamp_until(int stamp)
334 // HACK!! This doesn't handle rollover!
335 // (Will it ever happen?)
337 return stamp - timestamp_ticker;
342 delta = stamp - timestamp_ticker;
345 if (delta > UINT_MAX/2)
346 delta = UINT_MAX - delta + 1;
347 else if (delta < - ( (int) (UINT_MAX/2)))
348 delta = UINT_MAX + delta + 1;
354 // alternate timestamp functions. The way these work is you call xtimestamp() to get the
355 // current counter value, and then call
358 return timestamp_ticker;
361 int timestamp_has_time_elapsed(int stamp, int time)
369 if (t <= timestamp_ticker)
370 return 1; // if we are unlucky enough to have it wrap on us, this will assume time has elapsed.
375 // timing functions -------------------------------------------------------------------------------
377 #define MAX_TIMING_EVENTS 15
382 typedef struct timing {
384 int microseconds_total;
390 timing Timing_events[MAX_TIMING_EVENTS];
391 int Timing_event_count = 0;
395 // lookup a timing event
396 int timing_event_lookup(char *event_name)
404 if(event_name == NULL){
408 // look through all events
409 for(idx=0; idx<MAX_TIMING_EVENTS; idx++){
410 if(!stricmp(Timing_events[idx].name, event_name)){
419 // start timing frame stuff
420 void timing_frame_start()
428 Timing_event_count = 0;
429 Timing_frame.start = timer_get_microseconds();
430 for(idx=0; idx<MAX_TIMING_EVENTS; idx++){
431 Timing_events[idx].microseconds_total = 0;
432 strcpy(Timing_events[idx].name, "");
433 Timing_events[idx].ref_count = 0;
438 // done timing the frame
439 void timing_frame_stop()
447 stop_time = timer_get_microseconds();
450 Timing_frame.microseconds_total = stop_time - Timing_frame.start;
454 // get the total frame time in microseconds
455 int timing_frame_total()
460 return Timing_frame.microseconds_total;
464 // time an individual event
465 void timing_event_start(char *event_name)
473 if(event_name == NULL){
477 // try and find the event
478 event = timing_event_lookup(event_name);
480 // if we already have one
482 Assert(Timing_events[event].ref_count == 0);
483 Timing_events[event].start = timer_get_microseconds();
484 Timing_events[event].ref_count++;
486 // if we need to add a new one
488 if(Timing_event_count < MAX_TIMING_EVENTS){
489 strcpy(Timing_events[Timing_event_count].name, event_name);
490 Timing_events[Timing_event_count].start = timer_get_microseconds();
491 Timing_events[Timing_event_count++].ref_count++;
497 // stop timing an event
498 void timing_event_stop(char *event_name)
506 if(event_name == NULL){
510 // try and find the event
511 event = timing_event_lookup(event_name);
513 // if we already have one
515 Assert(Timing_events[event].ref_count == 1);
516 Timing_events[event].microseconds_total += timer_get_microseconds() - Timing_events[event].start;
517 Timing_events[event].ref_count--;
522 // get the total time for an event in microseconds
523 int timing_event_total(char *event_name)
531 if(event_name == NULL){
535 // try and find the event
536 event = timing_event_lookup(event_name);
541 return Timing_events[event].microseconds_total;
545 // get the percentage of total frametime for the event (0.0 to 1.0)
546 float timing_event_pct(char *event_name)
554 if(event_name == NULL){
558 // try and find the event
559 event = timing_event_lookup(event_name);
564 return (float)((double)Timing_events[event].microseconds_total / (double)Timing_frame.microseconds_total);
569 void timing_display(int x, int y)
576 gr_set_color_fast(&Color_bright_blue);
579 gr_printf(x, y, "Total time %f\n", (float)timing_frame_total() / 1000000.0f);
582 // each event percentage
583 for(idx=0; idx<Timing_event_count; idx++){
584 gr_printf(x, y, "%s : %f\n", Timing_events[idx].name, timing_event_pct(Timing_events[idx].name));