1c051843
Fix cursor cache invalidation contract
a73x 2026-04-08 19:24
diff --git a/src/main.zig b/src/main.zig index 36cb8a9..08690d5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -711,12 +711,21 @@ test "repackRowCaches keeps cursor span explicit for empty and non-empty cursor try std.testing.expectEqualDeep([2]f32{ 9.0, 10.0 }, packed_instances.items[2].cell_pos); } test "rebuildCursorInstances emits visible cursor instance without changing layout state when count is unchanged" { test "rebuildCursorInstances invalidates packed cursor state when count is unchanged" { var cache = RenderCache.empty; defer cache.deinit(std.testing.allocator); cache.cursor_instances = try makeTestInstances(std.testing.allocator, 1); cache.cursor_instances.items[0].cell_pos = .{ 99.0, 99.0 }; try cache.packed_instances.append(std.testing.allocator, .{ .cell_pos = .{ 11.0, 11.0 }, .glyph_size = .{ 1.0, 1.0 }, .glyph_bearing = .{ 0.0, 0.0 }, .uv_rect = .{ 0.0, 0.0, 0.0, 0.0 }, .fg = .{ 0.0, 0.0, 0.0, 0.0 }, .bg = .{ 0.0, 0.0, 0.0, 0.0 }, }); cache.total_instance_count = 4; cache.layout_dirty = false; var cursor_instances = try makeTestInstances(std.testing.allocator, 1); @@ -726,22 +735,37 @@ test "rebuildCursorInstances emits visible cursor instance without changing layo const rebuilt = try cache.rebuildCursorInstances(std.testing.allocator, cursor_instances.items); try std.testing.expect(!rebuilt.len_changed); try std.testing.expect(rebuilt.packed_invalidated); try std.testing.expectEqual(@as(usize, 1), cache.cursor_instances.items.len); try std.testing.expectEqualDeep([2]f32{ 4.0, 7.0 }, cache.cursor_instances.items[0].cell_pos); try std.testing.expect(!cache.layout_dirty); try std.testing.expectEqual(@as(usize, 0), cache.packed_instances.items.len); try std.testing.expectEqual(@as(u32, 0), cache.total_instance_count); try std.testing.expect(cache.layout_dirty); } test "rebuildCursorInstances marks layout dirty when cursor instance count changes" { test "rebuildCursorInstances invalidates packed cursor state when cursor instance count changes" { var cache = RenderCache.empty; defer cache.deinit(std.testing.allocator); cache.cursor_instances = try makeTestInstances(std.testing.allocator, 1); try cache.packed_instances.append(std.testing.allocator, .{ .cell_pos = .{ 13.0, 13.0 }, .glyph_size = .{ 1.0, 1.0 }, .glyph_bearing = .{ 0.0, 0.0 }, .uv_rect = .{ 0.0, 0.0, 0.0, 0.0 }, .fg = .{ 0.0, 0.0, 0.0, 0.0 }, .bg = .{ 0.0, 0.0, 0.0, 0.0 }, }); cache.total_instance_count = 4; cache.layout_dirty = false; const rebuilt = try cache.rebuildCursorInstances(std.testing.allocator, &.{}); try std.testing.expect(rebuilt.len_changed); try std.testing.expect(rebuilt.packed_invalidated); try std.testing.expectEqual(@as(usize, 0), cache.cursor_instances.items.len); try std.testing.expectEqual(@as(usize, 0), cache.packed_instances.items.len); try std.testing.expectEqual(@as(u32, 0), cache.total_instance_count); try std.testing.expect(cache.layout_dirty); } @@ -1051,16 +1075,18 @@ const RenderCache = struct { self: *RenderCache, alloc: std.mem.Allocator, cursor_instances: []const renderer.Instance, ) !RowRebuildResult { ) !CursorRebuildResult { const old_len = self.cursor_instances.items.len; self.cursor_instances.clearRetainingCapacity(); try self.cursor_instances.appendSlice(alloc, cursor_instances); const len_changed = markLayoutDirtyOnLenChange(old_len, self.cursor_instances.items.len); if (len_changed) self.layout_dirty = true; self.packed_instances.clearRetainingCapacity(); self.total_instance_count = 0; self.layout_dirty = true; return .{ .len_changed = len_changed, .len_changed = markLayoutDirtyOnLenChange(old_len, self.cursor_instances.items.len), .packed_invalidated = true, }; } }; @@ -1075,6 +1101,11 @@ const RowRebuildResult = struct { len_changed: bool, }; const CursorRebuildResult = struct { len_changed: bool, packed_invalidated: bool, }; fn repackRowCaches( alloc: std.mem.Allocator, packed_instances: *std.ArrayListUnmanaged(renderer.Instance),