917999fd
Fix dirty-row packing contract
a73x 2026-04-08 18:41
diff --git a/src/main.zig b/src/main.zig index 11fadd0..39c40e4 100644 --- a/src/main.zig +++ b/src/main.zig @@ -640,11 +640,38 @@ test "repackRowCaches assigns contiguous offsets" { 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, &.{}); var cursor_instances = try makeTestInstances(std.testing.allocator, 1); defer cursor_instances.deinit(std.testing.allocator); rows[0].instances.items[0].cell_pos[1] = 10.0; rows[0].instances.items[1].cell_pos[1] = 10.0; rows[1].instances.items[0].cell_pos[1] = 20.0; rows[1].instances.items[1].cell_pos[1] = 20.0; rows[1].instances.items[2].cell_pos[1] = 20.0; cursor_instances.items[0].cell_pos[0] = 99.0; cursor_instances.items[0].cell_pos[1] = 99.0; const packed_result = try repackRowCaches( std.testing.allocator, &packed_instances, &rows, cursor_instances.items, ); try std.testing.expectEqual(@as(u32, 0), rows[0].gpu_offset_instances); try std.testing.expectEqual(@as(u32, 2), rows[0].gpu_len_instances); try std.testing.expectEqual(@as(u32, 2), rows[1].gpu_offset_instances); try std.testing.expectEqual(@as(u32, 5), total); try std.testing.expectEqual(@as(u32, 3), rows[1].gpu_len_instances); try std.testing.expectEqual(@as(u32, 6), packed_result.total_instances); try std.testing.expectEqual(@as(u32, 5), packed_result.cursor_offset_instances); try std.testing.expectEqual(@as(u32, 1), packed_result.cursor_len_instances); try std.testing.expectEqual(@as(usize, 6), packed_instances.items.len); try std.testing.expectEqualDeep([2]f32{ 0.0, 10.0 }, packed_instances.items[0].cell_pos); try std.testing.expectEqualDeep([2]f32{ 1.0, 10.0 }, packed_instances.items[1].cell_pos); try std.testing.expectEqualDeep([2]f32{ 0.0, 20.0 }, packed_instances.items[2].cell_pos); try std.testing.expectEqualDeep([2]f32{ 1.0, 20.0 }, packed_instances.items[3].cell_pos); try std.testing.expectEqualDeep([2]f32{ 2.0, 20.0 }, packed_instances.items[4].cell_pos); try std.testing.expectEqualDeep([2]f32{ 99.0, 99.0 }, packed_instances.items[5].cell_pos); } test "markLayoutDirtyOnLenChange returns true when row length changes" { @@ -652,6 +679,46 @@ test "markLayoutDirtyOnLenChange returns true when row length changes" { try std.testing.expect(!markLayoutDirtyOnLenChange(3, 3)); } test "repackRowCaches keeps cursor span explicit for empty and non-empty cursor instances" { var rows = [_]RowInstanceCache{ .{ .instances = try makeTestInstances(std.testing.allocator, 1), }, }; 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 empty_cursor_result = try repackRowCaches(std.testing.allocator, &packed_instances, &rows, &.{}); try std.testing.expectEqual(@as(u32, 1), empty_cursor_result.total_instances); try std.testing.expectEqual(@as(u32, 1), empty_cursor_result.cursor_offset_instances); try std.testing.expectEqual(@as(u32, 0), empty_cursor_result.cursor_len_instances); var cursor_instances = try makeTestInstances(std.testing.allocator, 2); defer cursor_instances.deinit(std.testing.allocator); cursor_instances.items[0].cell_pos[0] = 7.0; cursor_instances.items[0].cell_pos[1] = 8.0; cursor_instances.items[1].cell_pos[0] = 9.0; cursor_instances.items[1].cell_pos[1] = 10.0; const non_empty_cursor_result = try repackRowCaches( std.testing.allocator, &packed_instances, &rows, cursor_instances.items, ); try std.testing.expectEqual(@as(u32, 3), non_empty_cursor_result.total_instances); try std.testing.expectEqual(@as(u32, 1), non_empty_cursor_result.cursor_offset_instances); try std.testing.expectEqual(@as(u32, 2), non_empty_cursor_result.cursor_len_instances); try std.testing.expectEqual(@as(usize, 3), packed_instances.items.len); try std.testing.expectEqualDeep([2]f32{ 0.0, 0.0 }, packed_instances.items[0].cell_pos); try std.testing.expectEqualDeep([2]f32{ 7.0, 8.0 }, packed_instances.items[1].cell_pos); try std.testing.expectEqualDeep([2]f32{ 9.0, 10.0 }, packed_instances.items[2].cell_pos); } const RowInstanceCache = struct { instances: std.ArrayListUnmanaged(renderer.Instance) = .empty, gpu_offset_instances: u32 = 0, @@ -662,24 +729,42 @@ const RowInstanceCache = struct { } }; const RowPackResult = struct { total_instances: u32, cursor_offset_instances: u32, cursor_len_instances: u32, }; fn repackRowCaches( alloc: std.mem.Allocator, packed_instances: *std.ArrayListUnmanaged(renderer.Instance), rows: []RowInstanceCache, cursor_instances: []const renderer.Instance, ) !u32 { ) !RowPackResult { packed_instances.clearRetainingCapacity(); var total_instances: u32 = 0; for (rows) |*row| { try packed_instances.appendSlice(alloc, row.instances.items); total_instances += @intCast(row.instances.items.len); } const cursor_offset_instances = total_instances; try packed_instances.appendSlice(alloc, cursor_instances); total_instances += @intCast(cursor_instances.len); 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)); return .{ .total_instances = total_instances, .cursor_offset_instances = cursor_offset_instances, .cursor_len_instances = @intCast(cursor_instances.len), }; } fn markLayoutDirtyOnLenChange(old_len: usize, new_len: usize) bool {