-
Notifications
You must be signed in to change notification settings - Fork 9
/
frame_timer.cpp
125 lines (104 loc) · 4.37 KB
/
frame_timer.cpp
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
//these are loaded from Settings in production code
double update_rate = 60;
int update_multiplicity = 1;
bool unlock_framerate = true;
//compute how many ticks one update should be
int64_t clocks_per_second = SDL_GetPerformanceFrequency();
double fixed_deltatime = 1.0 / update_rate;
int64_t desired_frametime = clocks_per_second / update_rate;
//these are to snap deltaTime to vsync values if it's close enough
int64_t vsync_maxerror = clocks_per_second * .0002;
//get the refresh rate of the display (you should detect which display the window is on in production)
int display_framerate = 60;
SDL_DisplayMode current_display_mode;
if(SDL_GetCurrentDisplayMode(0, ¤t_display_mode)==0) {
display_framerate = current_display_mode.refresh_rate;
}
int64_t snap_hz = display_framerate;
if(snap_hz <= 0) snap_hz = 60;
//these are to snap deltaTime to vsync values if it's close enough
int64_t snap_frequencies[8] = {};
for(int i = 0; i<8; i++) {
snap_frequencies[i] = (clocks_per_second / snap_hz) * (i+1);
}
//this is for delta time averaging
//I know you can and should use a ring buffer for this, but I didn't want to include dependencies in this sample code
const int time_history_count = 4;
int64_t time_averager[time_history_count] = {desired_frametime, desired_frametime, desired_frametime, desired_frametime};
int64_t averager_residual = 0;
//these are stored in my Application class and are not local variables in production code
bool running = true;
bool resync = true;
int64_t prev_frame_time = SDL_GetPerformanceCounter();
int64_t frame_accumulator = 0;
while (running){
//frame timer
int64_t current_frame_time = SDL_GetPerformanceCounter();
int64_t delta_time = current_frame_time - prev_frame_time;
prev_frame_time = current_frame_time;
//handle unexpected timer anomalies (overflow, extra slow frames, etc)
if(delta_time > desired_frametime*8){ //ignore extra-slow frames
delta_time = desired_frametime;
}
if(delta_time < 0){
delta_time = 0;
}
//vsync time snapping
for(int64_t snap : snap_frequencies){
if(std::abs(delta_time - snap) < vsync_maxerror){
delta_time = snap;
break;
}
}
//delta time averaging
for(int i = 0; i<time_history_count-1; i++){
time_averager[i] = time_averager[i+1];
}
time_averager[time_history_count-1] = delta_time;
int64_t averager_sum = 0;
for(int i = 0; i<time_history_count; i++){
averager_sum += time_averager[i];
}
delta_time = averager_sum / time_history_count;
averager_residual += averager_sum % time_history_count;
delta_time += averager_residual / time_history_count;
averager_residual %= time_history_count;
//add to the accumulator
frame_accumulator += delta_time;
//spiral of death protection
if(frame_accumulator > desired_frametime*8){
resync = true;
}
//timer resync if requested
if(resync) {
frame_accumulator = 0;
delta_time = desired_frametime;
resync = false;
}
// process system events
ProcessEvents();
if(unlock_framerate){ //UNLOCKED FRAMERATE, INTERPOLATION ENABLED
int64_t consumedDeltaTime = delta_time;
while(frame_accumulator >= desired_frametime){
game.fixed_update(fixed_deltatime);
if(consumedDeltaTime > desired_frametime){ //cap variable update's dt to not be larger than fixed update, and interleave it (so game state can always get animation frames it needs)
game.variable_update(fixed_deltatime);
consumedDeltaTime -= desired_frametime;
}
frame_accumulator -= desired_frametime;
}
game.variable_update((double)consumedDeltaTime / clocks_per_second);
game.render((double)frame_accumulator / desired_frametime);
display(); //swap buffers
} else { //LOCKED FRAMERATE, NO INTERPOLATION
while(frame_accumulator >= desired_frametime*update_multiplicity){
for(int i = 0; i<update_multiplicity; i++){
game.fixed_update(fixed_deltatime);
game.variable_update(fixed_deltatime);
frame_accumulator -= desired_frametime;
}
}
game.render(1.0);
display(); //swap buffers
}
}