a73x

f60856b1

Add dirty-row packing helpers

a73x   2026-04-08 18:32


diff --git a/src/main.zig b/src/main.zig
index 413a922..11fadd0 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -622,6 +622,92 @@ test "planRowRefresh rebuilds cursor when only column changes on same row" {
    try std.testing.expectEqual(@as(usize, 0), plan.rows_to_rebuild.count());
}

test "repackRowCaches assigns contiguous offsets" {
    var rows = [_]RowInstanceCache{
        .{
            .instances = try makeTestInstances(std.testing.allocator, 2),
            .gpu_offset_instances = 99,
            .gpu_len_instances = 0,
        },
        .{
            .instances = try makeTestInstances(std.testing.allocator, 3),
            .gpu_offset_instances = 99,
            .gpu_len_instances = 0,
        },
    };
    defer for (&rows) |*row| row.instances.deinit(std.testing.allocator);

    var packed_instances: std.ArrayListUnmanaged(renderer.Instance) = .empty;
    defer packed_instances.deinit(std.testing.allocator);

    const total = try repackRowCaches(std.testing.allocator, &packed_instances, &rows, &.{});

    try std.testing.expectEqual(@as(u32, 0), rows[0].gpu_offset_instances);
    try std.testing.expectEqual(@as(u32, 2), rows[1].gpu_offset_instances);
    try std.testing.expectEqual(@as(u32, 5), total);
}

test "markLayoutDirtyOnLenChange returns true when row length changes" {
    try std.testing.expect(markLayoutDirtyOnLenChange(2, 3));
    try std.testing.expect(!markLayoutDirtyOnLenChange(3, 3));
}

const RowInstanceCache = struct {
    instances: std.ArrayListUnmanaged(renderer.Instance) = .empty,
    gpu_offset_instances: u32 = 0,
    gpu_len_instances: u32 = 0,

    fn deinit(self: *RowInstanceCache, alloc: std.mem.Allocator) void {
        self.instances.deinit(alloc);
    }
};

fn repackRowCaches(
    alloc: std.mem.Allocator,
    packed_instances: *std.ArrayListUnmanaged(renderer.Instance),
    rows: []RowInstanceCache,
    cursor_instances: []const renderer.Instance,
) !u32 {
    packed_instances.clearRetainingCapacity();

    var offset: u32 = 0;
    for (rows) |*row| {
        row.gpu_offset_instances = offset;
        row.gpu_len_instances = @intCast(row.instances.items.len);
        try packed_instances.appendSlice(alloc, row.instances.items);
        offset += @intCast(row.instances.items.len);
    }

    try packed_instances.appendSlice(alloc, cursor_instances);
    return offset + @as(u32, @intCast(cursor_instances.len));
}

fn markLayoutDirtyOnLenChange(old_len: usize, new_len: usize) bool {
    return old_len != new_len;
}

fn makeTestInstances(
    alloc: std.mem.Allocator,
    count: usize,
) !std.ArrayListUnmanaged(renderer.Instance) {
    var instances: std.ArrayListUnmanaged(renderer.Instance) = .empty;
    try instances.ensureTotalCapacity(alloc, count);

    var i: usize = 0;
    while (i < count) : (i += 1) {
        try instances.append(alloc, .{
            .cell_pos = .{ @floatFromInt(i), 0.0 },
            .glyph_size = .{ 1.0, 1.0 },
            .glyph_bearing = .{ 0.0, 0.0 },
            .uv_rect = .{ 0.0, 0.0, 1.0, 1.0 },
            .fg = .{ 1.0, 1.0, 1.0, 1.0 },
            .bg = .{ 0.0, 0.0, 0.0, 1.0 },
        });
    }

    return instances;
}

fn runDrawSmokeTest(alloc: std.mem.Allocator) !void {
    var conn = try wayland_client.Connection.init();
    defer conn.deinit();