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