2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Io/Timer.cpp $
15 * Include file for timer stuff
18 * Revision 1.7 2002/06/09 04:41:22 relnev
19 * added copyright header
21 * Revision 1.6 2002/06/05 08:05:29 relnev
22 * stub/warning removal.
24 * reworked the sound code.
26 * Revision 1.5 2002/05/29 21:38:20 relnev
29 * Revision 1.4 2002/05/28 21:36:10 relnev
30 * some more timer junk.
32 * tried to fix software mode.
34 * Revision 1.3 2002/05/28 17:26:57 theoddone33
35 * Fill in some timer and palette setting stubs. Still no display
37 * Revision 1.2 2002/05/07 03:16:46 theoddone33
38 * The Great Newline Fix
40 * Revision 1.1.1.1 2002/05/03 03:28:09 root
44 * 4 5/26/99 3:19p Dave
45 * Fixed release build errors.
47 * 3 5/17/99 6:03p Dave
48 * Added new timing code. Put in code to allow camera speed zooming.
50 * 2 10/07/98 10:53a Dave
53 * 1 10/07/98 10:49a Dave
55 * 14 4/13/98 10:16a John
56 * Switched gettime back to timer_get_milliseconds, which is now thread
59 * 13 4/13/98 10:11a John
60 * Made timer functions thread safe. Made timer_init be called in all
63 * 12 4/13/98 8:09a John
64 * Made timer_get functions thread safe.
66 * 11 12/02/97 3:56p John
67 * fixed a bug with timestamp_until, created one with it rolling over.
69 * 10 9/11/97 7:12p Hoffoss
70 * Added more functionality to training sexp handling code.
72 * 9 8/29/97 4:49p Allender
73 * added mprintf to check for wrap problems with timestamp ticker
75 * 8 8/28/97 1:38p Allender
76 * from John: changes to timer func to detect early rollever
78 * 7 7/29/97 5:30p Lawrance
79 * move gettime() from keyboard module to timer module
81 * 6 7/16/97 4:42p Mike
82 * Make afterburner shake viewer, not ship.
83 * Shake for limited time.
84 * Add timestamp_until() function to timer library.
86 * 5 2/17/97 5:18p John
87 * Added a bunch of RCS headers to a bunch of old files that don't have
101 #include "alphacolors.h"
108 static longlong Timer_last_value, Timer_base;
109 static uint Timer_freq=0;
110 static CRITICAL_SECTION Timer_lock;
113 static int Timer_inited = 0;
117 if ( Timer_inited ) {
122 DeleteCriticalSection( &Timer_lock );
129 if ( !Timer_inited ) {
131 // SDL_InitSubSystem(SDL_INIT_TIMER);
134 QueryPerformanceFrequency(&tmp);
135 Assert( tmp.HighPart == 0 );
136 Timer_freq = tmp.LowPart;
138 QueryPerformanceCounter((LARGE_INTEGER *)&Timer_base);
139 QueryPerformanceCounter((LARGE_INTEGER *)&Timer_last_value);
141 InitializeCriticalSection(&Timer_lock);
151 // Fills Time_now with the ticks since program start
152 static void timer_get(LARGE_INTEGER * out)
154 EnterCriticalSection(&Timer_lock);
159 QueryPerformanceCounter((LARGE_INTEGER *)&time_tmp);
160 if ( time_tmp < Timer_last_value ) {
161 // The clock has rolled!
162 Timer_base = time_tmp;
163 mprintf(( "TIMER ROLLED!\n" ));
164 // Hack: I'm not accounting for the time before roll occured,
165 // since I'm not sure at what value this timer is going to roll at.
168 Time_now = time_tmp - Timer_base;
169 Timer_last_value = time_tmp;
171 out->QuadPart = Time_now;
173 LeaveCriticalSection(&Timer_lock);
177 fix timer_get_fixed_seconds()
180 __extension__ long long a = SDL_GetTicks();
183 return (fix)(a / 1000);
186 LARGE_INTEGER temp_large;
189 Int3(); // Make sure you call timer_init before anything that uses timer functions!
193 timer_get(&temp_large);
195 // Timing in fixed point (16.16) seconds.
196 // Can be used for up to 1000 hours
197 _asm mov edx, temp_large.HighPart
198 _asm mov eax, temp_large.LowPart
200 _asm shld edx, eax, 16 ; Keep 32+11 bits
202 // edx:eax = number of 1.19Mhz pulses elapsed.
203 _asm mov ebx, Timer_freq
205 // Make sure we won't divide overflow. Make time wrap at about 9 hours
207 _asm sub edx, ebx ; subtract until negative...
208 _asm jns sub_again ; ...to prevent divide overflow...
209 _asm add edx, ebx ; ...then add in to get correct value.
211 //eax = fixed point seconds elapsed...
219 fix timer_get_fixed_secondsX()
221 return timer_get_fixed_seconds();
224 fix timer_get_approx_seconds()
226 return timer_get_fixed_seconds();
229 int timer_get_milliseconds()
232 return SDL_GetTicks();
235 LARGE_INTEGER temp_large;
238 Int3(); // Make sure you call timer_init before anything that uses timer functions!
242 timer_get(&temp_large);
244 temp_large.QuadPart *= (longlong)1000;
246 // Timing in milliseconds.
247 _asm mov edx, temp_large.HighPart
248 _asm mov eax, temp_large.LowPart
250 //_asm shld edx, eax, 16 ; Keep 32+11 bits
252 // edx:eax = number of 1.19Mhz pulses elapsed.
253 _asm mov ebx, Timer_freq
255 // Make sure we won't divide overflow. Make time wrap at about 9 hours
257 _asm sub edx, ebx ; subtract until negative...
258 _asm jns sub_again ; ...to prevent divide overflow...
259 _asm add edx, ebx ; ...then add in to get correct value.
261 //eax = milliseconds elapsed...
269 int timer_get_microseconds()
272 return SDL_GetTicks() * 1000;
275 LARGE_INTEGER temp_large;
278 Int3(); // Make sure you call timer_init before anything that uses timer functions!
282 timer_get(&temp_large);
284 temp_large.QuadPart *= (longlong)1000000;
286 // Timing in milliseconds.
287 _asm mov edx, temp_large.HighPart
288 _asm mov eax, temp_large.LowPart
290 //_asm shld edx, eax, 16 ; Keep 32+11 bits
292 // edx:eax = number of 1.19Mhz pulses elapsed.
293 _asm mov ebx, Timer_freq
295 // Make sure we won't divide overflow. Make time wrap at about 9 hours
297 _asm sub edx, ebx ; subtract until negative...
298 _asm jns sub_again ; ...to prevent divide overflow...
299 _asm add edx, ebx ; ...then add in to get correct value.
301 //eax = milliseconds elapsed...
310 // 1 means always return true
311 // 2 and above actually check the time
312 int timestamp_ticker = 2;
314 void timestamp_reset()
316 timestamp_ticker = 2;
319 // Restrict all time values between 0 and MAX_TIME
320 // so we don't have to use UINTs to calculate rollover.
321 // For debugging & testing, you could set this to
322 // something like 1 minute (6000).
323 #define MAX_TIME (INT_MAX/2)
325 void timestamp_inc(float frametime)
327 timestamp_ticker += (int)(frametime*TIMESTAMP_FREQUENCY);
329 if ( timestamp_ticker > MAX_TIME ) {
330 timestamp_ticker = 2; // Roll!
333 if (timestamp_ticker < 2 ) {
334 mprintf(("Whoa!!! timestamp_ticker < 2 -- resetting to 2!!!\n"));
335 timestamp_ticker = 2;
339 int timestamp(int delta_ms )
342 if (delta_ms < 0 ) return 0;
343 if (delta_ms == 0 ) return 1;
344 t2 = timestamp_ticker + delta_ms;
345 if ( t2 > MAX_TIME ) {
347 t2 = delta_ms - (MAX_TIME-timestamp_ticker);
349 if (t2 < 2 ) t2 = 2; // hack??
353 // Returns milliseconds until timestamp will elapse.
354 // Negative value gives milliseconds ago that timestamp elapsed.
355 int timestamp_until(int stamp)
358 // HACK!! This doesn't handle rollover!
359 // (Will it ever happen?)
361 return stamp - timestamp_ticker;
366 delta = stamp - timestamp_ticker;
369 if (delta > UINT_MAX/2)
370 delta = UINT_MAX - delta + 1;
371 else if (delta < - ( (int) (UINT_MAX/2)))
372 delta = UINT_MAX + delta + 1;
378 // alternate timestamp functions. The way these work is you call xtimestamp() to get the
379 // current counter value, and then call
382 return timestamp_ticker;
385 int timestamp_has_time_elapsed(int stamp, int time)
393 if (t <= timestamp_ticker)
394 return 1; // if we are unlucky enough to have it wrap on us, this will assume time has elapsed.
399 // timing functions -------------------------------------------------------------------------------
401 #define MAX_TIMING_EVENTS 15
406 typedef struct timing {
408 int microseconds_total;
414 timing Timing_events[MAX_TIMING_EVENTS];
415 int Timing_event_count = 0;
419 // lookup a timing event
420 int timing_event_lookup(char *event_name)
428 if(event_name == NULL){
432 // look through all events
433 for(idx=0; idx<MAX_TIMING_EVENTS; idx++){
434 if(!stricmp(Timing_events[idx].name, event_name)){
443 // start timing frame stuff
444 void timing_frame_start()
452 Timing_event_count = 0;
453 Timing_frame.start = timer_get_microseconds();
454 for(idx=0; idx<MAX_TIMING_EVENTS; idx++){
455 Timing_events[idx].microseconds_total = 0;
456 strcpy(Timing_events[idx].name, "");
457 Timing_events[idx].ref_count = 0;
462 // done timing the frame
463 void timing_frame_stop()
471 stop_time = timer_get_microseconds();
474 Timing_frame.microseconds_total = stop_time - Timing_frame.start;
478 // get the total frame time in microseconds
479 int timing_frame_total()
484 return Timing_frame.microseconds_total;
488 // time an individual event
489 void timing_event_start(char *event_name)
497 if(event_name == NULL){
501 // try and find the event
502 event = timing_event_lookup(event_name);
504 // if we already have one
506 Assert(Timing_events[event].ref_count == 0);
507 Timing_events[event].start = timer_get_microseconds();
508 Timing_events[event].ref_count++;
510 // if we need to add a new one
512 if(Timing_event_count < MAX_TIMING_EVENTS){
513 strcpy(Timing_events[Timing_event_count].name, event_name);
514 Timing_events[Timing_event_count].start = timer_get_microseconds();
515 Timing_events[Timing_event_count++].ref_count++;
521 // stop timing an event
522 void timing_event_stop(char *event_name)
530 if(event_name == NULL){
534 // try and find the event
535 event = timing_event_lookup(event_name);
537 // if we already have one
539 Assert(Timing_events[event].ref_count == 1);
540 Timing_events[event].microseconds_total += timer_get_microseconds() - Timing_events[event].start;
541 Timing_events[event].ref_count--;
546 // get the total time for an event in microseconds
547 int timing_event_total(char *event_name)
555 if(event_name == NULL){
559 // try and find the event
560 event = timing_event_lookup(event_name);
565 return Timing_events[event].microseconds_total;
569 // get the percentage of total frametime for the event (0.0 to 1.0)
570 float timing_event_pct(char *event_name)
578 if(event_name == NULL){
582 // try and find the event
583 event = timing_event_lookup(event_name);
588 return (float)((double)Timing_events[event].microseconds_total / (double)Timing_frame.microseconds_total);
593 void timing_display(int x, int y)
600 gr_set_color_fast(&Color_bright_blue);
603 gr_printf(x, y, "Total time %f\n", (float)timing_frame_total() / 1000000.0f);
606 // each event percentage
607 for(idx=0; idx<Timing_event_count; idx++){
608 gr_printf(x, y, "%s : %f\n", Timing_events[idx].name, timing_event_pct(Timing_events[idx].name));