a73x

10cbcfdb

Handle out-of-date swapchains during draw

a73x   2026-04-08 15:33

Commit message
Handle out-of-date swapchains during draw

src/main.zig
Old New
@@ -310,11 +310,18 @@ fn runTerminal(alloc: std.mem.Allocator) !void {
310 try ctx.uploadInstances(instances.items); 310 try ctx.uploadInstances(instances.items);
311 } 311 }
312 312
313 try ctx.drawCells( 313 ctx.drawCells(
314 @intCast(instances.items.len), 314 @intCast(instances.items.len),
315 .{ @floatFromInt(cell_w), @floatFromInt(cell_h) }, 315 .{ @floatFromInt(cell_w), @floatFromInt(cell_h) },
316 default_bg, 316 default_bg,
317 ); 317 ) catch |err| switch (err) {
318 error.OutOfDateKHR => {
319 _ = try ctx.vkd.deviceWaitIdle(ctx.device);
320 try ctx.recreateSwapchain(window.width, window.height);
321 continue;
322 },
323 else => return err,
324 };
318 } 325 }
319 326
320 _ = try ctx.vkd.deviceWaitIdle(ctx.device); 327 _ = try ctx.vkd.deviceWaitIdle(ctx.device);
@@ -509,7 +516,14 @@ fn runDrawSmokeTest(alloc: std.mem.Allocator) !void {
509 _ = conn.display.readEvents(); 516 _ = conn.display.readEvents();
510 } 517 }
511 _ = conn.display.dispatchPending(); 518 _ = conn.display.dispatchPending();
512 try ctx.drawCells(1, .{ cell_w, cell_h }, .{ 0.0, 0.0, 0.0, 1.0 }); 519 ctx.drawCells(1, .{ cell_w, cell_h }, .{ 0.0, 0.0, 0.0, 1.0 }) catch |err| switch (err) {
520 error.OutOfDateKHR => {
521 _ = try ctx.vkd.deviceWaitIdle(ctx.device);
522 try ctx.recreateSwapchain(window.width, window.height);
523 continue;
524 },
525 else => return err,
526 };
513 _ = conn.display.flush(); 527 _ = conn.display.flush();
514 std.Thread.sleep(16 * std.time.ns_per_ms); 528 std.Thread.sleep(16 * std.time.ns_per_ms);
515 } 529 }
src/renderer.zig
Old New
@@ -313,6 +313,10 @@ fn nextInstanceCapacity(current: u32, needed: u32) u32 {
313 return capacity; 313 return capacity;
314 } 314 }
315 315
316 fn swapchainNeedsRebuild(result: vk.Result) bool {
317 return result == .suboptimal_khr;
318 }
319
316 pub const Context = struct { 320 pub const Context = struct {
317 alloc: std.mem.Allocator, 321 alloc: std.mem.Allocator,
318 vkb: vk.BaseWrapper, 322 vkb: vk.BaseWrapper,
@@ -998,13 +1002,17 @@ pub const Context = struct {
998 try self.vkd.resetFences(self.device, 1, @ptrCast(&self.in_flight_fence)); 1002 try self.vkd.resetFences(self.device, 1, @ptrCast(&self.in_flight_fence));
999 1003
1000 // Acquire next image 1004 // Acquire next image
1001 const acquire = try self.vkd.acquireNextImageKHR( 1005 const acquire = self.vkd.acquireNextImageKHR(
1002 self.device, 1006 self.device,
1003 self.swapchain, 1007 self.swapchain,
1004 std.math.maxInt(u64), 1008 std.math.maxInt(u64),
1005 self.image_available, 1009 self.image_available,
1006 .null_handle, 1010 .null_handle,
1007 ); 1011 ) catch |err| switch (err) {
1012 error.OutOfDateKHR => return error.OutOfDateKHR,
1013 else => return err,
1014 };
1015 if (swapchainNeedsRebuild(acquire.result)) return error.OutOfDateKHR;
1008 const image_index = acquire.image_index; 1016 const image_index = acquire.image_index;
1009 1017
1010 // Record command buffer 1018 // Record command buffer
@@ -1046,13 +1054,17 @@ pub const Context = struct {
1046 }), self.in_flight_fence); 1054 }), self.in_flight_fence);
1047 1055
1048 // Present 1056 // Present
1049 _ = try self.vkd.queuePresentKHR(self.present_queue, &vk.PresentInfoKHR{ 1057 const present_result = self.vkd.queuePresentKHR(self.present_queue, &vk.PresentInfoKHR{
1050 .wait_semaphore_count = 1, 1058 .wait_semaphore_count = 1,
1051 .p_wait_semaphores = @ptrCast(&self.render_finished), 1059 .p_wait_semaphores = @ptrCast(&self.render_finished),
1052 .swapchain_count = 1, 1060 .swapchain_count = 1,
1053 .p_swapchains = @ptrCast(&self.swapchain), 1061 .p_swapchains = @ptrCast(&self.swapchain),
1054 .p_image_indices = @ptrCast(&image_index), 1062 .p_image_indices = @ptrCast(&image_index),
1055 }); 1063 }) catch |err| switch (err) {
1064 error.OutOfDateKHR => return error.OutOfDateKHR,
1065 else => return err,
1066 };
1067 if (swapchainNeedsRebuild(present_result)) return error.OutOfDateKHR;
1056 } 1068 }
1057 1069
1058 /// Upload CPU R8 pixels into the GPU atlas image. 1070 /// Upload CPU R8 pixels into the GPU atlas image.
@@ -1193,13 +1205,17 @@ pub const Context = struct {
1193 try self.vkd.resetFences(self.device, 1, @ptrCast(&self.in_flight_fence)); 1205 try self.vkd.resetFences(self.device, 1, @ptrCast(&self.in_flight_fence));
1194 1206
1195 // Acquire next image 1207 // Acquire next image
1196 const acquire = try self.vkd.acquireNextImageKHR( 1208 const acquire = self.vkd.acquireNextImageKHR(
1197 self.device, 1209 self.device,
1198 self.swapchain, 1210 self.swapchain,
1199 std.math.maxInt(u64), 1211 std.math.maxInt(u64),
1200 self.image_available, 1212 self.image_available,
1201 .null_handle, 1213 .null_handle,
1202 ); 1214 ) catch |err| switch (err) {
1215 error.OutOfDateKHR => return error.OutOfDateKHR,
1216 else => return err,
1217 };
1218 if (swapchainNeedsRebuild(acquire.result)) return error.OutOfDateKHR;
1203 const image_index = acquire.image_index; 1219 const image_index = acquire.image_index;
1204 1220
1205 // Record command buffer 1221 // Record command buffer
@@ -1289,13 +1305,17 @@ pub const Context = struct {
1289 }), self.in_flight_fence); 1305 }), self.in_flight_fence);
1290 1306
1291 // Present 1307 // Present
1292 _ = try self.vkd.queuePresentKHR(self.present_queue, &vk.PresentInfoKHR{ 1308 const present_result = self.vkd.queuePresentKHR(self.present_queue, &vk.PresentInfoKHR{
1293 .wait_semaphore_count = 1, 1309 .wait_semaphore_count = 1,
1294 .p_wait_semaphores = @ptrCast(&self.render_finished), 1310 .p_wait_semaphores = @ptrCast(&self.render_finished),
1295 .swapchain_count = 1, 1311 .swapchain_count = 1,
1296 .p_swapchains = @ptrCast(&self.swapchain), 1312 .p_swapchains = @ptrCast(&self.swapchain),
1297 .p_image_indices = @ptrCast(&image_index), 1313 .p_image_indices = @ptrCast(&image_index),
1298 }); 1314 }) catch |err| switch (err) {
1315 error.OutOfDateKHR => return error.OutOfDateKHR,
1316 else => return err,
1317 };
1318 if (swapchainNeedsRebuild(present_result)) return error.OutOfDateKHR;
1299 } 1319 }
1300 }; 1320 };
1301 1321
@@ -1320,3 +1340,8 @@ test "nextInstanceCapacity grows geometrically" {
1320 try std.testing.expectEqual(@as(u32, 32_000), nextInstanceCapacity(16_000, 16_001)); 1340 try std.testing.expectEqual(@as(u32, 32_000), nextInstanceCapacity(16_000, 16_001));
1321 try std.testing.expectEqual(@as(u32, 4), nextInstanceCapacity(1, 3)); 1341 try std.testing.expectEqual(@as(u32, 4), nextInstanceCapacity(1, 3));
1322 } 1342 }
1343
1344 test "swapchainNeedsRebuild flags suboptimal result" {
1345 try std.testing.expect(swapchainNeedsRebuild(.suboptimal_khr));
1346 try std.testing.expect(!swapchainNeedsRebuild(.success));
1347 }