66ece494
renderer: reorder resetFences after acquire, add errdefer re-signal
a73x 2026-04-18 12:35
Commit message
src/renderer.zig
| Old | New | ||
|---|---|---|---|
| @@ -1267,15 +1267,31 @@ pub const Context = struct { | |||
| 1267 | self.instance_capacity = new_capacity; | 1267 | self.instance_capacity = new_capacity; |
| 1268 | } | 1268 | } |
| 1269 | 1269 | ||
| 1270 | /// Submit an empty command batch that signals `fence`. Used as an | ||
| 1271 | /// errdefer recovery path when acquire has succeeded, resetFences has | ||
| 1272 | /// run, but we failed before queueSubmit — we need to put the fence | ||
| 1273 | /// back in the signaled state so the next frame's wait succeeds. | ||
| 1274 | fn resignalFence(self: *Context, fence: vk.Fence) void { | ||
| 1275 | const submit_info = vk.SubmitInfo{}; | ||
| 1276 | _ = self.vkd.queueSubmit( | ||
| 1277 | self.graphics_queue, | ||
| 1278 | 1, | ||
| 1279 | @ptrCast(&submit_info), | ||
| 1280 | fence, | ||
| 1281 | ) catch |err| { | ||
| 1282 | std.log.warn("resignalFence: {s}", .{@errorName(err)}); | ||
| 1283 | }; | ||
| 1284 | } | ||
| 1285 | |||
| 1270 | /// Record a command buffer that begins the render pass with the given clear color and presents. | 1286 | /// Record a command buffer that begins the render pass with the given clear color and presents. |
| 1271 | /// Does not bind the pipeline or draw — just clear + present. | 1287 | /// Does not bind the pipeline or draw — just clear + present. |
| 1272 | /// Blocks until the previous frame's fence signals. | 1288 | /// Blocks until the previous frame's fence signals. |
| 1273 | pub fn drawClear(self: *Context, clear_color: [4]f32) !void { | 1289 | pub fn drawClear(self: *Context, clear_color: [4]f32) !void { |
| 1274 | // Wait for previous frame to finish | 1290 | // Wait for previous frame to finish |
| 1275 | _ = try self.vkd.waitForFences(self.device, 1, @ptrCast(&self.in_flight_fence), .true, std.math.maxInt(u64)); | 1291 | _ = try self.vkd.waitForFences(self.device, 1, @ptrCast(&self.in_flight_fence), .true, std.math.maxInt(u64)); |
| 1276 | try self.vkd.resetFences(self.device, 1, @ptrCast(&self.in_flight_fence)); | ||
| 1277 | 1292 | ||
| 1278 | // Acquire next image | 1293 | // Acquire next image BEFORE reset, so an acquire failure leaves the |
| 1294 | // fence in a safe state (signaled from the prior frame). | ||
| 1279 | const acquire = self.vkd.acquireNextImageKHR( | 1295 | const acquire = self.vkd.acquireNextImageKHR( |
| 1280 | self.device, | 1296 | self.device, |
| 1281 | self.swapchain, | 1297 | self.swapchain, |
| @@ -1289,6 +1305,9 @@ pub const Context = struct { | |||
| 1289 | if (swapchainNeedsRebuild(acquire.result)) return error.OutOfDateKHR; | 1305 | if (swapchainNeedsRebuild(acquire.result)) return error.OutOfDateKHR; |
| 1290 | const image_index = acquire.image_index; | 1306 | const image_index = acquire.image_index; |
| 1291 | 1307 | ||
| 1308 | try self.vkd.resetFences(self.device, 1, @ptrCast(&self.in_flight_fence)); | ||
| 1309 | errdefer self.resignalFence(self.in_flight_fence); | ||
| 1310 | |||
| 1292 | // Record command buffer | 1311 | // Record command buffer |
| 1293 | try self.vkd.resetCommandBuffer(self.command_buffer, .{}); | 1312 | try self.vkd.resetCommandBuffer(self.command_buffer, .{}); |
| 1294 | try self.vkd.beginCommandBuffer(self.command_buffer, &vk.CommandBufferBeginInfo{ | 1313 | try self.vkd.beginCommandBuffer(self.command_buffer, &vk.CommandBufferBeginInfo{ |
| @@ -1708,13 +1727,13 @@ pub const Context = struct { | |||
| 1708 | 1727 | ||
| 1709 | // Wait for previous frame to finish | 1728 | // Wait for previous frame to finish |
| 1710 | _ = try self.vkd.waitForFences(self.device, 1, @ptrCast(&self.in_flight_fence), .true, std.math.maxInt(u64)); | 1729 | _ = try self.vkd.waitForFences(self.device, 1, @ptrCast(&self.in_flight_fence), .true, std.math.maxInt(u64)); |
| 1711 | try self.vkd.resetFences(self.device, 1, @ptrCast(&self.in_flight_fence)); | ||
| 1712 | if (timing_out) |t| { | 1730 | if (timing_out) |t| { |
| 1713 | t.wait_fences_us = readTimer(&timer); | 1731 | t.wait_fences_us = readTimer(&timer); |
| 1714 | timer.reset(); | 1732 | timer.reset(); |
| 1715 | } | 1733 | } |
| 1716 | 1734 | ||
| 1717 | // Acquire next image | 1735 | // Acquire next image BEFORE reset, so an acquire failure leaves the |
| 1736 | // fence in a safe state (signaled from the prior frame). | ||
| 1718 | const acquire = self.vkd.acquireNextImageKHR( | 1737 | const acquire = self.vkd.acquireNextImageKHR( |
| 1719 | self.device, | 1738 | self.device, |
| 1720 | self.swapchain, | 1739 | self.swapchain, |
| @@ -1732,6 +1751,9 @@ pub const Context = struct { | |||
| 1732 | timer.reset(); | 1751 | timer.reset(); |
| 1733 | } | 1752 | } |
| 1734 | 1753 | ||
| 1754 | try self.vkd.resetFences(self.device, 1, @ptrCast(&self.in_flight_fence)); | ||
| 1755 | errdefer self.resignalFence(self.in_flight_fence); | ||
| 1756 | |||
| 1735 | // Record command buffer | 1757 | // Record command buffer |
| 1736 | try self.vkd.resetCommandBuffer(self.command_buffer, .{}); | 1758 | try self.vkd.resetCommandBuffer(self.command_buffer, .{}); |
| 1737 | try self.vkd.beginCommandBuffer(self.command_buffer, &vk.CommandBufferBeginInfo{ | 1759 | try self.vkd.beginCommandBuffer(self.command_buffer, &vk.CommandBufferBeginInfo{ |