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