waitTime
Takes output of Window.end. Returns microseconds the app should wait
(with event interruption) before running the render loop again.
If Window.max_fps is not null, will sleep to keep the framerate under
that (usually set in the Debug window).
Pass return value to backend.waitEventTimeout().
Cooperates with Window.beginWait to estimate how much time is being spent
outside the render loop and account for that.
Parameters
- self:*Self
- *Self
- end_micros:?u32
- ?u32
Source
Implementation
pub fn waitTime(self: *Self, end_micros: ?u32) u32 {
// end_micros is the naive value we want to be between last begin and next begin
// minimum time to wait to hit max fps target
var min_micros: u32 = 0;
if (self.max_fps) |mfps| {
min_micros = @as(u32, @trunc(1_000_000.0 / mfps));
}
//std.debug.print(" end {d:6} min {d:6}", .{end_micros, min_micros});
// wait_micros is amount on top of min_micros we will conditionally wait
var wait_micros = (end_micros orelse 0) -| min_micros;
// assume that we won't target a specific time to sleep but if we do
// calculate the targets before removing so_far and slop
self.loop_wait_target = null;
self.loop_wait_target_can_interrupt = false;
const target_min = min_micros;
const target = min_micros + wait_micros;
// how long it's taken from begin to here
var so_far_nanos = @max(self.frame_time_ns, self.backend.nanoTime()) - self.frame_time_ns;
so_far_nanos = @divFloor(so_far_nanos, 1000);
so_far_nanos = @min(so_far_nanos, (1 << 32) - 1);
var so_far_micros = @as(u32, @intCast(so_far_nanos));
//std.debug.print(" far {d:6}", .{so_far_micros});
// take time from min_micros first
const min_so_far = @min(so_far_micros, min_micros);
so_far_micros -= min_so_far;
min_micros -= min_so_far;
// then take time from wait_micros
const min_so_far2 = @min(so_far_micros, wait_micros);
so_far_micros -= min_so_far2;
wait_micros -= min_so_far2;
var slop = self.loop_target_slop;
// get slop we can take out of min_micros
const min_us_slop = @min(slop, @as(i32, @intCast(min_micros)));
slop -= min_us_slop;
if (min_us_slop >= 0) {
min_micros -= @as(u32, @intCast(min_us_slop));
} else {
min_micros += @as(u32, @intCast(-min_us_slop));
}
// remaining slop we can take out of wait_micros
const wait_us_slop = @min(slop, @as(i32, @intCast(wait_micros)));
slop -= wait_us_slop;
if (wait_us_slop >= 0) {
wait_micros -= @as(u32, @intCast(wait_us_slop));
} else {
wait_micros += @as(u32, @intCast(-wait_us_slop));
}
//std.debug.print(" min {d:6}", .{min_micros});
if (min_micros > 0) {
// wait unconditionally for fps target
self.backend.sleep(min_micros * 1000);
self.loop_wait_target = self.frame_time_ns + (@as(i128, @intCast(target_min)) * 1000);
}
var wait_time_micros_final: u32 = blk: {
if (end_micros == null) {
// no target, wait indefinitely for next event
self.loop_wait_target = null;
//std.debug.print(" wait indef\n", .{});
break :blk std.math.maxInt(u32);
} else if (wait_micros > 0) {
// wait conditionally
// since we have a timeout we will try to hit that target but set our
// flag so that we don't adjust for the target if we wake up to an event
self.loop_wait_target = self.frame_time_ns + (@as(i128, @intCast(target)) * 1000);
self.loop_wait_target_can_interrupt = true;
//std.debug.print(" wait {d:6}\n", .{wait_micros});
break :blk wait_micros;
} else {
// trying to hit the target but ran out of time
//std.debug.print(" wait none\n", .{});
break :blk 0;
// if we had a wait target from min_micros leave it
}
};
// Now that we have the waitTime result for this windows, collect the child's ones
// and return the smallest to ensure the window in most pressing need gets satisfaction
var child_win_it = self.child_os_wins.iterator();
while (child_win_it.next()) |remaining_win| {
const w = remaining_win.value_ptr.dvui_win;
const child_wait_event_micros = w.waitTime(remaining_win.value_ptr.end_micros);
wait_time_micros_final = @min(wait_time_micros_final, child_wait_event_micros);
}
return wait_time_micros_final;
}