917999fd
Fix dirty-row packing contract
a73x 2026-04-08 18:41
Commit message
src/main.zig
| Old | New | ||
|---|---|---|---|
| @@ -640,11 +640,38 @@ test "repackRowCaches assigns contiguous offsets" { | |||
| 640 | var packed_instances: std.ArrayListUnmanaged(renderer.Instance) = .empty; | 640 | var packed_instances: std.ArrayListUnmanaged(renderer.Instance) = .empty; |
| 641 | defer packed_instances.deinit(std.testing.allocator); | 641 | defer packed_instances.deinit(std.testing.allocator); |
| 642 | 642 | ||
| 643 | const total = try repackRowCaches(std.testing.allocator, &packed_instances, &rows, &.{}); | 643 | var cursor_instances = try makeTestInstances(std.testing.allocator, 1); |
| 644 | defer cursor_instances.deinit(std.testing.allocator); | ||
| 645 | |||
| 646 | rows[0].instances.items[0].cell_pos[1] = 10.0; | ||
| 647 | rows[0].instances.items[1].cell_pos[1] = 10.0; | ||
| 648 | rows[1].instances.items[0].cell_pos[1] = 20.0; | ||
| 649 | rows[1].instances.items[1].cell_pos[1] = 20.0; | ||
| 650 | rows[1].instances.items[2].cell_pos[1] = 20.0; | ||
| 651 | cursor_instances.items[0].cell_pos[0] = 99.0; | ||
| 652 | cursor_instances.items[0].cell_pos[1] = 99.0; | ||
| 653 | |||
| 654 | const packed_result = try repackRowCaches( | ||
| 655 | std.testing.allocator, | ||
| 656 | &packed_instances, | ||
| 657 | &rows, | ||
| 658 | cursor_instances.items, | ||
| 659 | ); | ||
| 644 | 660 | ||
| 645 | try std.testing.expectEqual(@as(u32, 0), rows[0].gpu_offset_instances); | 661 | try std.testing.expectEqual(@as(u32, 0), rows[0].gpu_offset_instances); |
| 662 | try std.testing.expectEqual(@as(u32, 2), rows[0].gpu_len_instances); | ||
| 646 | try std.testing.expectEqual(@as(u32, 2), rows[1].gpu_offset_instances); | 663 | try std.testing.expectEqual(@as(u32, 2), rows[1].gpu_offset_instances); |
| 647 | try std.testing.expectEqual(@as(u32, 5), total); | 664 | try std.testing.expectEqual(@as(u32, 3), rows[1].gpu_len_instances); |
| 665 | try std.testing.expectEqual(@as(u32, 6), packed_result.total_instances); | ||
| 666 | try std.testing.expectEqual(@as(u32, 5), packed_result.cursor_offset_instances); | ||
| 667 | try std.testing.expectEqual(@as(u32, 1), packed_result.cursor_len_instances); | ||
| 668 | try std.testing.expectEqual(@as(usize, 6), packed_instances.items.len); | ||
| 669 | try std.testing.expectEqualDeep([2]f32{ 0.0, 10.0 }, packed_instances.items[0].cell_pos); | ||
| 670 | try std.testing.expectEqualDeep([2]f32{ 1.0, 10.0 }, packed_instances.items[1].cell_pos); | ||
| 671 | try std.testing.expectEqualDeep([2]f32{ 0.0, 20.0 }, packed_instances.items[2].cell_pos); | ||
| 672 | try std.testing.expectEqualDeep([2]f32{ 1.0, 20.0 }, packed_instances.items[3].cell_pos); | ||
| 673 | try std.testing.expectEqualDeep([2]f32{ 2.0, 20.0 }, packed_instances.items[4].cell_pos); | ||
| 674 | try std.testing.expectEqualDeep([2]f32{ 99.0, 99.0 }, packed_instances.items[5].cell_pos); | ||
| 648 | } | 675 | } |
| 649 | 676 | ||
| 650 | test "markLayoutDirtyOnLenChange returns true when row length changes" { | 677 | test "markLayoutDirtyOnLenChange returns true when row length changes" { |
| @@ -652,6 +679,46 @@ test "markLayoutDirtyOnLenChange returns true when row length changes" { | |||
| 652 | try std.testing.expect(!markLayoutDirtyOnLenChange(3, 3)); | 679 | try std.testing.expect(!markLayoutDirtyOnLenChange(3, 3)); |
| 653 | } | 680 | } |
| 654 | 681 | ||
| 682 | test "repackRowCaches keeps cursor span explicit for empty and non-empty cursor instances" { | ||
| 683 | var rows = [_]RowInstanceCache{ | ||
| 684 | .{ | ||
| 685 | .instances = try makeTestInstances(std.testing.allocator, 1), | ||
| 686 | }, | ||
| 687 | }; | ||
| 688 | defer for (&rows) |*row| row.instances.deinit(std.testing.allocator); | ||
| 689 | |||
| 690 | var packed_instances: std.ArrayListUnmanaged(renderer.Instance) = .empty; | ||
| 691 | defer packed_instances.deinit(std.testing.allocator); | ||
| 692 | |||
| 693 | const empty_cursor_result = try repackRowCaches(std.testing.allocator, &packed_instances, &rows, &.{}); | ||
| 694 | try std.testing.expectEqual(@as(u32, 1), empty_cursor_result.total_instances); | ||
| 695 | try std.testing.expectEqual(@as(u32, 1), empty_cursor_result.cursor_offset_instances); | ||
| 696 | try std.testing.expectEqual(@as(u32, 0), empty_cursor_result.cursor_len_instances); | ||
| 697 | |||
| 698 | var cursor_instances = try makeTestInstances(std.testing.allocator, 2); | ||
| 699 | defer cursor_instances.deinit(std.testing.allocator); | ||
| 700 | |||
| 701 | cursor_instances.items[0].cell_pos[0] = 7.0; | ||
| 702 | cursor_instances.items[0].cell_pos[1] = 8.0; | ||
| 703 | cursor_instances.items[1].cell_pos[0] = 9.0; | ||
| 704 | cursor_instances.items[1].cell_pos[1] = 10.0; | ||
| 705 | |||
| 706 | const non_empty_cursor_result = try repackRowCaches( | ||
| 707 | std.testing.allocator, | ||
| 708 | &packed_instances, | ||
| 709 | &rows, | ||
| 710 | cursor_instances.items, | ||
| 711 | ); | ||
| 712 | |||
| 713 | try std.testing.expectEqual(@as(u32, 3), non_empty_cursor_result.total_instances); | ||
| 714 | try std.testing.expectEqual(@as(u32, 1), non_empty_cursor_result.cursor_offset_instances); | ||
| 715 | try std.testing.expectEqual(@as(u32, 2), non_empty_cursor_result.cursor_len_instances); | ||
| 716 | try std.testing.expectEqual(@as(usize, 3), packed_instances.items.len); | ||
| 717 | try std.testing.expectEqualDeep([2]f32{ 0.0, 0.0 }, packed_instances.items[0].cell_pos); | ||
| 718 | try std.testing.expectEqualDeep([2]f32{ 7.0, 8.0 }, packed_instances.items[1].cell_pos); | ||
| 719 | try std.testing.expectEqualDeep([2]f32{ 9.0, 10.0 }, packed_instances.items[2].cell_pos); | ||
| 720 | } | ||
| 721 | |||
| 655 | const RowInstanceCache = struct { | 722 | const RowInstanceCache = struct { |
| 656 | instances: std.ArrayListUnmanaged(renderer.Instance) = .empty, | 723 | instances: std.ArrayListUnmanaged(renderer.Instance) = .empty, |
| 657 | gpu_offset_instances: u32 = 0, | 724 | gpu_offset_instances: u32 = 0, |
| @@ -662,24 +729,42 @@ const RowInstanceCache = struct { | |||
| 662 | } | 729 | } |
| 663 | }; | 730 | }; |
| 664 | 731 | ||
| 732 | const RowPackResult = struct { | ||
| 733 | total_instances: u32, | ||
| 734 | cursor_offset_instances: u32, | ||
| 735 | cursor_len_instances: u32, | ||
| 736 | }; | ||
| 737 | |||
| 665 | fn repackRowCaches( | 738 | fn repackRowCaches( |
| 666 | alloc: std.mem.Allocator, | 739 | alloc: std.mem.Allocator, |
| 667 | packed_instances: *std.ArrayListUnmanaged(renderer.Instance), | 740 | packed_instances: *std.ArrayListUnmanaged(renderer.Instance), |
| 668 | rows: []RowInstanceCache, | 741 | rows: []RowInstanceCache, |
| 669 | cursor_instances: []const renderer.Instance, | 742 | cursor_instances: []const renderer.Instance, |
| 670 | ) !u32 { | 743 | ) !RowPackResult { |
| 671 | packed_instances.clearRetainingCapacity(); | 744 | packed_instances.clearRetainingCapacity(); |
| 672 | 745 | ||
| 746 | var total_instances: u32 = 0; | ||
| 747 | for (rows) |*row| { | ||
| 748 | try packed_instances.appendSlice(alloc, row.instances.items); | ||
| 749 | total_instances += @intCast(row.instances.items.len); | ||
| 750 | } | ||
| 751 | |||
| 752 | const cursor_offset_instances = total_instances; | ||
| 753 | try packed_instances.appendSlice(alloc, cursor_instances); | ||
| 754 | total_instances += @intCast(cursor_instances.len); | ||
| 755 | |||
| 673 | var offset: u32 = 0; | 756 | var offset: u32 = 0; |
| 674 | for (rows) |*row| { | 757 | for (rows) |*row| { |
| 675 | row.gpu_offset_instances = offset; | 758 | row.gpu_offset_instances = offset; |
| 676 | row.gpu_len_instances = @intCast(row.instances.items.len); | 759 | row.gpu_len_instances = @intCast(row.instances.items.len); |
| 677 | try packed_instances.appendSlice(alloc, row.instances.items); | ||
| 678 | offset += @intCast(row.instances.items.len); | 760 | offset += @intCast(row.instances.items.len); |
| 679 | } | 761 | } |
| 680 | 762 | ||
| 681 | try packed_instances.appendSlice(alloc, cursor_instances); | 763 | return .{ |
| 682 | return offset + @as(u32, @intCast(cursor_instances.len)); | 764 | .total_instances = total_instances, |
| 765 | .cursor_offset_instances = cursor_offset_instances, | ||
| 766 | .cursor_len_instances = @intCast(cursor_instances.len), | ||
| 767 | }; | ||
| 683 | } | 768 | } |
| 684 | 769 | ||
| 685 | fn markLayoutDirtyOnLenChange(old_len: usize, new_len: usize) bool { | 770 | fn markLayoutDirtyOnLenChange(old_len: usize, new_len: usize) bool { |