2fd9a6ab
Fix partial upload fallback contract
a73x 2026-04-08 19:38
Commit message
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 | } |