Quick answer: Check the result of vkAcquireNextImageKHR and vkQueuePresentKHR. On VK_ERROR_OUT_OF_DATE_KHR or VK_SUBOPTIMAL_KHR, recreate the swapchain and skip the frame.
A Vulkan renderer corrupts or crashes after the window is resized. The swapchain is now the wrong size and the code never recreates it.
Check Acquire
VkResult r = vkAcquireNextImageKHR(device, swapchain, UINT64_MAX,
imageAvailable, VK_NULL_HANDLE, &imageIndex);
if (r == VK_ERROR_OUT_OF_DATE_KHR) {
RecreateSwapchain();
return; // skip this frame
} else if (r != VK_SUCCESS && r != VK_SUBOPTIMAL_KHR) {
Fatal("acquire failed");
}
Check Present
r = vkQueuePresentKHR(presentQueue, &presentInfo);
if (r == VK_ERROR_OUT_OF_DATE_KHR || r == VK_SUBOPTIMAL_KHR || framebufferResized) {
framebufferResized = false;
RecreateSwapchain();
}
SUBOPTIMAL still presents but signals the swapchain should be recreated soon.
RecreateSwapchain
void RecreateSwapchain() {
// handle minimized window: wait until non-zero size
int w = 0, h = 0;
glfwGetFramebufferSize(window, &w, &h);
while (w == 0 || h == 0) {
glfwGetFramebufferSize(window, &w, &h);
glfwWaitEvents();
}
vkDeviceWaitIdle(device);
CleanupSwapchain();
CreateSwapchain();
CreateImageViews();
CreateFramebuffers();
}
Wait for idle, destroy old swapchain resources, recreate at the new size. Handle the minimized (0×0) case explicitly — don’t create a zero-size swapchain.
Resize Callback
Set a framebufferResized flag from the window resize callback. Some drivers don’t report OUT_OF_DATE reliably, so the explicit flag is a backstop.
Verifying
Resize, maximize, and minimize the window repeatedly. Rendering follows the new size with no corruption or crashes. Validation layers stay quiet.
“Acquire and present can say ‘out of date’. Always check, recreate, and handle the minimized case.”
Recreating the swapchain on every SUBOPTIMAL can thrash — defer it to once per frame and coalesce with the resize flag.