]> icculus.org git repositories - taylor/freespace2.git/blob - src/io/timer.cpp
Initial revision
[taylor/freespace2.git] / src / io / timer.cpp
1 /*
2  * $Logfile: /Freespace2/code/Io/Timer.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Include file for timer stuff
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:09  root
11  * Initial revision
12  *
13  * 
14  * 4     5/26/99 3:19p Dave
15  * Fixed release build errors.
16  * 
17  * 3     5/17/99 6:03p Dave
18  * Added new timing code. Put in code to allow camera speed zooming.
19  * 
20  * 2     10/07/98 10:53a Dave
21  * Initial checkin.
22  * 
23  * 1     10/07/98 10:49a Dave
24  * 
25  * 14    4/13/98 10:16a John
26  * Switched gettime back to timer_get_milliseconds, which is now thread
27  * safe.
28  * 
29  * 13    4/13/98 10:11a John
30  * Made timer functions thread safe.  Made timer_init be called in all
31  * projects.
32  * 
33  * 12    4/13/98 8:09a John
34  * Made timer_get functions thread safe.
35  * 
36  * 11    12/02/97 3:56p John
37  * fixed a bug with timestamp_until, created one with it rolling over.
38  * 
39  * 10    9/11/97 7:12p Hoffoss
40  * Added more functionality to training sexp handling code.
41  * 
42  * 9     8/29/97 4:49p Allender
43  * added mprintf to check for wrap problems with timestamp ticker
44  * 
45  * 8     8/28/97 1:38p Allender
46  * from John:  changes to timer func to detect early rollever
47  * 
48  * 7     7/29/97 5:30p Lawrance
49  * move gettime() from keyboard module to timer module
50  * 
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.
55  * 
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
58  * them.
59  *
60  * $NoKeywords: $
61  */
62
63 #ifndef PLAT_UNIX
64 #include <windows.h>
65 #endif
66
67 #include        "limits.h"
68 #include "pstypes.h"
69 #include "timer.h"
70 #include "2d.h"
71 #include "alphacolors.h"
72
73 #ifndef NDEBUG
74         #define USE_TIMING
75 #endif
76
77 static longlong Timer_last_value, Timer_base;
78 static uint Timer_freq=0;
79
80 static int Timer_inited = 0;
81
82 static CRITICAL_SECTION Timer_lock;
83
84 void timer_close()
85 {
86         if ( Timer_inited )     {
87                 Timer_inited = 0;
88 #ifdef PLAT_UNIX
89                 STUB_FUNCTION;
90 #else
91                 DeleteCriticalSection( &Timer_lock );
92 #endif
93         }
94 }
95
96 void timer_init()
97 {
98         if ( !Timer_inited )    {
99 #ifdef PLAT_UNIX
100                 STUB_FUNCTION;
101 #else
102                 LARGE_INTEGER tmp;
103                 QueryPerformanceFrequency(&tmp);
104                 Assert( tmp.HighPart == 0 );
105                 Timer_freq = tmp.LowPart;
106
107                 QueryPerformanceCounter((LARGE_INTEGER *)&Timer_base);
108                 QueryPerformanceCounter((LARGE_INTEGER *)&Timer_last_value);
109
110                 InitializeCriticalSection(&Timer_lock);
111 #endif
112                 
113                 Timer_inited = 1;
114
115                 atexit(timer_close);
116         }
117 }
118
119 // Fills Time_now with the ticks since program start
120 static void timer_get(LARGE_INTEGER * out)
121 {
122 #ifdef PLAT_UNIX
123         STUB_FUNCTION;
124 #else
125         EnterCriticalSection(&Timer_lock);
126
127         longlong time_tmp;
128         longlong Time_now;
129
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.
137                 Time_now = time_tmp;
138         }
139         Time_now = time_tmp - Timer_base;
140         Timer_last_value = time_tmp;
141
142         out->QuadPart = Time_now;
143
144         LeaveCriticalSection(&Timer_lock);
145 #endif
146 }
147
148 fix timer_get_fixed_seconds()
149 {
150 #ifdef PLAT_UNIX
151         STUB_FUNCTION;
152         return 0;
153 #else
154         int tmp;
155         LARGE_INTEGER temp_large;
156
157         if (!Timer_inited) {
158                 Int3();                                 // Make sure you call timer_init before anything that uses timer functions!
159                 return 0;
160         }
161
162         timer_get(&temp_large);
163
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
168
169         _asm    shld    edx, eax, 16            ; Keep 32+11 bits
170         _asm    shl     eax, 16                 
171         // edx:eax = number of 1.19Mhz pulses elapsed.
172         _asm    mov     ebx, Timer_freq
173
174         // Make sure we won't divide overflow.  Make time wrap at about 9 hours
175 sub_again:
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.
179         _asm    div     ebx
180         //eax = fixed point seconds elapsed...
181
182         _asm mov tmp, eax
183
184         return tmp;
185 #endif
186 }
187
188 fix timer_get_fixed_secondsX()
189 {
190         return timer_get_fixed_seconds();
191 }
192
193 fix timer_get_approx_seconds()
194 {
195         return timer_get_fixed_seconds();
196 }
197
198 int timer_get_milliseconds()
199 {
200 #ifdef PLAT_UNIX
201         STUB_FUNCTION;
202         return 0;
203 #else
204         int tmp;
205         LARGE_INTEGER temp_large;
206
207         if (!Timer_inited) {
208                 Int3();                                 // Make sure you call timer_init before anything that uses timer functions!
209                 return 0;
210         }
211
212         timer_get(&temp_large);
213
214         temp_large.QuadPart *= (longlong)1000;
215
216         // Timing in milliseconds.
217         _asm    mov edx, temp_large.HighPart
218         _asm    mov eax, temp_large.LowPart
219
220         //_asm  shld    edx, eax, 16            ; Keep 32+11 bits
221         //_asm  shl     eax, 16                 
222         // edx:eax = number of 1.19Mhz pulses elapsed.
223         _asm    mov     ebx, Timer_freq
224
225         // Make sure we won't divide overflow.  Make time wrap at about 9 hours
226 sub_again:
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.
230         _asm    div     ebx
231         //eax = milliseconds elapsed...
232
233         _asm mov tmp, eax
234
235         return tmp;
236 #endif
237 }
238
239 int timer_get_microseconds()
240 {
241 #ifdef PLAT_UNIX
242         STUB_FUNCTION;
243         return 0;
244 #else
245         int tmp;
246         LARGE_INTEGER temp_large;
247
248         if (!Timer_inited) {
249                 Int3();                                 // Make sure you call timer_init before anything that uses timer functions!
250                 return 0;
251         }
252
253         timer_get(&temp_large);
254
255         temp_large.QuadPart *= (longlong)1000000;
256
257         // Timing in milliseconds.
258         _asm    mov edx, temp_large.HighPart
259         _asm    mov eax, temp_large.LowPart
260
261         //_asm  shld    edx, eax, 16            ; Keep 32+11 bits
262         //_asm  shl     eax, 16                 
263         // edx:eax = number of 1.19Mhz pulses elapsed.
264         _asm    mov     ebx, Timer_freq
265
266         // Make sure we won't divide overflow.  Make time wrap at about 9 hours
267 sub_again:
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.
271         _asm    div     ebx
272         //eax = milliseconds elapsed...
273
274         _asm mov tmp, eax
275
276         return tmp;
277 #endif
278 }
279
280 // 0 means invalid,
281 // 1 means always return true
282 // 2 and above actually check the time
283 int timestamp_ticker = 2;
284
285 void timestamp_reset()
286 {
287         timestamp_ticker = 2;
288 }
289
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)
295
296 void timestamp_inc(float frametime)
297 {
298         timestamp_ticker += (int)(frametime*TIMESTAMP_FREQUENCY);
299
300         if ( timestamp_ticker > MAX_TIME )      {
301                 timestamp_ticker = 2;           // Roll!
302         }
303
304         if (timestamp_ticker < 2 ) {
305                 mprintf(("Whoa!!!  timestamp_ticker < 2 -- resetting to 2!!!\n"));
306                 timestamp_ticker = 2;
307         }
308 }
309
310 int timestamp(int delta_ms )
311 {
312         int t2;
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 )    {
317                 // wrap!!!
318                 t2 = delta_ms - (MAX_TIME-timestamp_ticker);
319         }
320         if (t2 < 2 ) t2 = 2;    // hack??
321         return t2;
322 }
323
324 //      Returns milliseconds until timestamp will elapse.
325 //      Negative value gives milliseconds ago that timestamp elapsed.
326 int timestamp_until(int stamp)
327 {
328         // JAS: FIX
329         // HACK!! This doesn't handle rollover!
330         // (Will it ever happen?)
331         
332         return stamp - timestamp_ticker;
333
334 /*
335         uint    delta;
336
337         delta = stamp - timestamp_ticker;
338         
339
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;
344
345         return delta;
346 */
347 }
348
349 // alternate timestamp functions.  The way these work is you call xtimestamp() to get the
350 // current counter value, and then call
351 int timestamp()
352 {
353         return timestamp_ticker;
354 }
355
356 int timestamp_has_time_elapsed(int stamp, int time)
357 {
358         int t;
359
360         if (time <= 0)
361                 return 1;
362
363         t = stamp + 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.
366
367         return 0;
368 }
369
370 // timing functions -------------------------------------------------------------------------------
371
372 #define MAX_TIMING_EVENTS               15
373
374 // timing struct
375 #ifdef USE_TIMING
376
377 typedef struct timing {
378         char name[64];
379         int microseconds_total;
380         int start;
381         int ref_count;
382 } timing;
383
384 timing Timing_frame;
385 timing Timing_events[MAX_TIMING_EVENTS];
386 int Timing_event_count = 0;
387
388 #endif
389
390 // lookup a timing event
391 int timing_event_lookup(char *event_name)
392 {
393 #ifndef USE_TIMING
394         return -1;
395 #else
396         int idx;
397
398         // sanity
399         if(event_name == NULL){
400                 return -1;
401         }
402
403         // look through all events
404         for(idx=0; idx<MAX_TIMING_EVENTS; idx++){
405                 if(!stricmp(Timing_events[idx].name, event_name)){
406                         return idx;
407                 }
408         }
409
410         return -1;
411 #endif
412 }
413
414 // start timing frame stuff
415 void timing_frame_start()
416 {
417 #ifndef USE_TIMING
418         return;
419 #else
420         int idx;
421
422         // restart the frame
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;
429         }
430 #endif
431 }
432
433 // done timing the frame
434 void timing_frame_stop()
435 {
436 #ifndef USE_TIMING
437         return;
438 #else   
439         int stop_time;
440
441         // stop time
442         stop_time = timer_get_microseconds();
443
444         // calc times
445         Timing_frame.microseconds_total = stop_time - Timing_frame.start;       
446 #endif
447 }
448
449 // get the total frame time in microseconds
450 int timing_frame_total()
451 {
452 #ifndef USE_TIMING
453         return 0;
454 #else
455         return Timing_frame.microseconds_total;
456 #endif
457 }
458
459 // time an individual event
460 void timing_event_start(char *event_name)
461 {
462 #ifndef USE_TIMING
463         return;
464 #else
465         int event;
466
467         // sanity
468         if(event_name == NULL){
469                 return;
470         }
471
472         // try and find the event
473         event = timing_event_lookup(event_name);
474
475         // if we already have one
476         if(event != -1){
477                 Assert(Timing_events[event].ref_count == 0);
478                 Timing_events[event].start = timer_get_microseconds();
479                 Timing_events[event].ref_count++;
480         }
481         // if we need to add a new one
482         else {
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++;
487                 }
488         }
489 #endif
490 }
491
492 // stop timing an event
493 void timing_event_stop(char *event_name)
494 {
495 #ifndef USE_TIMING
496         return;
497 #else
498         int event;
499
500         // sanity
501         if(event_name == NULL){
502                 return;
503         }
504
505         // try and find the event
506         event = timing_event_lookup(event_name);
507
508         // if we already have one
509         if(event != -1){
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--;
513         }
514 #endif
515 }
516
517 // get the total time for an event in microseconds
518 int timing_event_total(char *event_name)
519 {
520 #ifndef USE_TIMING
521         return -1;
522 #else
523         int event;
524
525         // sanity
526         if(event_name == NULL){
527                 return -1;
528         }
529
530         // try and find the event
531         event = timing_event_lookup(event_name);
532         if(event == -1){
533                 return -1;
534         }
535         
536         return Timing_events[event].microseconds_total;
537 #endif
538 }
539
540 // get the percentage of total frametime for the event (0.0 to 1.0)
541 float timing_event_pct(char *event_name)
542 {
543 #ifndef USE_TIMING
544         return 0.0f;
545 #else
546         int event;
547
548         // sanity
549         if(event_name == NULL){
550                 return -1.0f;
551         }
552
553         // try and find the event
554         event = timing_event_lookup(event_name);
555         if(event == -1){
556                 return -1.0f;
557         }
558
559         return (float)((double)Timing_events[event].microseconds_total / (double)Timing_frame.microseconds_total);
560 #endif
561 }
562
563 // display timing 
564 void timing_display(int x, int y)
565 {
566 #ifndef USE_TIMING
567         return;
568 #else
569         int idx;
570
571         gr_set_color_fast(&Color_bright_blue);
572
573         // total time
574         gr_printf(x, y, "Total time %f\n", (float)timing_frame_total() / 1000000.0f);
575         y += 10;
576
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));
580                 y += 10;
581         }
582 #endif
583 }