From 72002a79cc77e5cfa66a9a81241741fd51d5c491 Mon Sep 17 00:00:00 2001 From: Simen Kirkvik Date: Mon, 16 Mar 2026 13:59:21 +0100 Subject: [PATCH] Try making frame flipping more robust --- src/main.zig | 145 +++++++++++++++++++++++++++++---------------------- src/root.zig | 3 +- 2 files changed, 85 insertions(+), 63 deletions(-) diff --git a/src/main.zig b/src/main.zig index f60dff0..971a672 100644 --- a/src/main.zig +++ b/src/main.zig @@ -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; diff --git a/src/root.zig b/src/root.zig index cc1a9a9..0b1ae74 100644 --- a/src/root.zig +++ b/src/root.zig @@ -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"); }