a73x

2fd9a6ab

Fix partial upload fallback contract

a73x   2026-04-08 19:38

Commit message
Fix partial upload fallback contract

src/renderer.zig
Old New
@@ -359,19 +359,23 @@ fn writeInstanceRange(
359 target: []Instance, 359 target: []Instance,
360 offset_instances: u32, 360 offset_instances: u32,
361 instances: []const Instance, 361 instances: []const Instance,
362 ) !void { 362 ) !bool {
363 if (instances.len == 0) return false;
364
363 const decision = planInstanceUpload(.{ 365 const decision = planInstanceUpload(.{
364 .current_capacity = std.math.cast(u32, target.len) orelse return error.InvalidInstanceRange, 366 .current_capacity = std.math.cast(u32, target.len) orelse return error.InvalidInstanceRange,
365 .offset_instances = offset_instances, 367 .offset_instances = offset_instances,
366 .write_len = std.math.cast(u32, instances.len) orelse return error.InvalidInstanceRange, 368 .write_len = std.math.cast(u32, instances.len) orelse return error.InvalidInstanceRange,
367 }); 369 });
368 switch (decision.upload_mode) { 370 switch (decision.upload_mode) {
369 .invalid_range, .full => return error.InvalidInstanceRange, 371 .invalid_range => return error.InvalidInstanceRange,
372 .full => return true,
370 .partial => {}, 373 .partial => {},
371 } 374 }
372 375
373 const offset: usize = @intCast(offset_instances); 376 const offset: usize = @intCast(offset_instances);
374 @memcpy(target[offset .. offset + instances.len], instances); 377 @memcpy(target[offset .. offset + instances.len], instances);
378 return false;
375 } 379 }
376 380
377 fn swapchainNeedsRebuild(result: vk.Result) bool { 381 fn swapchainNeedsRebuild(result: vk.Result) bool {
@@ -1261,6 +1265,8 @@ pub const Context = struct {
1261 offset_instances: u32, 1265 offset_instances: u32,
1262 instances: []const Instance, 1266 instances: []const Instance,
1263 ) !bool { 1267 ) !bool {
1268 if (instances.len == 0) return false;
1269
1264 const decision = planInstanceUpload(.{ 1270 const decision = planInstanceUpload(.{
1265 .current_capacity = self.instance_capacity, 1271 .current_capacity = self.instance_capacity,
1266 .offset_instances = offset_instances, 1272 .offset_instances = offset_instances,
@@ -1272,8 +1278,6 @@ pub const Context = struct {
1272 .partial => {}, 1278 .partial => {},
1273 } 1279 }
1274 1280
1275 if (instances.len == 0) return false;
1276
1277 const range = planInstanceRangeWrite(offset_instances, @intCast(instances.len)); 1281 const range = planInstanceRangeWrite(offset_instances, @intCast(instances.len));
1278 const mapped = try self.vkd.mapMemory( 1282 const mapped = try self.vkd.mapMemory(
1279 self.device, 1283 self.device,
@@ -1524,7 +1528,7 @@ test "writeInstanceRange overwrites only the requested window" {
1524 testInstance(120), 1528 testInstance(120),
1525 }; 1529 };
1526 1530
1527 try writeInstanceRange(target[0..], 1, replacement[0..]); 1531 try std.testing.expect(!(try writeInstanceRange(target[0..], 1, replacement[0..])));
1528 1532
1529 try std.testing.expectEqualDeep(testInstance(0), target[0]); 1533 try std.testing.expectEqualDeep(testInstance(0), target[0]);
1530 try std.testing.expectEqualDeep(testInstance(100), target[1]); 1534 try std.testing.expectEqualDeep(testInstance(100), target[1]);
@@ -1532,7 +1536,7 @@ test "writeInstanceRange overwrites only the requested window" {
1532 try std.testing.expectEqualDeep(testInstance(60), target[3]); 1536 try std.testing.expectEqualDeep(testInstance(60), target[3]);
1533 } 1537 }
1534 1538
1535 test "writeInstanceRange rejects writes past the backing slice" { 1539 test "writeInstanceRange reports full-upload fallback when capacity is too small" {
1536 var target = [_]Instance{ 1540 var target = [_]Instance{
1537 testInstance(0), 1541 testInstance(0),
1538 testInstance(20), 1542 testInstance(20),
@@ -1542,8 +1546,32 @@ test "writeInstanceRange rejects writes past the backing slice" {
1542 testInstance(120), 1546 testInstance(120),
1543 }; 1547 };
1544 1548
1549 try std.testing.expect(try writeInstanceRange(target[0..], 1, replacement[0..]));
1550 try std.testing.expectEqualDeep(testInstance(0), target[0]);
1551 try std.testing.expectEqualDeep(testInstance(20), target[1]);
1552 }
1553
1554 test "writeInstanceRange treats zero-length writes as a no-op without fallback" {
1555 var target = [_]Instance{
1556 testInstance(0),
1557 testInstance(20),
1558 };
1559 const empty = [_]Instance{};
1560
1561 try std.testing.expect(!(try writeInstanceRange(target[0..], 99, empty[0..])));
1562 try std.testing.expectEqualDeep(testInstance(0), target[0]);
1563 try std.testing.expectEqualDeep(testInstance(20), target[1]);
1564 }
1565
1566 test "writeInstanceRange rejects overflowing ranges" {
1567 var target = [_]Instance{
1568 testInstance(0),
1569 testInstance(20),
1570 };
1571 const replacement = [_]Instance{testInstance(100)};
1572
1545 try std.testing.expectError( 1573 try std.testing.expectError(
1546 error.InvalidInstanceRange, 1574 error.InvalidInstanceRange,
1547 writeInstanceRange(target[0..], 1, replacement[0..]), 1575 writeInstanceRange(target[0..], std.math.maxInt(u32), replacement[0..]),
1548 ); 1576 );
1549 } 1577 }