10cbcfdb
Handle out-of-date swapchains during draw
a73x 2026-04-08 15:33
Commit message
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 | } | ||