File: timer.h

package info (click to toggle)
freespace2 24.2.0%2Brepack-1
  • links: PTS, VCS
  • area: non-free
  • in suites: forky, sid
  • size: 43,716 kB
  • sloc: cpp: 595,001; ansic: 21,741; python: 1,174; sh: 457; makefile: 248; xml: 181
file content (259 lines) | stat: -rw-r--r-- 11,154 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/*
 * Copyright (C) Volition, Inc. 1999.  All rights reserved.
 *
 * All source code herein is the property of Volition, Inc. You may not sell 
 * or otherwise commercially exploit the source or things you created based on the 
 * source.
 *
*/ 



#ifndef _TIMER_H
#define _TIMER_H

#include "globalincs/pstypes.h"

#include "utils/id.h"
#include "utils/Random.h"

#include <cstdint>

using Random = util::Random;

// Empty structs used as type-safe markers.
struct ui_timestamp_tag {};
struct timestamp_tag {};

// These classes use the "strong typedef" pattern, based on
// similar usage in gamesnd, based in turn on this article:
// https://www.ilikebigbits.com/2014_05_06_type_safe_handles.html
// 
// Do not add additional fields or member functions to these classes
// (such as the functionality in timestamp_delta).  These classes must
// remain lightweight so that they can be optimized away.

// Timestamp measuring real time.  Runs regardless of time compression or pausing.
// Useful for timing real-world events and UI features like cursor blinking.
class UI_TIMESTAMP : public util::ID<ui_timestamp_tag, int, -1>
{
public:
	static UI_TIMESTAMP invalid() { return {}; }
	static UI_TIMESTAMP never() { return UI_TIMESTAMP(0); }
	static UI_TIMESTAMP immediate() { return UI_TIMESTAMP(1); }

	UI_TIMESTAMP() = default;
	explicit UI_TIMESTAMP(int val) : ID(val) { }

	inline bool isNever() const { return m_val == 0; }
	inline bool isImmediate() const { return m_val == 1; }
	inline bool isFinite() const { return isValid() && !isNever(); }
};

// Timestamp measuring game time.  Sensitive to time compression and pausing.
// Useful for timing in-mission events.
class TIMESTAMP : public util::ID<timestamp_tag, int, -1>
{
public:
	static TIMESTAMP invalid() { return {}; }
	static TIMESTAMP never() { return TIMESTAMP(0); }
	static TIMESTAMP immediate() { return TIMESTAMP(1); }

	TIMESTAMP() = default;
	explicit TIMESTAMP(int val) : ID(val) { }

	inline bool isNever() const { return m_val == 0; }
	inline bool isImmediate() const { return m_val == 1; }
	inline bool isFinite() const { return isValid() && !isNever(); }
};

// For converting from old-style timestamps:
//   A timestamp of 0 is invalid
//   A timestamp of 1 elapses immediately
//   A timestamp of 2 or more corresponds to that value of the timestamp timer
// See also the implementation of timestamp(int).  Also be careful not to confuse these values
// with the deltas; timestamp(-1) returns 0 and timestamp(0) returns 1.  But timestamp(1)
// returns the current value of timestamp_ms() plus 1 millisecond.  To add further confusion,
// legacy code often used -1 rather than 0 to indicate an invalid timestamp.


//==========================================================================
// This installs the timer services and interrupts at the rate specified by
// count_val.  If 'function' isn't 0, the function pointed to by function will
// be called 'freq' times per second.  Should be > 19 and anything around
// 2-3000 is gonna start slowing down the system.  Count_val should be
// 1,193,180 divided by your target frequency. Use 0 for the normal 18.2 Hz
// interrupt rate.

#ifdef _WIN32
#define TIMER_FREQUENCY 1193180
#endif

extern void timer_init();
extern void timer_close();
extern void timer_start_frame();

//==========================================================================
// These functions return the time since the timer was initialized in
// some various units. The total length of reading time varies for each
// one.  They will roll around after they read 2^32.
// There are milliseconds, milliseconds times 10, milliseconds times 100,
// and microseconds.  They time out after 1000 hrs, 100 hrs, 10 hrs, and
// 1 hr, respectively.

extern fix timer_get_fixed_seconds();		// Rolls about every 9 hours...
extern fix timer_get_approx_seconds();		// Returns time since program started... accurate to 1/120th of a second
extern int timer_get_milliseconds();		//
extern std::uint64_t timer_get_microseconds();
extern std::uint64_t timer_get_nanoseconds();
extern int timer_get_seconds();				// seconds since program started... not accurate, but good for long
											//     runtimes with second-based timeouts


//=================================================================
//=================================================================
//               T I M E S T A M P   F U N C T I O N S
//=================================================================
//=================================================================

// You shouldn't use the output of timestamp() directly, 
// but if you have to, use the TIMESTAMP_FREQUENCY to 
// scale it correctly.
// TODO: remove when nobody uses the output directly
#define TIMESTAMP_FREQUENCY 1000

// conversion factors
constexpr int MILLISECONDS_PER_SECOND = 1000;
constexpr int MICROSECONDS_PER_MILLISECOND = 1000;
constexpr int NANOSECONDS_PER_MICROSECOND = 1000;
constexpr uint64_t MICROSECONDS_PER_SECOND = MICROSECONDS_PER_MILLISECOND * MILLISECONDS_PER_SECOND;
constexpr uint64_t NANOSECONDS_PER_SECOND = NANOSECONDS_PER_MICROSECOND * MICROSECONDS_PER_SECOND;

// use this call to get the current counter value (which represents the time at the time
// this function is called).  I.e. it doesn't return a count that would be in the future,
// but the count that is right now.
int timestamp();
TIMESTAMP _timestamp();					// use a leading underscore for now until all timestamps are converted over

// same, but for use in the UI, so not subject to time compression or pauses
UI_TIMESTAMP ui_timestamp();

inline bool timestamp_valid(int stamp) {
	return stamp != 0;
}

// To do timing, call this with the interval you
// want to check.  Then, pass this to timestamp_elapsed
// to see if delta_ms time has elapsed.   If delta_ms is
// zero, the next call to timestamp_elapsed will always
// return 1.  If delta_ms is less than zero, then this is
// considered an invalid timestamp and all calls to 
// timestamp_elapsed will return 0.
// In other words:
// pass -1 for an invalid timestamp that will never time out
// pass 0 for a timestamp that is instantly timed out
// pass n > 0 for timestamp n milliseconds in the future.
int timestamp(int delta_ms);
TIMESTAMP _timestamp(int delta_ms);		// use a leading underscore for now until all timestamps are converted over
UI_TIMESTAMP ui_timestamp(int delta_ms);

TIMESTAMP timestamp_delta(TIMESTAMP stamp, int delta_ms);
UI_TIMESTAMP ui_timestamp_delta(UI_TIMESTAMP stamp, int delta_ms);

int timestamp_get_delta(TIMESTAMP before, TIMESTAMP after);
int ui_timestamp_get_delta(UI_TIMESTAMP before, UI_TIMESTAMP after);

// gets a timestamp randomly between a and b milliseconds in
// the future.
inline int timestamp_rand(int a, int b) {
	return timestamp(Random::next(a, b));
}
inline TIMESTAMP _timestamp_rand(int a, int b) {
	return _timestamp(Random::next(a, b));
}

//	Returns milliseconds until timestamp will elapse.  Invalid timestamps are assumed to occur at approximately T=0.
int timestamp_until(int stamp);
//	Returns milliseconds until timestamp will elapse.  This will Assert against Invalid or Never timestamps but fail gracefully by returning INT_MAX.
int timestamp_until(TIMESTAMP stamp);
//	Returns milliseconds until timestamp will elapse.  This will Assert against Invalid or Never timestamps but fail gracefully by returning INT_MAX.
int ui_timestamp_until(UI_TIMESTAMP stamp);

//	Returns milliseconds after timestamp has elapsed.  Invalid timestamps are assumed to occur at approximately T=0.
int timestamp_since(int stamp);
//	Returns milliseconds after timestamp has elapsed.  This will Assert against Invalid or Never timestamps but fail gracefully by returning INT_MIN.
int timestamp_since(TIMESTAMP stamp);
//	Returns milliseconds after timestamp has elapsed.  This will Assert against Invalid or Never timestamps but fail gracefully by returning INT_MIN.
int ui_timestamp_since(UI_TIMESTAMP stamp);

// A standard comparison function that returns <0, 0, or >0 as the left item is less than, equal to, or greater than the right item.
int timestamp_compare(TIMESTAMP t1, TIMESTAMP t2);
// A standard comparison function that returns <0, 0, or >0 as the left item is less than, equal to, or greater than the right item.
int ui_timestamp_compare(UI_TIMESTAMP t1, UI_TIMESTAMP t2);

// Checks that a timestamp occurs between the "before" and "after" timestamps.
bool timestamp_in_between(TIMESTAMP stamp, TIMESTAMP before, TIMESTAMP after);
// Checks that a timestamp occurs between the "before" and "after" timestamps.
bool ui_timestamp_in_between(UI_TIMESTAMP stamp, UI_TIMESTAMP before, UI_TIMESTAMP after);

// Example that makes a ship fire in 1/2 second

// ...
// ship->next_fire = timestamp(500);
// ...
// if (fire && timestamp_elapsed(ship->next_fire))
//   fire_laser();

bool timestamp_elapsed( int stamp );
bool timestamp_elapsed( TIMESTAMP stamp );
bool timestamp_elapsed_last_frame( TIMESTAMP stamp );
bool ui_timestamp_elapsed( UI_TIMESTAMP stamp );
bool ui_timestamp_elapsed_last_frame( UI_TIMESTAMP stamp );

// safer version of timestamp
bool timestamp_elapsed_safe(int a, int b);
bool timestamp_elapsed_safe(TIMESTAMP a, int b);
bool ui_timestamp_elapsed_safe(UI_TIMESTAMP a, int b);

//=================================================================
//               T I M E S T A M P   A D J U S T M E N T
//=================================================================

// This should be called when the timestamp should stop ticking, e.g when the game is paused.
// The "sudo" is for cases where we want the time to remain paused, e.g. during level loading,
//     even if the game loses focus (which would normally unpause when focus is regained)
void timestamp_pause(bool sudo);

// This should be called when the timestamp should resume ticking, e.g. when the player is in-mission.
// See above re: the sudo parameter
void timestamp_unpause(bool sudo);

// for timestamp handling during debugging
bool timestamp_is_paused();
void timestamp_adjust_pause_offset(int delta_milliseconds);

enum class TIMER_DIRECTION { FORWARD, BACKWARD };
// Alters the timestamp time forward or backward.  Use with caution!
void timestamp_adjust_seconds(float delta_seconds, TIMER_DIRECTION dir);
void timestamp_adjust_microseconds(uint64_t delta_microseconds, TIMER_DIRECTION dir);

// This should be called when the game time compression is changed in any way, so that
// the timestamp will be consistent with the faster or slower time.
void timestamp_update_time_compression();

//=================================================================
//               M I S S I O N   T I M E
//=================================================================

// Save the timestamp corresponding to the beginning of the mission
void timestamp_start_mission();

// Calculate the current mission time using the timestamps
fix timestamp_get_mission_time();
uint64_t timestamp_get_mission_time_in_microseconds();
int timestamp_get_mission_time_in_milliseconds();

// Advance mission time by specific offset (for in-game joining)
void timestamp_offset_mission_time(float offset);

#endif