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 the
6  * source.
7  *
8 */
9 
10 
11 
12 #include "globalincs/pstypes.h"
13 #include "io/timer.h"
14 #include "graphics/2d.h"
15 #include "globalincs/alphacolors.h"
16 
17 #include "osapi/osapi.h"	// for multi-thread macros
18 
19 #include <SDL_timer.h>
20 #include <climits>
21 
22 static Uint64 Timer_perf_counter_freq = 0;	// perf counter frequency - number of ticks per second
23 static Uint64 Timer_base_value;
24 
25 static int Timer_inited = 0;
26 
27 
28 #define MICROSECONDS_PER_SECOND 1000000
29 #define NANOSECONDS_PER_SECOND 1000000000
30 
31 static long double Timer_to_microseconds;
32 static long double Timer_to_nanoseconds;
33 
get_performance_counter()34 static uint64_t get_performance_counter()
35 {
36 	Assertion(Timer_inited, "This function can only be used when the timer system is initialized!");
37 
38 	auto counter = SDL_GetPerformanceCounter();
39 
40 	return counter - Timer_base_value;
41 }
42 
timer_close()43 void timer_close()
44 {
45 	if ( Timer_inited )	{
46 		Timer_inited = 0;
47 	}
48 }
49 
timer_init()50 void timer_init()
51 {
52 	if ( !Timer_inited )	{
53 		Timer_perf_counter_freq = SDL_GetPerformanceFrequency();
54 		Timer_base_value = SDL_GetPerformanceCounter();
55 		Timer_to_nanoseconds = (long double) NANOSECONDS_PER_SECOND / (long double) Timer_perf_counter_freq;
56 		Timer_to_microseconds = (long double) MICROSECONDS_PER_SECOND / (long double) Timer_perf_counter_freq;
57 		Timer_inited = 1;
58 
59 		atexit(timer_close);
60 	}
61 }
62 
timer_get_fixed_seconds()63 fix timer_get_fixed_seconds()
64 {
65 	if (!Timer_inited) {
66 		Int3();					// Make sure you call timer_init before anything that uses timer functions!
67 		return 0;
68 	}
69 
70 	auto time = timer_get_microseconds();
71 	time *= 65536;
72 
73 	return (fix)(time / MICROSECONDS_PER_SECOND);
74 }
75 
timer_get_approx_seconds()76 fix timer_get_approx_seconds()
77 {
78 	return timer_get_fixed_seconds();
79 }
80 
timer_get_seconds()81 int timer_get_seconds()
82 {
83 	if (!Timer_inited) {
84 		Int3();
85 		return 0;
86 	}
87 
88 	return (int) (timer_get_microseconds() / MICROSECONDS_PER_SECOND);
89 }
90 
timer_get_milliseconds()91 int timer_get_milliseconds()
92 {
93 	if (!Timer_inited) {
94 		Int3();					// Make sure you call timer_init before anything that uses timer functions!
95 		return 0;
96 	}
97 
98 	return (int) (timer_get_microseconds() / 1000);
99 }
100 
timer_get_microseconds()101 std::uint64_t timer_get_microseconds()
102 {
103 	auto time = get_performance_counter();
104 
105 	return (uint64_t) (time * Timer_to_microseconds);
106 }
107 
timer_get_nanoseconds()108 std::uint64_t timer_get_nanoseconds()
109 {
110 	auto time = get_performance_counter();
111 
112     return (uint64_t) (time * Timer_to_nanoseconds);
113 }
114 
115 // 0 means invalid,
116 // 1 means always return true
117 // 2 and above actually check the time
118 std::uint64_t timestamp_ticker = 2;
119 
timestamp_reset()120 void timestamp_reset()
121 {
122 	timestamp_ticker = 2;
123 }
124 
125 // Restrict all time values between 0 and MAX_TIME
126 // so we don't have to use UINTs to calculate rollover.
127 // For debugging & testing, you could set this to
128 // something like 1 minute (6000).
129 extern const std::uint32_t MAX_TIME = INT_MAX / 2;
130 
timestamp_ms()131 static int timestamp_ms() {
132 	if (timestamp_ticker <= 2) {
133 		// These are special values, don't adjust them
134 		return (int)timestamp_ticker;
135 	}
136 	return (int)(timestamp_ticker / 1000);
137 }
138 
timestamp_inc(fix frametime)139 void timestamp_inc(fix frametime)
140 {
141 	// Compute the microseconds, assumes that a fix uses the lower 16 bit for storing the fractional part
142 	auto delta = (std::uint64_t)frametime;
143 	delta = delta * (MICROSECONDS_PER_SECOND / 65536);
144 
145 	timestamp_ticker += delta;
146 
147 	if ( timestamp_ms() > (int)MAX_TIME )	{
148 		timestamp_ticker = 2;		// Roll!
149 	}
150 
151 	if (timestamp_ticker < 2 ) {
152 		mprintf(("Whoa!!!  timestamp_ticker < 2 -- resetting to 2!!!\n"));
153 		timestamp_ticker = 2;
154 	}
155 }
156 
timestamp(int delta_ms)157 int timestamp(int delta_ms ) {
158 	int t2;
159 	if (delta_ms < 0 ) return 0;
160 	if (delta_ms == 0 ) return 1;
161 	t2 = timestamp_ms() + delta_ms;
162 	if ( t2 > (int)MAX_TIME )	{
163 		// wrap!!!
164 		t2 = delta_ms - (MAX_TIME-timestamp_ms());
165 	}
166 	if (t2 < 2 ) t2 = 2;	// hack??
167 	return t2;
168 }
169 
170 //	Returns milliseconds until timestamp will elapse.
171 //	Negative value gives milliseconds ago that timestamp elapsed.
timestamp_until(int stamp)172 int timestamp_until(int stamp) {
173 	// JAS: FIX
174 	// HACK!! This doesn't handle rollover!
175 	// (Will it ever happen?)
176 
177 	return stamp - timestamp_ms();
178 
179 /*
180 	uint	delta;
181 
182 	delta = stamp - timestamp_ticker;
183 
184 
185 	if (delta > UINT_MAX/2)
186 		delta = UINT_MAX - delta + 1;
187 	else if (delta < - ( (int) (UINT_MAX/2)))
188 		delta = UINT_MAX + delta + 1;
189 
190 	return delta;
191 */
192 }
193 
194 // alternate timestamp functions.  The way these work is you call xtimestamp() to get the
195 // current counter value, and then call
timestamp()196 int timestamp() {
197 	return timestamp_ms();
198 }
199 
timestamp_has_time_elapsed(int stamp,int time)200 int timestamp_has_time_elapsed(int stamp, int time) {
201 	int t;
202 
203 	if (time <= 0)
204 		return 1;
205 
206 	t = stamp + time;
207 	if (t <= timestamp_ms())
208 		return 1;  // if we are unlucky enough to have it wrap on us, this will assume time has elapsed.
209 
210 	return 0;
211 }
timestamp_elapsed(int stamp)212 bool timestamp_elapsed(int stamp) {
213 	if (stamp == 0) {
214 		return false;
215 	}
216 
217 	return timestamp_ms() >= stamp;
218 }
timestamp_elapsed_safe(int a,int b)219 bool timestamp_elapsed_safe(int a, int b) {
220 	if (a == 0) {
221 		return true;
222 	}
223 
224 	return timestamp_ms() >= a || timestamp_ms() < (a - b + 100);
225 }
timestamp_set_value(int value)226 void timestamp_set_value(int value) {
227 	timestamp_ticker = (std::uint64_t) value * 1000;
228 }
229 
230