a73x

33c334d6

Migrate runTextCoverageCompare to FrameLoop

a73x   2026-04-16 11:23

Drops the manual 16ms sleep and prepareRead/cancelRead dance in favor
of the shared readiness primitive. Same visual output; now also
freeze-safe under workspace change.

diff --git a/src/main.zig b/src/main.zig
index 057f19f..0293d64 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -2555,26 +2555,22 @@ fn runTextCoverageCompare(alloc: std.mem.Allocator) !void {
    atlas.dirty = false;
    try ctx.uploadInstances(scene.instances.items);

    const wl_fd = conn.display.getFd();
    var pollfds = [_]std.posix.pollfd{
        .{ .fd = wl_fd, .events = std.posix.POLL.IN, .revents = 0 },
    };
    var frame_loop = frame_loop_mod.FrameLoop.init(
        window.displayOps(conn.display),
        window.surfaceStateView(),
    );
    defer frame_loop.deinit();
    window.frame_loop = &frame_loop;
    defer window.frame_loop = null;

    var last_window_w = window.width;
    var last_window_h = window.height;
    var last_scale: i32 = geom.buffer_scale;

    while (!window.should_close) {
        _ = conn.display.flush();
        if (conn.display.prepareRead()) {
            pollfds[0].revents = 0;
            _ = std.posix.poll(&pollfds, 16) catch {};
            if (pollfds[0].revents & std.posix.POLL.IN != 0) {
                _ = conn.display.readEvents();
            } else {
                conn.display.cancelRead();
            }
        }
        _ = conn.display.dispatchPending();
        try frame_loop.waitForWork(&.{}, 16);

        if (!frame_loop.canRender()) continue;

        const current_scale = window.bufferScale();
        const scale_changed = current_scale != last_scale;
@@ -2592,15 +2588,8 @@ fn runTextCoverageCompare(alloc: std.mem.Allocator) !void {
                    config.font_size_px,
                    current_scale,
                );
                // Rebuild the scene against the fresh atlas.
                scene.deinit(alloc);
                scene = try buildTextCoverageCompareScene(alloc, &face, &atlas);

                // Do NOT touch window.width/window.height here — those reflect the
                // compositor's configured surface size (from xdg_toplevel.configure).
                // Overwriting them forced sway to non-integer-scale our buffer to fit
                // its tile, which was the actual cause of the residual fuzz.

                window.surface.setBufferScale(geom.buffer_scale);
                try ctx.uploadAtlas(atlas.pixels);
                atlas.dirty = false;
@@ -2611,7 +2600,6 @@ fn runTextCoverageCompare(alloc: std.mem.Allocator) !void {
            const buf_w = window.width * @as(u32, @intCast(geom.buffer_scale));
            const buf_h = window.height * @as(u32, @intCast(geom.buffer_scale));
            try ctx.recreateSwapchain(buf_w, buf_h);

            last_window_w = window.width;
            last_window_h = window.height;
        }
@@ -2630,13 +2618,13 @@ fn runTextCoverageCompare(alloc: std.mem.Allocator) !void {
                try ctx.recreateSwapchain(buf_w, buf_h);
                last_window_w = window.width;
                last_window_h = window.height;
                frame_loop.forceArm();
                continue;
            },
            else => return err,
        };

        _ = conn.display.flush();
        std.Thread.sleep(16 * std.time.ns_per_ms);
        try frame_loop.commitRender();
    }

    _ = try ctx.vkd.deviceWaitIdle(ctx.device);