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