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