a73x

7a8bf92e

Add render cache data structures

a73x   2026-04-08 18:52

Commit message
Add render cache data structures

src/main.zig
Old New
@@ -719,6 +719,56 @@ test "repackRowCaches keeps cursor span explicit for empty and non-empty cursor
719 try std.testing.expectEqualDeep([2]f32{ 9.0, 10.0 }, packed_instances.items[2].cell_pos); 719 try std.testing.expectEqualDeep([2]f32{ 9.0, 10.0 }, packed_instances.items[2].cell_pos);
720 } 720 }
721 721
722 test "RenderCache resizeRows preserves surviving row caches" {
723 var cache = RenderCache.empty;
724 defer cache.deinit(std.testing.allocator);
725
726 try cache.resizeRows(std.testing.allocator, 2);
727 cache.rows[0].instances = try makeTestInstances(std.testing.allocator, 1);
728 cache.rows[0].gpu_offset_instances = 17;
729 cache.rows[0].gpu_len_instances = 1;
730
731 try cache.resizeRows(std.testing.allocator, 3);
732 try std.testing.expectEqual(@as(usize, 3), cache.rows.len);
733 try std.testing.expectEqual(@as(usize, 1), cache.rows[0].instances.items.len);
734 try std.testing.expectEqual(@as(u32, 17), cache.rows[0].gpu_offset_instances);
735 try std.testing.expectEqual(@as(u32, 1), cache.rows[0].gpu_len_instances);
736 try std.testing.expectEqual(@as(usize, 0), cache.rows[2].instances.items.len);
737
738 try cache.resizeRows(std.testing.allocator, 1);
739 try std.testing.expectEqual(@as(usize, 1), cache.rows.len);
740 try std.testing.expectEqual(@as(usize, 1), cache.rows[0].instances.items.len);
741 try std.testing.expectEqual(@as(u32, 17), cache.rows[0].gpu_offset_instances);
742 try std.testing.expectEqual(@as(u32, 1), cache.rows[0].gpu_len_instances);
743 }
744
745 test "RenderCache deinit resets fields after releasing row storage" {
746 var cache = RenderCache.empty;
747
748 try cache.resizeRows(std.testing.allocator, 2);
749 cache.rows[0].instances = try makeTestInstances(std.testing.allocator, 2);
750 cache.rows[1].instances = try makeTestInstances(std.testing.allocator, 1);
751
752 var cursor_instances = try makeTestInstances(std.testing.allocator, 1);
753 defer cursor_instances.deinit(std.testing.allocator);
754 try cache.cursor_instances.append(std.testing.allocator, cursor_instances.items[0]);
755
756 var packed_instances = try makeTestInstances(std.testing.allocator, 1);
757 defer packed_instances.deinit(std.testing.allocator);
758 try cache.packed_instances.append(std.testing.allocator, packed_instances.items[0]);
759
760 cache.total_instance_count = 3;
761 cache.layout_dirty = false;
762
763 cache.deinit(std.testing.allocator);
764
765 try std.testing.expectEqual(@as(usize, 0), cache.rows.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);
769 try std.testing.expect(cache.layout_dirty);
770 }
771
722 const RowInstanceCache = struct { 772 const RowInstanceCache = struct {
723 instances: std.ArrayListUnmanaged(renderer.Instance) = .empty, 773 instances: std.ArrayListUnmanaged(renderer.Instance) = .empty,
724 gpu_offset_instances: u32 = 0, 774 gpu_offset_instances: u32 = 0,
@@ -726,6 +776,65 @@ const RowInstanceCache = struct {
726 776
727 fn deinit(self: *RowInstanceCache, alloc: std.mem.Allocator) void { 777 fn deinit(self: *RowInstanceCache, alloc: std.mem.Allocator) void {
728 self.instances.deinit(alloc); 778 self.instances.deinit(alloc);
779 self.* = .{};
780 }
781 };
782
783 const RenderCache = struct {
784 rows: []RowInstanceCache = &.{},
785 cursor_instances: std.ArrayListUnmanaged(renderer.Instance) = .empty,
786 packed_instances: std.ArrayListUnmanaged(renderer.Instance) = .empty,
787 total_instance_count: u32 = 0,
788 layout_dirty: bool = true,
789
790 const empty: RenderCache = .{};
791
792 fn resizeRows(self: *RenderCache, alloc: std.mem.Allocator, row_count: usize) !void {
793 if (self.rows.len == row_count) return;
794 if (row_count == 0) {
795 const old_rows = self.rows;
796 if (old_rows.len > 0) {
797 var row_idx: usize = 0;
798 while (row_idx < old_rows.len) : (row_idx += 1) {
799 old_rows[row_idx].deinit(alloc);
800 }
801 alloc.free(old_rows);
802 }
803 self.rows = &.{};
804 return;
805 }
806
807 const old_rows = self.rows;
808 var new_rows = try alloc.alloc(RowInstanceCache, row_count);
809 for (new_rows) |*row| row.* = .{};
810
811 const copy_len = @min(old_rows.len, row_count);
812 if (copy_len > 0) {
813 @memcpy(new_rows[0..copy_len], old_rows[0..copy_len]);
814 }
815
816 if (row_count < old_rows.len) {
817 var row_idx = row_count;
818 while (row_idx < old_rows.len) : (row_idx += 1) {
819 old_rows[row_idx].deinit(alloc);
820 }
821 }
822
823 if (old_rows.len > 0) {
824 alloc.free(old_rows);
825 }
826
827 self.rows = new_rows;
828 }
829
830 fn deinit(self: *RenderCache, alloc: std.mem.Allocator) void {
831 for (self.rows) |*row| row.deinit(alloc);
832 if (self.rows.len > 0) {
833 alloc.free(self.rows);
834 }
835 self.cursor_instances.deinit(alloc);
836 self.packed_instances.deinit(alloc);
837 self.* = .{};
729 } 838 }
730 }; 839 };
731 840