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