Try making frame flipping more robust

This commit is contained in:
2026-03-16 13:59:21 +01:00
parent 4e27783354
commit 72002a79cc
2 changed files with 85 additions and 63 deletions
+84 -61
View File
@@ -43,11 +43,13 @@ const WaylandState = struct {
running: bool,
configured: bool,
display: ?*c.wl_display,
compositor: ?*c.wl_compositor,
seat: ?*c.wl_seat,
shared_memory: ?*c.wl_shm,
xdg_wm_base: ?*c.xdg_wm_base,
surface: ?*c.wl_surface,
buffer: ?*c.wl_buffer,
xkb_context: ?*c.xkb_context,
xkb_keymap: ?*c.xkb_keymap,
@@ -56,7 +58,7 @@ const WaylandState = struct {
allocator: std.mem.Allocator,
resize: bool,
frame_ready: bool,
frame_commited: bool,
window_resized: bool,
window_width: i32,
@@ -120,8 +122,10 @@ fn wlFrameHandleDone(data: ?*anyopaque, callback: ?*c.wl_callback, callback_data
const new_callback: ?*c.wl_callback = c.wl_surface_frame(wl_state.surface);
_ = c.wl_callback_add_listener(new_callback, &frame_callback_listener, wl_state);
// std.debug.print("[Wayland] Frame ready\n", .{});
wl_state.frame_ready = true;
std.debug.print("[Wayland] Frame ready\n", .{});
wl_state.frame_commited = true;
}
const frame_callback_listener: c.wl_callback_listener = .{
@@ -811,6 +815,39 @@ const registry_listener: c.wl_registry_listener = .{
.global_remove = registryHandleGlobalRemove,
};
fn pollEvents(wl_state: *WaylandState, fds: *[1]linux.pollfd) void {
while (true) {
const cont = std.posix.poll(fds, 0) catch |err| {
switch (err) {
error.Unexpected => {
std.debug.print("Poll error: Unexpected.\n", .{});
},
error.SystemResources => {
std.debug.print("Poll error: SystemResources: Kernel has no space for table allocations.\n", .{});
},
else => {
std.debug.print("Poll error: network stuff idk.\n", .{});
},
}
return;
};
if (cont == 0) break;
for (fds) |fd| {
if (fd.revents & std.posix.POLL.IN > 0) {
const ret = c.wl_display_dispatch(wl_state.display);
if (ret < 0) {
std.debug.print("Dispatch error {d}.\n", .{ret});
wl_state.running = false;
return;
}
}
}
}
}
pub fn main() u8 {
const display = c.wl_display_connect(null);
if (display == null) {
@@ -823,6 +860,7 @@ pub fn main() u8 {
std.debug.print("[Wayland] Connection established.\n", .{});
var wl_state: WaylandState = undefined;
wl_state.display = display;
wl_state.xkb_context = null;
wl_state.xkb_state = null;
wl_state.xkb_keymap = null;
@@ -884,7 +922,6 @@ pub fn main() u8 {
var shared_memory_pool: ?*c.wl_shm_pool = undefined;
var shared_memory_pool_data: []u8 = undefined;
var wl_buffer: *c.wl_buffer = undefined;
const monitor_update_hz: f64 = 60;
const game_update_hz: f64 = monitor_update_hz;
@@ -913,14 +950,15 @@ pub fn main() u8 {
const ret: c_int = c.wl_callback_add_listener(cb, &frame_callback_listener, &wl_state);
std.debug.print("[Wayland] Added frame callback with ret {d}.\n", .{ret});
}
wl_state.frame_ready = true;
var timer = std.time.Timer.start() catch {
std.debug.print("[Wayland] ERROR: timer not supported.\n", .{});
return 1;
};
var fds: [1]std.os.linux.pollfd = .{
var frame_count: u32 = 0;
var fds: [1]linux.pollfd = .{
.{
.fd = wl_fd,
.events = std.posix.POLL.IN,
@@ -928,36 +966,7 @@ pub fn main() u8 {
},
};
while (wl_state.running) {
outer: while (true) {
const cont = std.posix.poll(&fds, 0) catch |err| {
switch (err) {
error.Unexpected => {
std.debug.print("Poll error: Unexpected.\n", .{});
},
error.SystemResources => {
std.debug.print("Poll error: SystemResources: Kernel has no space for table allocations.\n", .{});
},
else => {
std.debug.print("Poll error: network stuff idk.\n", .{});
},
}
return 0;
};
if (cont == 0) break;
for (fds) |fd| {
if (fd.revents & std.posix.POLL.IN > 0) {
const ret = c.wl_display_dispatch(display);
if (ret < 0) {
std.debug.print("Dispatch error {d}.\n", .{ret});
wl_state.running = false;
break :outer;
}
}
}
}
pollEvents(&wl_state, &fds);
if (wl_state.resize) {
const new_pool = createSharedMemoryPool(wl_state) catch {
@@ -983,8 +992,8 @@ pub fn main() u8 {
stride,
c.WL_SHM_FORMAT_ARGB8888,
);
if (buffer != null) {
wl_buffer = buffer.?;
if (buffer) |buf| {
wl_state.buffer = buf;
} else {
std.debug.print("[Wayland] Error creating buffer!\n", .{});
wl_state.running = false;
@@ -993,53 +1002,67 @@ pub fn main() u8 {
wl_state.resize = false;
wl_state.window_resized = false;
wl_state.frame_commited = true;
c.wl_surface_attach(wl_state.surface, wl_state.buffer, 0, 0);
c.wl_surface_damage_buffer(wl_state.surface, 0, 0, wl_state.window_width, wl_state.window_height);
c.wl_surface_commit(wl_state.surface);
}
}
const render_start_time: u64 = timer.read();
if (wl_state.frame_ready) {
wl_state.app_input.delta_time = frame_target_time_s;
wl_state.app_input.delta_time = frame_target_time_s;
const app_offscreen_buffer: puzzle.platform.OffscreenBuffer = .{
.width = wl_state.window_width,
.height = wl_state.window_height,
.stride = wl_state.window_width * 4,
.data = shared_memory_pool_data,
};
puzzle.updateAndRender(&app_memory, app_offscreen_buffer, &wl_state.app_input);
}
const app_offscreen_buffer: puzzle.platform.OffscreenBuffer = .{
.width = wl_state.window_width,
.height = wl_state.window_height,
.stride = wl_state.window_width * 4,
.data = shared_memory_pool_data,
};
wl_state.app_input.controls = puzzle.platform.Controls{ .key = old_key };
puzzle.updateAndRender(&app_memory, app_offscreen_buffer, &wl_state.app_input);
const render_end_time: u64 = timer.read();
const render_time = render_end_time - render_start_time;
const render_time_ms: f64 = @as(f64, @floatFromInt(render_time)) / @as(f64, @floatFromInt(std.time.ns_per_ms));
std.debug.print("[Wayland] Render: {d:2.3}ms.\n", .{ render_time_ms });
var sleep_time: u64 = frame_target_time_ns;
if (render_time < frame_target_time_ns) {
sleep_time -= render_time;
const sleep_time_ms: f64 = @as(f64, @floatFromInt(sleep_time)) / @as(f64, @floatFromInt(std.time.ns_per_ms));
std.debug.print("[Wayland] Sleeping: {d:2.3}ms.\n", .{ sleep_time_ms });
std.posix.nanosleep(0, sleep_time);
} else {
sleep_time = 0;
std.debug.print("MISSED FRAME\n", .{});
std.debug.print("[Wayland] MISSED FRAME\n", .{});
}
if (wl_state.frame_ready) {
c.wl_surface_attach(wl_surface, wl_buffer, 0, 0);
c.wl_surface_damage_buffer(wl_surface, 0, 0, wl_state.window_width, wl_state.window_height);
c.wl_surface_commit(wl_surface);
wl_state.frame_ready = false;
}
c.wl_surface_attach(wl_state.surface, wl_state.buffer, 0, 0);
c.wl_surface_damage_buffer(wl_state.surface, 0, 0, wl_state.window_width, wl_state.window_height);
c.wl_surface_commit(wl_state.surface);
_ = c.wl_display_roundtrip(display);
var wait_frame: u32 = 0;
while (!wl_state.frame_commited) {
_ = c.wl_display_dispatch(display);
wait_frame += 1;
}
std.debug.print("[Wayland] Waited for {d} frame(s).\n", .{wait_frame});
wl_state.frame_commited = false;
// std.posix.nanosleep(0, std.time.ns_per_ms * 32);
const current = timer.lap();
const current_ms: f64 = @as(f64, @floatFromInt(current)) / std.time.ns_per_ms;
const sleep_time_ms: f64 = @as(f64, @floatFromInt(sleep_time)) / @as(f64, @floatFromInt(std.time.ns_per_ms));
std.debug.print(
"Render: {d:2.3}ms, Sleeping: {d:2.3}ms, Frame: {d:.3}ms.\n",
.{ render_time_ms, sleep_time_ms, current_ms },
);
// std.debug.print(
// "[Wayland] Render: {d:2.3}ms, Sleeping: {d:2.3}ms, Frame: {d:.3}ms.\n",
// .{ render_time_ms, sleep_time_ms, current_ms },
// );
std.debug.print("[Wayland] Frame done: {d:.3}ms.\n", .{ current_ms });
frame_count += 1;
}
return 0;
+1 -2
View File
@@ -821,6 +821,7 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
}
}
// TODO: Animate bricks to new position
if (clear_pressed) {
if (valid_pos) {
const board = state.board;
@@ -1024,9 +1025,7 @@ pub fn updateAndRender(memory: *platform.AppMemory, buffer: platform.OffscreenBu
const height = buffer.height - state.font.glyph_height;
drawString(buffer, state.font, V2.initI32(8, height - 2 * state.font.glyph_height + 8), scale, "Clear: q");
drawString(buffer, state.font, V2.initI32(8, height - state.font.glyph_height), scale, "Flip: w");
drawString(buffer, state.font, V2.initI32(8, height - 8), scale, "Rotate: e");
}