2 * $Logfile: /Freespace2/code/Io/Timer.cpp $
7 * Include file for timer stuff
10 * Revision 1.6 2002/06/05 08:05:29 relnev
11 * stub/warning removal.
13 * reworked the sound code.
15 * Revision 1.5 2002/05/29 21:38:20 relnev
18 * Revision 1.4 2002/05/28 21:36:10 relnev
19 * some more timer junk.
21 * tried to fix software mode.
23 * Revision 1.3 2002/05/28 17:26:57 theoddone33
24 * Fill in some timer and palette setting stubs. Still no display
26 * Revision 1.2 2002/05/07 03:16:46 theoddone33
27 * The Great Newline Fix
29 * Revision 1.1.1.1 2002/05/03 03:28:09 root
33 * 4 5/26/99 3:19p Dave
34 * Fixed release build errors.
36 * 3 5/17/99 6:03p Dave
37 * Added new timing code. Put in code to allow camera speed zooming.
39 * 2 10/07/98 10:53a Dave
42 * 1 10/07/98 10:49a Dave
44 * 14 4/13/98 10:16a John
45 * Switched gettime back to timer_get_milliseconds, which is now thread
48 * 13 4/13/98 10:11a John
49 * Made timer functions thread safe. Made timer_init be called in all
52 * 12 4/13/98 8:09a John
53 * Made timer_get functions thread safe.
55 * 11 12/02/97 3:56p John
56 * fixed a bug with timestamp_until, created one with it rolling over.
58 * 10 9/11/97 7:12p Hoffoss
59 * Added more functionality to training sexp handling code.
61 * 9 8/29/97 4:49p Allender
62 * added mprintf to check for wrap problems with timestamp ticker
64 * 8 8/28/97 1:38p Allender
65 * from John: changes to timer func to detect early rollever
67 * 7 7/29/97 5:30p Lawrance
68 * move gettime() from keyboard module to timer module
70 * 6 7/16/97 4:42p Mike
71 * Make afterburner shake viewer, not ship.
72 * Shake for limited time.
73 * Add timestamp_until() function to timer library.
75 * 5 2/17/97 5:18p John
76 * Added a bunch of RCS headers to a bunch of old files that don't have
90 #include "alphacolors.h"
97 static longlong Timer_last_value, Timer_base;
98 static uint Timer_freq=0;
99 static CRITICAL_SECTION Timer_lock;
102 static int Timer_inited = 0;
106 if ( Timer_inited ) {
111 DeleteCriticalSection( &Timer_lock );
118 if ( !Timer_inited ) {
120 // SDL_InitSubSystem(SDL_INIT_TIMER);
123 QueryPerformanceFrequency(&tmp);
124 Assert( tmp.HighPart == 0 );
125 Timer_freq = tmp.LowPart;
127 QueryPerformanceCounter((LARGE_INTEGER *)&Timer_base);
128 QueryPerformanceCounter((LARGE_INTEGER *)&Timer_last_value);
130 InitializeCriticalSection(&Timer_lock);
140 // Fills Time_now with the ticks since program start
141 static void timer_get(LARGE_INTEGER * out)
143 EnterCriticalSection(&Timer_lock);
148 QueryPerformanceCounter((LARGE_INTEGER *)&time_tmp);
149 if ( time_tmp < Timer_last_value ) {
150 // The clock has rolled!
151 Timer_base = time_tmp;
152 mprintf(( "TIMER ROLLED!\n" ));
153 // Hack: I'm not accounting for the time before roll occured,
154 // since I'm not sure at what value this timer is going to roll at.
157 Time_now = time_tmp - Timer_base;
158 Timer_last_value = time_tmp;
160 out->QuadPart = Time_now;
162 LeaveCriticalSection(&Timer_lock);
166 fix timer_get_fixed_seconds()
169 __extension__ long long a = SDL_GetTicks();
172 return (fix)(a / 1000);
175 LARGE_INTEGER temp_large;
178 Int3(); // Make sure you call timer_init before anything that uses timer functions!
182 timer_get(&temp_large);
184 // Timing in fixed point (16.16) seconds.
185 // Can be used for up to 1000 hours
186 _asm mov edx, temp_large.HighPart
187 _asm mov eax, temp_large.LowPart
189 _asm shld edx, eax, 16 ; Keep 32+11 bits
191 // edx:eax = number of 1.19Mhz pulses elapsed.
192 _asm mov ebx, Timer_freq
194 // Make sure we won't divide overflow. Make time wrap at about 9 hours
196 _asm sub edx, ebx ; subtract until negative...
197 _asm jns sub_again ; ...to prevent divide overflow...
198 _asm add edx, ebx ; ...then add in to get correct value.
200 //eax = fixed point seconds elapsed...
208 fix timer_get_fixed_secondsX()
210 return timer_get_fixed_seconds();
213 fix timer_get_approx_seconds()
215 return timer_get_fixed_seconds();
218 int timer_get_milliseconds()
221 return SDL_GetTicks();
224 LARGE_INTEGER temp_large;
227 Int3(); // Make sure you call timer_init before anything that uses timer functions!
231 timer_get(&temp_large);
233 temp_large.QuadPart *= (longlong)1000;
235 // Timing in milliseconds.
236 _asm mov edx, temp_large.HighPart
237 _asm mov eax, temp_large.LowPart
239 //_asm shld edx, eax, 16 ; Keep 32+11 bits
241 // edx:eax = number of 1.19Mhz pulses elapsed.
242 _asm mov ebx, Timer_freq
244 // Make sure we won't divide overflow. Make time wrap at about 9 hours
246 _asm sub edx, ebx ; subtract until negative...
247 _asm jns sub_again ; ...to prevent divide overflow...
248 _asm add edx, ebx ; ...then add in to get correct value.
250 //eax = milliseconds elapsed...
258 int timer_get_microseconds()
261 return SDL_GetTicks() * 1000;
264 LARGE_INTEGER temp_large;
267 Int3(); // Make sure you call timer_init before anything that uses timer functions!
271 timer_get(&temp_large);
273 temp_large.QuadPart *= (longlong)1000000;
275 // Timing in milliseconds.
276 _asm mov edx, temp_large.HighPart
277 _asm mov eax, temp_large.LowPart
279 //_asm shld edx, eax, 16 ; Keep 32+11 bits
281 // edx:eax = number of 1.19Mhz pulses elapsed.
282 _asm mov ebx, Timer_freq
284 // Make sure we won't divide overflow. Make time wrap at about 9 hours
286 _asm sub edx, ebx ; subtract until negative...
287 _asm jns sub_again ; ...to prevent divide overflow...
288 _asm add edx, ebx ; ...then add in to get correct value.
290 //eax = milliseconds elapsed...
299 // 1 means always return true
300 // 2 and above actually check the time
301 int timestamp_ticker = 2;
303 void timestamp_reset()
305 timestamp_ticker = 2;
308 // Restrict all time values between 0 and MAX_TIME
309 // so we don't have to use UINTs to calculate rollover.
310 // For debugging & testing, you could set this to
311 // something like 1 minute (6000).
312 #define MAX_TIME (INT_MAX/2)
314 void timestamp_inc(float frametime)
316 timestamp_ticker += (int)(frametime*TIMESTAMP_FREQUENCY);
318 if ( timestamp_ticker > MAX_TIME ) {
319 timestamp_ticker = 2; // Roll!
322 if (timestamp_ticker < 2 ) {
323 mprintf(("Whoa!!! timestamp_ticker < 2 -- resetting to 2!!!\n"));
324 timestamp_ticker = 2;
328 int timestamp(int delta_ms )
331 if (delta_ms < 0 ) return 0;
332 if (delta_ms == 0 ) return 1;
333 t2 = timestamp_ticker + delta_ms;
334 if ( t2 > MAX_TIME ) {
336 t2 = delta_ms - (MAX_TIME-timestamp_ticker);
338 if (t2 < 2 ) t2 = 2; // hack??
342 // Returns milliseconds until timestamp will elapse.
343 // Negative value gives milliseconds ago that timestamp elapsed.
344 int timestamp_until(int stamp)
347 // HACK!! This doesn't handle rollover!
348 // (Will it ever happen?)
350 return stamp - timestamp_ticker;
355 delta = stamp - timestamp_ticker;
358 if (delta > UINT_MAX/2)
359 delta = UINT_MAX - delta + 1;
360 else if (delta < - ( (int) (UINT_MAX/2)))
361 delta = UINT_MAX + delta + 1;
367 // alternate timestamp functions. The way these work is you call xtimestamp() to get the
368 // current counter value, and then call
371 return timestamp_ticker;
374 int timestamp_has_time_elapsed(int stamp, int time)
382 if (t <= timestamp_ticker)
383 return 1; // if we are unlucky enough to have it wrap on us, this will assume time has elapsed.
388 // timing functions -------------------------------------------------------------------------------
390 #define MAX_TIMING_EVENTS 15
395 typedef struct timing {
397 int microseconds_total;
403 timing Timing_events[MAX_TIMING_EVENTS];
404 int Timing_event_count = 0;
408 // lookup a timing event
409 int timing_event_lookup(char *event_name)
417 if(event_name == NULL){
421 // look through all events
422 for(idx=0; idx<MAX_TIMING_EVENTS; idx++){
423 if(!stricmp(Timing_events[idx].name, event_name)){
432 // start timing frame stuff
433 void timing_frame_start()
441 Timing_event_count = 0;
442 Timing_frame.start = timer_get_microseconds();
443 for(idx=0; idx<MAX_TIMING_EVENTS; idx++){
444 Timing_events[idx].microseconds_total = 0;
445 strcpy(Timing_events[idx].name, "");
446 Timing_events[idx].ref_count = 0;
451 // done timing the frame
452 void timing_frame_stop()
460 stop_time = timer_get_microseconds();
463 Timing_frame.microseconds_total = stop_time - Timing_frame.start;
467 // get the total frame time in microseconds
468 int timing_frame_total()
473 return Timing_frame.microseconds_total;
477 // time an individual event
478 void timing_event_start(char *event_name)
486 if(event_name == NULL){
490 // try and find the event
491 event = timing_event_lookup(event_name);
493 // if we already have one
495 Assert(Timing_events[event].ref_count == 0);
496 Timing_events[event].start = timer_get_microseconds();
497 Timing_events[event].ref_count++;
499 // if we need to add a new one
501 if(Timing_event_count < MAX_TIMING_EVENTS){
502 strcpy(Timing_events[Timing_event_count].name, event_name);
503 Timing_events[Timing_event_count].start = timer_get_microseconds();
504 Timing_events[Timing_event_count++].ref_count++;
510 // stop timing an event
511 void timing_event_stop(char *event_name)
519 if(event_name == NULL){
523 // try and find the event
524 event = timing_event_lookup(event_name);
526 // if we already have one
528 Assert(Timing_events[event].ref_count == 1);
529 Timing_events[event].microseconds_total += timer_get_microseconds() - Timing_events[event].start;
530 Timing_events[event].ref_count--;
535 // get the total time for an event in microseconds
536 int timing_event_total(char *event_name)
544 if(event_name == NULL){
548 // try and find the event
549 event = timing_event_lookup(event_name);
554 return Timing_events[event].microseconds_total;
558 // get the percentage of total frametime for the event (0.0 to 1.0)
559 float timing_event_pct(char *event_name)
567 if(event_name == NULL){
571 // try and find the event
572 event = timing_event_lookup(event_name);
577 return (float)((double)Timing_events[event].microseconds_total / (double)Timing_frame.microseconds_total);
582 void timing_display(int x, int y)
589 gr_set_color_fast(&Color_bright_blue);
592 gr_printf(x, y, "Total time %f\n", (float)timing_frame_total() / 1000000.0f);
595 // each event percentage
596 for(idx=0; idx<Timing_event_count; idx++){
597 gr_printf(x, y, "%s : %f\n", Timing_events[idx].name, timing_event_pct(Timing_events[idx].name));