1c051843
Fix cursor cache invalidation contract
a73x 2026-04-08 19:24
Commit message
src/main.zig
| Old | New | ||
|---|---|---|---|
| @@ -711,12 +711,21 @@ test "repackRowCaches keeps cursor span explicit for empty and non-empty cursor | |||
| 711 | try std.testing.expectEqualDeep([2]f32{ 9.0, 10.0 }, packed_instances.items[2].cell_pos); | 711 | try std.testing.expectEqualDeep([2]f32{ 9.0, 10.0 }, packed_instances.items[2].cell_pos); |
| 712 | } | 712 | } |
| 713 | 713 | ||
| 714 | test "rebuildCursorInstances emits visible cursor instance without changing layout state when count is unchanged" { | 714 | test "rebuildCursorInstances invalidates packed cursor state when count is unchanged" { |
| 715 | var cache = RenderCache.empty; | 715 | var cache = RenderCache.empty; |
| 716 | defer cache.deinit(std.testing.allocator); | 716 | defer cache.deinit(std.testing.allocator); |
| 717 | 717 | ||
| 718 | cache.cursor_instances = try makeTestInstances(std.testing.allocator, 1); | 718 | cache.cursor_instances = try makeTestInstances(std.testing.allocator, 1); |
| 719 | cache.cursor_instances.items[0].cell_pos = .{ 99.0, 99.0 }; | 719 | cache.cursor_instances.items[0].cell_pos = .{ 99.0, 99.0 }; |
| 720 | try cache.packed_instances.append(std.testing.allocator, .{ | ||
| 721 | .cell_pos = .{ 11.0, 11.0 }, | ||
| 722 | .glyph_size = .{ 1.0, 1.0 }, | ||
| 723 | .glyph_bearing = .{ 0.0, 0.0 }, | ||
| 724 | .uv_rect = .{ 0.0, 0.0, 0.0, 0.0 }, | ||
| 725 | .fg = .{ 0.0, 0.0, 0.0, 0.0 }, | ||
| 726 | .bg = .{ 0.0, 0.0, 0.0, 0.0 }, | ||
| 727 | }); | ||
| 728 | cache.total_instance_count = 4; | ||
| 720 | cache.layout_dirty = false; | 729 | cache.layout_dirty = false; |
| 721 | 730 | ||
| 722 | var cursor_instances = try makeTestInstances(std.testing.allocator, 1); | 731 | var cursor_instances = try makeTestInstances(std.testing.allocator, 1); |
| @@ -726,22 +735,37 @@ test "rebuildCursorInstances emits visible cursor instance without changing layo | |||
| 726 | const rebuilt = try cache.rebuildCursorInstances(std.testing.allocator, cursor_instances.items); | 735 | const rebuilt = try cache.rebuildCursorInstances(std.testing.allocator, cursor_instances.items); |
| 727 | 736 | ||
| 728 | try std.testing.expect(!rebuilt.len_changed); | 737 | try std.testing.expect(!rebuilt.len_changed); |
| 738 | try std.testing.expect(rebuilt.packed_invalidated); | ||
| 729 | try std.testing.expectEqual(@as(usize, 1), cache.cursor_instances.items.len); | 739 | try std.testing.expectEqual(@as(usize, 1), cache.cursor_instances.items.len); |
| 730 | try std.testing.expectEqualDeep([2]f32{ 4.0, 7.0 }, cache.cursor_instances.items[0].cell_pos); | 740 | try std.testing.expectEqualDeep([2]f32{ 4.0, 7.0 }, cache.cursor_instances.items[0].cell_pos); |
| 731 | try std.testing.expect(!cache.layout_dirty); | 741 | try std.testing.expectEqual(@as(usize, 0), cache.packed_instances.items.len); |
| 742 | try std.testing.expectEqual(@as(u32, 0), cache.total_instance_count); | ||
| 743 | try std.testing.expect(cache.layout_dirty); | ||
| 732 | } | 744 | } |
| 733 | 745 | ||
| 734 | test "rebuildCursorInstances marks layout dirty when cursor instance count changes" { | 746 | test "rebuildCursorInstances invalidates packed cursor state when cursor instance count changes" { |
| 735 | var cache = RenderCache.empty; | 747 | var cache = RenderCache.empty; |
| 736 | defer cache.deinit(std.testing.allocator); | 748 | defer cache.deinit(std.testing.allocator); |
| 737 | 749 | ||
| 738 | cache.cursor_instances = try makeTestInstances(std.testing.allocator, 1); | 750 | cache.cursor_instances = try makeTestInstances(std.testing.allocator, 1); |
| 751 | try cache.packed_instances.append(std.testing.allocator, .{ | ||
| 752 | .cell_pos = .{ 13.0, 13.0 }, | ||
| 753 | .glyph_size = .{ 1.0, 1.0 }, | ||
| 754 | .glyph_bearing = .{ 0.0, 0.0 }, | ||
| 755 | .uv_rect = .{ 0.0, 0.0, 0.0, 0.0 }, | ||
| 756 | .fg = .{ 0.0, 0.0, 0.0, 0.0 }, | ||
| 757 | .bg = .{ 0.0, 0.0, 0.0, 0.0 }, | ||
| 758 | }); | ||
| 759 | cache.total_instance_count = 4; | ||
| 739 | cache.layout_dirty = false; | 760 | cache.layout_dirty = false; |
| 740 | 761 | ||
| 741 | const rebuilt = try cache.rebuildCursorInstances(std.testing.allocator, &.{}); | 762 | const rebuilt = try cache.rebuildCursorInstances(std.testing.allocator, &.{}); |
| 742 | 763 | ||
| 743 | try std.testing.expect(rebuilt.len_changed); | 764 | try std.testing.expect(rebuilt.len_changed); |
| 765 | try std.testing.expect(rebuilt.packed_invalidated); | ||
| 744 | try std.testing.expectEqual(@as(usize, 0), cache.cursor_instances.items.len); | 766 | try std.testing.expectEqual(@as(usize, 0), cache.cursor_instances.items.len); |
| 767 | try std.testing.expectEqual(@as(usize, 0), cache.packed_instances.items.len); | ||
| 768 | try std.testing.expectEqual(@as(u32, 0), cache.total_instance_count); | ||
| 745 | try std.testing.expect(cache.layout_dirty); | 769 | try std.testing.expect(cache.layout_dirty); |
| 746 | } | 770 | } |
| 747 | 771 | ||
| @@ -1051,16 +1075,18 @@ const RenderCache = struct { | |||
| 1051 | self: *RenderCache, | 1075 | self: *RenderCache, |
| 1052 | alloc: std.mem.Allocator, | 1076 | alloc: std.mem.Allocator, |
| 1053 | cursor_instances: []const renderer.Instance, | 1077 | cursor_instances: []const renderer.Instance, |
| 1054 | ) !RowRebuildResult { | 1078 | ) !CursorRebuildResult { |
| 1055 | const old_len = self.cursor_instances.items.len; | 1079 | const old_len = self.cursor_instances.items.len; |
| 1056 | self.cursor_instances.clearRetainingCapacity(); | 1080 | self.cursor_instances.clearRetainingCapacity(); |
| 1057 | try self.cursor_instances.appendSlice(alloc, cursor_instances); | 1081 | try self.cursor_instances.appendSlice(alloc, cursor_instances); |
| 1058 | 1082 | ||
| 1059 | const len_changed = markLayoutDirtyOnLenChange(old_len, self.cursor_instances.items.len); | 1083 | self.packed_instances.clearRetainingCapacity(); |
| 1060 | if (len_changed) self.layout_dirty = true; | 1084 | self.total_instance_count = 0; |
| 1085 | self.layout_dirty = true; | ||
| 1061 | 1086 | ||
| 1062 | return .{ | 1087 | return .{ |
| 1063 | .len_changed = len_changed, | 1088 | .len_changed = markLayoutDirtyOnLenChange(old_len, self.cursor_instances.items.len), |
| 1089 | .packed_invalidated = true, | ||
| 1064 | }; | 1090 | }; |
| 1065 | } | 1091 | } |
| 1066 | }; | 1092 | }; |
| @@ -1075,6 +1101,11 @@ const RowRebuildResult = struct { | |||
| 1075 | len_changed: bool, | 1101 | len_changed: bool, |
| 1076 | }; | 1102 | }; |
| 1077 | 1103 | ||
| 1104 | const CursorRebuildResult = struct { | ||
| 1105 | len_changed: bool, | ||
| 1106 | packed_invalidated: bool, | ||
| 1107 | }; | ||
| 1108 | |||
| 1078 | fn repackRowCaches( | 1109 | fn repackRowCaches( |
| 1079 | alloc: std.mem.Allocator, | 1110 | alloc: std.mem.Allocator, |
| 1080 | packed_instances: *std.ArrayListUnmanaged(renderer.Instance), | 1111 | packed_instances: *std.ArrayListUnmanaged(renderer.Instance), |