Skip to content

Commit fcf405e

Browse files
committed
Smoothing out fps limit
1 parent 18e354d commit fcf405e

File tree

2 files changed

+34
-9
lines changed

2 files changed

+34
-9
lines changed

src/examples/forest.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ fn on_init() void {
146146
// capture and hide the mouse!
147147
papp.captureMouse(true);
148148

149+
// set a FPS limit, to test that as well
150+
papp.setTargetFPS(60);
151+
149152
sprite_batch = batcher.SpriteBatcher.init(.{}) catch {
150153
debug.showErrorScreen("Fatal error during batch init!");
151154
return;

src/framework/platform/app.zig

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const AppBackend = sokol_app_backend.App;
1010

1111
const NS_PER_SECOND: i64 = 1_000_000_000;
1212
const NS_PER_SECOND_F: f32 = 1_000_000_000.0;
13+
const NS_FPS_LIMIT_OVERHEAD = 1_250_000; // tuned to ensure consistent frame pacing
1314

1415
const state = struct {
1516
// FPS cap vars, if set
@@ -148,23 +149,44 @@ fn on_frame() void {
148149

149150
// tell modules this frame is done
150151
modules.postDrawModules();
151-
}
152152

153-
/// Get time elapsed since last tick. Also calculate the FPS!
154-
fn calcDeltaTime() f32 {
155-
if (state.reset_delta) {
156-
state.reset_delta = false;
157-
state.game_loop_timer.reset();
158-
return 1.0 / 60.0;
159-
}
153+
// keep under our FPS limit, if needed
154+
limitFps();
155+
}
160156

157+
fn limitFps() void {
161158
if (state.target_fps != null) {
162159
// Try to hit our target FPS!
163-
const frame_len_ns = state.game_loop_timer.read();
164160

161+
// Easy case, just stop here if we are under the target frame length
162+
const initial_frame_ns = state.game_loop_timer.read();
163+
if (initial_frame_ns >= state.target_fps_ns)
164+
return;
165+
166+
// Harder case, we are faster than the target frame length.
167+
// Note: time.sleep does not ensure consistent timing.
168+
// Due to this we need to sleep most of the time, but busy loop the rest.
169+
170+
const frame_len_ns = initial_frame_ns + NS_FPS_LIMIT_OVERHEAD;
165171
if (frame_len_ns < state.target_fps_ns) {
166172
time.sleep(state.target_fps_ns - frame_len_ns);
167173
}
174+
175+
// Eat up the rest of the time in a busy loop to ensure consistent frame pacing
176+
while (true) {
177+
const cur_frame_len_ns = state.game_loop_timer.read();
178+
if (cur_frame_len_ns >= state.target_fps_ns)
179+
break;
180+
}
181+
}
182+
}
183+
184+
/// Get time elapsed since last tick. Also calculate the FPS!
185+
fn calcDeltaTime() f32 {
186+
if (state.reset_delta) {
187+
state.reset_delta = false;
188+
state.game_loop_timer.reset();
189+
return 1.0 / 60.0;
168190
}
169191

170192
// calculate the fps by counting frames each second

0 commit comments

Comments
 (0)