From f87c2d328727178f75184ed4ced81ce68df7833d Mon Sep 17 00:00:00 2001 From: swinston Date: Sat, 5 Jul 2025 14:54:00 -0700 Subject: [PATCH 1/7] Add exception handling for vk::SystemError across mainLoop and presentation functions - Catch `vk::SystemError` in `mainLoop` and presentation logic to handle `vk::Result::eErrorOutOfDateKHR` gracefully. - Log and ignore swapchain-related errors during shutdown or presentation to prevent crashes while rethrowing unexpected exceptions. - Updated multiple attachments for consistent error handling. --- attachments/15_hello_triangle.cpp | 13 ++++++- attachments/16_frames_in_flight.cpp | 13 ++++++- attachments/17_swap_chain_recreation.cpp | 40 ++++++++++++++++----- attachments/18_vertex_input.cpp | 40 ++++++++++++++++----- attachments/19_vertex_buffer.cpp | 40 ++++++++++++++++----- attachments/20_staging_buffer.cpp | 40 ++++++++++++++++----- attachments/21_index_buffer.cpp | 13 ++++++- attachments/22_descriptor_layout.cpp | 13 ++++++- attachments/23_descriptor_sets.cpp | 13 ++++++- attachments/24_texture_image.cpp | 13 ++++++- attachments/25_sampler.cpp | 13 ++++++- attachments/26_texture_mapping.cpp | 27 ++++++++++----- attachments/27_depth_buffering.cpp | 27 ++++++++++----- attachments/28_model_loading.cpp | 13 ++++++- attachments/29_mipmapping.cpp | 13 ++++++- attachments/30_multisampling.cpp | 42 ++++++++++++++++------ attachments/31_compute_shader.cpp | 44 ++++++++++++++++++------ 17 files changed, 335 insertions(+), 82 deletions(-) diff --git a/attachments/15_hello_triangle.cpp b/attachments/15_hello_triangle.cpp index 487022e7..9e4cf323 100644 --- a/attachments/15_hello_triangle.cpp +++ b/attachments/15_hello_triangle.cpp @@ -100,7 +100,18 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - drawFrame(); + try { + drawFrame(); + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; + } else { + // Rethrow other errors + throw; + } + } } device.waitIdle(); diff --git a/attachments/16_frames_in_flight.cpp b/attachments/16_frames_in_flight.cpp index 7da930c4..2b2066dc 100644 --- a/attachments/16_frames_in_flight.cpp +++ b/attachments/16_frames_in_flight.cpp @@ -104,7 +104,18 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - drawFrame(); + try { + drawFrame(); + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; + } else { + // Rethrow other errors + throw; + } + } } device.waitIdle(); diff --git a/attachments/17_swap_chain_recreation.cpp b/attachments/17_swap_chain_recreation.cpp index 66913ac8..071e5ea0 100644 --- a/attachments/17_swap_chain_recreation.cpp +++ b/attachments/17_swap_chain_recreation.cpp @@ -111,7 +111,18 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - drawFrame(); + try { + drawFrame(); + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; + } else { + // Rethrow other errors + throw; + } + } } device.waitIdle(); @@ -535,14 +546,25 @@ class HelloTriangleApplication { graphicsQueue.submit(submitInfo, *inFlightFences[currentFrame]); - const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], - .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; - result = presentQueue.presentKHR( presentInfoKHR ); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], + .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; + result = presentQueue.presentKHR( presentInfoKHR ); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during presentation" << std::endl; + } else { + // Rethrow other errors + throw; + } } semaphoreIndex = (semaphoreIndex + 1) % presentCompleteSemaphore.size(); currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; diff --git a/attachments/18_vertex_input.cpp b/attachments/18_vertex_input.cpp index e35067f9..1b7e6946 100644 --- a/attachments/18_vertex_input.cpp +++ b/attachments/18_vertex_input.cpp @@ -135,7 +135,18 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - drawFrame(); + try { + drawFrame(); + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; + } else { + // Rethrow other errors + throw; + } + } } device.waitIdle(); @@ -562,14 +573,25 @@ class HelloTriangleApplication { graphicsQueue.submit(submitInfo, *inFlightFences[currentFrame]); - const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], - .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; - result = presentQueue.presentKHR( presentInfoKHR ); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], + .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; + result = presentQueue.presentKHR( presentInfoKHR ); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during presentation" << std::endl; + } else { + // Rethrow other errors + throw; + } } semaphoreIndex = (semaphoreIndex + 1) % presentCompleteSemaphore.size(); currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; diff --git a/attachments/19_vertex_buffer.cpp b/attachments/19_vertex_buffer.cpp index ef7d4cd8..41f05779 100644 --- a/attachments/19_vertex_buffer.cpp +++ b/attachments/19_vertex_buffer.cpp @@ -139,7 +139,18 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - drawFrame(); + try { + drawFrame(); + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; + } else { + // Rethrow other errors + throw; + } + } } device.waitIdle(); @@ -592,14 +603,25 @@ class HelloTriangleApplication { graphicsQueue.submit(submitInfo, *inFlightFences[currentFrame]); - const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], - .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; - result = presentQueue.presentKHR( presentInfoKHR ); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], + .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; + result = presentQueue.presentKHR( presentInfoKHR ); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during presentation" << std::endl; + } else { + // Rethrow other errors + throw; + } } semaphoreIndex = (semaphoreIndex + 1) % presentCompleteSemaphore.size(); currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; diff --git a/attachments/20_staging_buffer.cpp b/attachments/20_staging_buffer.cpp index 1361bfbe..09b88b33 100644 --- a/attachments/20_staging_buffer.cpp +++ b/attachments/20_staging_buffer.cpp @@ -139,7 +139,18 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - drawFrame(); + try { + drawFrame(); + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; + } else { + // Rethrow other errors + throw; + } + } } device.waitIdle(); @@ -611,14 +622,25 @@ class HelloTriangleApplication { graphicsQueue.submit(submitInfo, *inFlightFences[currentFrame]); - const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], - .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; - result = presentQueue.presentKHR( presentInfoKHR ); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], + .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; + result = presentQueue.presentKHR( presentInfoKHR ); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during presentation" << std::endl; + } else { + // Rethrow other errors + throw; + } } semaphoreIndex = (semaphoreIndex + 1) % presentCompleteSemaphore.size(); currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; diff --git a/attachments/21_index_buffer.cpp b/attachments/21_index_buffer.cpp index 3fb574b0..62af938a 100644 --- a/attachments/21_index_buffer.cpp +++ b/attachments/21_index_buffer.cpp @@ -147,7 +147,18 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - drawFrame(); + try { + drawFrame(); + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; + } else { + // Rethrow other errors + throw; + } + } } device.waitIdle(); diff --git a/attachments/22_descriptor_layout.cpp b/attachments/22_descriptor_layout.cpp index dfef6e4e..88944f1a 100644 --- a/attachments/22_descriptor_layout.cpp +++ b/attachments/22_descriptor_layout.cpp @@ -164,7 +164,18 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - drawFrame(); + try { + drawFrame(); + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; + } else { + // Rethrow other errors + throw; + } + } } device.waitIdle(); diff --git a/attachments/23_descriptor_sets.cpp b/attachments/23_descriptor_sets.cpp index a2879e22..d8b2497c 100644 --- a/attachments/23_descriptor_sets.cpp +++ b/attachments/23_descriptor_sets.cpp @@ -169,7 +169,18 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - drawFrame(); + try { + drawFrame(); + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; + } else { + // Rethrow other errors + throw; + } + } } device.waitIdle(); diff --git a/attachments/24_texture_image.cpp b/attachments/24_texture_image.cpp index f41383e0..1928790c 100644 --- a/attachments/24_texture_image.cpp +++ b/attachments/24_texture_image.cpp @@ -176,7 +176,18 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - drawFrame(); + try { + drawFrame(); + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; + } else { + // Rethrow other errors + throw; + } + } } device.waitIdle(); diff --git a/attachments/25_sampler.cpp b/attachments/25_sampler.cpp index 3c6583f3..cdf5229a 100644 --- a/attachments/25_sampler.cpp +++ b/attachments/25_sampler.cpp @@ -180,7 +180,18 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - drawFrame(); + try { + drawFrame(); + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; + } else { + // Rethrow other errors + throw; + } + } } device.waitIdle(); diff --git a/attachments/26_texture_mapping.cpp b/attachments/26_texture_mapping.cpp index 824576a2..6fdbc890 100644 --- a/attachments/26_texture_mapping.cpp +++ b/attachments/26_texture_mapping.cpp @@ -931,14 +931,25 @@ class HelloTriangleApplication { graphicsQueue.submit(submitInfo, *inFlightFences[currentFrame]); - const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], - .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; - result = presentQueue.presentKHR(presentInfoKHR); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], + .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; + result = presentQueue.presentKHR(presentInfoKHR); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during presentation" << std::endl; + } else { + // Rethrow other errors + throw; + } } semaphoreIndex = (semaphoreIndex + 1) % presentCompleteSemaphore.size(); currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; diff --git a/attachments/27_depth_buffering.cpp b/attachments/27_depth_buffering.cpp index 8c0a9e14..f6162d9e 100644 --- a/attachments/27_depth_buffering.cpp +++ b/attachments/27_depth_buffering.cpp @@ -1016,14 +1016,25 @@ class HelloTriangleApplication { graphicsQueue.submit(submitInfo, *inFlightFences[currentFrame]); - const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], - .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; - result = presentQueue.presentKHR(presentInfoKHR); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], + .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; + result = presentQueue.presentKHR(presentInfoKHR); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during presentation" << std::endl; + } else { + // Rethrow other errors + throw; + } } semaphoreIndex = (semaphoreIndex + 1) % presentCompleteSemaphore.size(); currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; diff --git a/attachments/28_model_loading.cpp b/attachments/28_model_loading.cpp index 509430f7..5906f430 100644 --- a/attachments/28_model_loading.cpp +++ b/attachments/28_model_loading.cpp @@ -198,7 +198,18 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - drawFrame(); + try { + drawFrame(); + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; + } else { + // Rethrow other errors + throw; + } + } } device.waitIdle(); diff --git a/attachments/29_mipmapping.cpp b/attachments/29_mipmapping.cpp index 1c487b8b..b1460d43 100644 --- a/attachments/29_mipmapping.cpp +++ b/attachments/29_mipmapping.cpp @@ -199,7 +199,18 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - drawFrame(); + try { + drawFrame(); + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; + } else { + // Rethrow other errors + throw; + } + } } device.waitIdle(); diff --git a/attachments/30_multisampling.cpp b/attachments/30_multisampling.cpp index 450406aa..3c856838 100644 --- a/attachments/30_multisampling.cpp +++ b/attachments/30_multisampling.cpp @@ -205,7 +205,18 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - drawFrame(); + try { + drawFrame(); + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; + } else { + // Rethrow other errors + throw; + } + } } device.waitIdle(); @@ -1155,16 +1166,27 @@ class HelloTriangleApplication { .signalSemaphoreCount = 1, .pSignalSemaphores = &*renderFinishedSemaphore[imageIndex] }; graphicsQueue.submit(submitInfo, *inFlightFences[currentFrame]); - - const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], - .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; - result = presentQueue.presentKHR(presentInfoKHR); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], + .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; + result = presentQueue.presentKHR(presentInfoKHR); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during presentation" << std::endl; + } else { + // Rethrow other errors + throw; + } } + semaphoreIndex = (semaphoreIndex + 1) % presentCompleteSemaphore.size(); currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; } diff --git a/attachments/31_compute_shader.cpp b/attachments/31_compute_shader.cpp index e00243d3..143a6b75 100644 --- a/attachments/31_compute_shader.cpp +++ b/attachments/31_compute_shader.cpp @@ -174,11 +174,22 @@ class ComputeShaderApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - drawFrame(); - // We want to animate the particle system using the last frames time to get smooth, frame-rate independent animation - double currentTime = glfwGetTime(); - lastFrameTime = (currentTime - lastTime) * 1000.0; - lastTime = currentTime; + try { + drawFrame(); + // We want to animate the particle system using the last frames time to get smooth, frame-rate independent animation + double currentTime = glfwGetTime(); + lastFrameTime = (currentTime - lastTime) * 1000.0; + lastTime = currentTime; + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; + } else { + // Rethrow other errors + throw; + } + } } device.waitIdle(); @@ -888,12 +899,23 @@ class ComputeShaderApplication { .pImageIndices = &imageIndex }; - result = presentQueue.presentKHR(presentInfo); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + result = presentQueue.presentKHR(presentInfo); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + // Swapchain is out of date, this can happen during window close + // Just ignore and continue to close + std::cout << "Ignoring ErrorOutOfDateKHR during presentation" << std::endl; + } else { + // Rethrow other errors + throw; + } } } currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; From f474f04827530eeaf25f29117ba5d0125d75e9fb Mon Sep 17 00:00:00 2001 From: swinston Date: Wed, 20 Aug 2025 20:26:38 -0700 Subject: [PATCH 2/7] Try to address resize issue in windows. --- attachments/17_swap_chain_recreation.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/attachments/17_swap_chain_recreation.cpp b/attachments/17_swap_chain_recreation.cpp index 30a0b0f1..8a3ab72f 100644 --- a/attachments/17_swap_chain_recreation.cpp +++ b/attachments/17_swap_chain_recreation.cpp @@ -84,6 +84,7 @@ class HelloTriangleApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -141,6 +142,7 @@ class HelloTriangleApplication { } void recreateSwapChain() { + framebufferResized = false; int width = 0, height = 0; glfwGetFramebufferSize(window, &width, &height); while (width == 0 || height == 0) { @@ -153,6 +155,13 @@ class HelloTriangleApplication { cleanupSwapChain(); createSwapChain(); createImageViews(); + // Recreate dependent objects in case format or extent changed + graphicsPipeline = nullptr; + createGraphicsPipeline(); + createSyncObjects(); + // Reset indices to avoid out-of-range after swapchain resize + semaphoreIndex = 0; + currentFrame = 0; } void createInstance() { @@ -250,8 +259,9 @@ class HelloTriangleApplication { { return strcmp( availableDeviceExtension.extensionName, requiredDeviceExtension ) == 0; } ); } ); - auto features = device.template getFeatures2(); - bool supportsRequiredFeatures = features.template get().dynamicRendering && + auto features = device.template getFeatures2(); + bool supportsRequiredFeatures = features.template get().shaderDrawParameters && + features.template get().dynamicRendering && features.template get().extendedDynamicState; return supportsVulkan1_3 && supportsGraphics && supportsAllRequiredExtensions && supportsRequiredFeatures; @@ -285,11 +295,12 @@ class HelloTriangleApplication { throw std::runtime_error("Could not find a queue for graphics and present -> terminating"); } - // query for Vulkan 1.3 features - vk::StructureChain featureChain = { + // query for required features (Vulkan 1.1 and 1.3) + vk::StructureChain featureChain = { {}, // vk::PhysicalDeviceFeatures2 - {.synchronization2 = true, .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features - {.extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT + { .shaderDrawParameters = true }, // vk::PhysicalDeviceVulkan11Features + { .synchronization2 = true, .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features + { .extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT }; // create a Device From fb7a5c21a3b6987e31b5c51a0226ddf1244d0e3d Mon Sep 17 00:00:00 2001 From: swinston Date: Thu, 21 Aug 2025 20:15:05 -0700 Subject: [PATCH 3/7] Refactor `drawFrame` error handling to simplify main loop and address `ErrorOutOfDateKHR` with swapchain recreation. --- attachments/17_swap_chain_recreation.cpp | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/attachments/17_swap_chain_recreation.cpp b/attachments/17_swap_chain_recreation.cpp index 8a3ab72f..0f465acf 100644 --- a/attachments/17_swap_chain_recreation.cpp +++ b/attachments/17_swap_chain_recreation.cpp @@ -113,18 +113,7 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - try { - drawFrame(); - } catch (const vk::SystemError& e) { - if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; - } else { - // Rethrow other errors - throw; - } - } + drawFrame(); } device.waitIdle(); @@ -537,11 +526,9 @@ class HelloTriangleApplication { } } catch (const vk::SystemError& e) { if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during presentation" << std::endl; + recreateSwapChain(); + return; } else { - // Rethrow other errors throw; } } From e37bb226037700c2b7d9f797c93c4738c53cd5ce Mon Sep 17 00:00:00 2001 From: swinston Date: Fri, 22 Aug 2025 16:59:35 -0700 Subject: [PATCH 4/7] Bring changes forward for resize. --- attachments/15_hello_triangle.cpp | 13 +---- attachments/16_frames_in_flight.cpp | 13 +---- attachments/17_swap_chain_recreation.cpp | 8 ---- attachments/18_vertex_input.cpp | 34 +++++-------- attachments/19_vertex_buffer.cpp | 36 ++++++-------- attachments/20_staging_buffer.cpp | 36 ++++++-------- attachments/21_index_buffer.cpp | 55 +++++++++++---------- attachments/22_descriptor_layout.cpp | 55 +++++++++++---------- attachments/23_descriptor_sets.cpp | 61 +++++++++++++----------- attachments/24_texture_image.cpp | 61 +++++++++++++----------- attachments/25_sampler.cpp | 47 +++++++++--------- attachments/26_texture_mapping.cpp | 15 +++--- attachments/27_depth_buffering.cpp | 13 +++-- attachments/28_model_loading.cpp | 39 ++++++++------- attachments/29_mipmapping.cpp | 39 ++++++++------- attachments/30_multisampling.cpp | 22 ++------- attachments/31_compute_shader.cpp | 28 ++++------- attachments/32_ecosystem_utilities.cpp | 36 +++++++++----- 18 files changed, 276 insertions(+), 335 deletions(-) diff --git a/attachments/15_hello_triangle.cpp b/attachments/15_hello_triangle.cpp index 0ad5129c..eedf7ef5 100644 --- a/attachments/15_hello_triangle.cpp +++ b/attachments/15_hello_triangle.cpp @@ -101,18 +101,7 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - try { - drawFrame(); - } catch (const vk::SystemError& e) { - if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; - } else { - // Rethrow other errors - throw; - } - } + drawFrame(); } device.waitIdle(); diff --git a/attachments/16_frames_in_flight.cpp b/attachments/16_frames_in_flight.cpp index 9ec0ff4f..35dedd6c 100644 --- a/attachments/16_frames_in_flight.cpp +++ b/attachments/16_frames_in_flight.cpp @@ -105,18 +105,7 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - try { - drawFrame(); - } catch (const vk::SystemError& e) { - if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; - } else { - // Rethrow other errors - throw; - } - } + drawFrame(); } device.waitIdle(); diff --git a/attachments/17_swap_chain_recreation.cpp b/attachments/17_swap_chain_recreation.cpp index 0f465acf..13095605 100644 --- a/attachments/17_swap_chain_recreation.cpp +++ b/attachments/17_swap_chain_recreation.cpp @@ -131,7 +131,6 @@ class HelloTriangleApplication { } void recreateSwapChain() { - framebufferResized = false; int width = 0, height = 0; glfwGetFramebufferSize(window, &width, &height); while (width == 0 || height == 0) { @@ -144,13 +143,6 @@ class HelloTriangleApplication { cleanupSwapChain(); createSwapChain(); createImageViews(); - // Recreate dependent objects in case format or extent changed - graphicsPipeline = nullptr; - createGraphicsPipeline(); - createSyncObjects(); - // Reset indices to avoid out-of-range after swapchain resize - semaphoreIndex = 0; - currentFrame = 0; } void createInstance() { diff --git a/attachments/18_vertex_input.cpp b/attachments/18_vertex_input.cpp index e957f8cd..52b553e2 100644 --- a/attachments/18_vertex_input.cpp +++ b/attachments/18_vertex_input.cpp @@ -108,6 +108,7 @@ class HelloTriangleApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -136,18 +137,7 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - try { - drawFrame(); - } catch (const vk::SystemError& e) { - if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; - } else { - // Rethrow other errors - throw; - } - } + drawFrame(); } device.waitIdle(); @@ -274,8 +264,9 @@ class HelloTriangleApplication { { return strcmp( availableDeviceExtension.extensionName, requiredDeviceExtension ) == 0; } ); } ); - auto features = device.template getFeatures2(); - bool supportsRequiredFeatures = features.template get().dynamicRendering && + auto features = device.template getFeatures2(); + bool supportsRequiredFeatures = features.template get().shaderDrawParameters && + features.template get().dynamicRendering && features.template get().extendedDynamicState; return supportsVulkan1_3 && supportsGraphics && supportsAllRequiredExtensions && supportsRequiredFeatures; @@ -309,11 +300,12 @@ class HelloTriangleApplication { throw std::runtime_error("Could not find a queue for graphics and present -> terminating"); } - // query for Vulkan 1.3 features - vk::StructureChain featureChain = { + // query for required features (Vulkan 1.1 and 1.3) + vk::StructureChain featureChain = { {}, // vk::PhysicalDeviceFeatures2 - {.synchronization2 = true, .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features - {.extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT + { .shaderDrawParameters = true }, // vk::PhysicalDeviceVulkan11Features + { .synchronization2 = true, .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features + { .extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT }; // create a Device @@ -553,11 +545,9 @@ class HelloTriangleApplication { } } catch (const vk::SystemError& e) { if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during presentation" << std::endl; + recreateSwapChain(); + return; } else { - // Rethrow other errors throw; } } diff --git a/attachments/19_vertex_buffer.cpp b/attachments/19_vertex_buffer.cpp index b667d334..85eee29d 100644 --- a/attachments/19_vertex_buffer.cpp +++ b/attachments/19_vertex_buffer.cpp @@ -111,6 +111,7 @@ class HelloTriangleApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -140,18 +141,7 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - try { - drawFrame(); - } catch (const vk::SystemError& e) { - if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; - } else { - // Rethrow other errors - throw; - } - } + drawFrame(); } device.waitIdle(); @@ -278,8 +268,9 @@ class HelloTriangleApplication { { return strcmp( availableDeviceExtension.extensionName, requiredDeviceExtension ) == 0; } ); } ); - auto features = device.template getFeatures2(); - bool supportsRequiredFeatures = features.template get().dynamicRendering && + auto features = device.template getFeatures2(); + bool supportsRequiredFeatures = features.template get().shaderDrawParameters && + features.template get().dynamicRendering && features.template get().extendedDynamicState; return supportsVulkan1_3 && supportsGraphics && supportsAllRequiredExtensions && supportsRequiredFeatures; @@ -313,11 +304,12 @@ class HelloTriangleApplication { throw std::runtime_error("Could not find a queue for graphics and present -> terminating"); } - // query for Vulkan 1.3 features - vk::StructureChain featureChain = { + // query for required features (Vulkan 1.1 and 1.3) + vk::StructureChain featureChain = { {}, // vk::PhysicalDeviceFeatures2 - {.synchronization2 = true, .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features - {.extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT + { .shaderDrawParameters = true }, // vk::PhysicalDeviceVulkan11Features + { .synchronization2 = true, .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features + { .extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT }; // create a Device @@ -353,6 +345,8 @@ class HelloTriangleApplication { } void createImageViews() { + swapChainImageViews.clear(); + vk::ImageViewCreateInfo imageViewCreateInfo{ .viewType = vk::ImageViewType::e2D, .format = swapChainImageFormat, .subresourceRange = { vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 } }; for ( auto image : swapChainImages ) @@ -583,11 +577,9 @@ class HelloTriangleApplication { } } catch (const vk::SystemError& e) { if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during presentation" << std::endl; + recreateSwapChain(); + return; } else { - // Rethrow other errors throw; } } diff --git a/attachments/20_staging_buffer.cpp b/attachments/20_staging_buffer.cpp index 212de728..22cf8d31 100644 --- a/attachments/20_staging_buffer.cpp +++ b/attachments/20_staging_buffer.cpp @@ -111,6 +111,7 @@ class HelloTriangleApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -140,18 +141,7 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - try { - drawFrame(); - } catch (const vk::SystemError& e) { - if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; - } else { - // Rethrow other errors - throw; - } - } + drawFrame(); } device.waitIdle(); @@ -278,8 +268,9 @@ class HelloTriangleApplication { { return strcmp( availableDeviceExtension.extensionName, requiredDeviceExtension ) == 0; } ); } ); - auto features = device.template getFeatures2(); - bool supportsRequiredFeatures = features.template get().dynamicRendering && + auto features = device.template getFeatures2(); + bool supportsRequiredFeatures = features.template get().shaderDrawParameters && + features.template get().dynamicRendering && features.template get().extendedDynamicState; return supportsVulkan1_3 && supportsGraphics && supportsAllRequiredExtensions && supportsRequiredFeatures; @@ -313,11 +304,12 @@ class HelloTriangleApplication { throw std::runtime_error("Could not find a queue for graphics and present -> terminating"); } - // query for Vulkan 1.3 features - vk::StructureChain featureChain = { + // query for required features (Vulkan 1.1 and 1.3) + vk::StructureChain featureChain = { {}, // vk::PhysicalDeviceFeatures2 - {.synchronization2 = true, .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features - {.extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT + { .shaderDrawParameters = true }, // vk::PhysicalDeviceVulkan11Features + { .synchronization2 = true, .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features + { .extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT }; // create a Device @@ -353,6 +345,8 @@ class HelloTriangleApplication { } void createImageViews() { + swapChainImageViews.clear(); + vk::ImageViewCreateInfo imageViewCreateInfo{ .viewType = vk::ImageViewType::e2D, .format = swapChainImageFormat, .subresourceRange = { vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 } }; for ( auto image : swapChainImages ) @@ -602,11 +596,9 @@ class HelloTriangleApplication { } } catch (const vk::SystemError& e) { if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during presentation" << std::endl; + recreateSwapChain(); + return; } else { - // Rethrow other errors throw; } } diff --git a/attachments/21_index_buffer.cpp b/attachments/21_index_buffer.cpp index b9dd8bc4..3479ec0e 100644 --- a/attachments/21_index_buffer.cpp +++ b/attachments/21_index_buffer.cpp @@ -118,6 +118,7 @@ class HelloTriangleApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -148,18 +149,7 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - try { - drawFrame(); - } catch (const vk::SystemError& e) { - if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; - } else { - // Rethrow other errors - throw; - } - } + drawFrame(); } device.waitIdle(); @@ -286,8 +276,9 @@ class HelloTriangleApplication { { return strcmp( availableDeviceExtension.extensionName, requiredDeviceExtension ) == 0; } ); } ); - auto features = device.template getFeatures2(); - bool supportsRequiredFeatures = features.template get().dynamicRendering && + auto features = device.template getFeatures2(); + bool supportsRequiredFeatures = features.template get().shaderDrawParameters && + features.template get().dynamicRendering && features.template get().extendedDynamicState; return supportsVulkan1_3 && supportsGraphics && supportsAllRequiredExtensions && supportsRequiredFeatures; @@ -321,11 +312,12 @@ class HelloTriangleApplication { throw std::runtime_error("Could not find a queue for graphics and present -> terminating"); } - // query for Vulkan 1.3 features - vk::StructureChain featureChain = { + // query for required features (Vulkan 1.1 and 1.3) + vk::StructureChain featureChain = { {}, // vk::PhysicalDeviceFeatures2 - {.synchronization2 = true, .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features - {.extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT + { .shaderDrawParameters = true }, // vk::PhysicalDeviceVulkan11Features + { .synchronization2 = true, .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features + { .extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT }; // create a Device @@ -361,6 +353,8 @@ class HelloTriangleApplication { } void createImageViews() { + swapChainImageViews.clear(); + vk::ImageViewCreateInfo imageViewCreateInfo{ .viewType = vk::ImageViewType::e2D, .format = swapChainImageFormat, .subresourceRange = { vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 } }; for ( auto image : swapChainImages ) @@ -615,14 +609,23 @@ class HelloTriangleApplication { queue.submit(submitInfo, *inFlightFences[currentFrame]); - const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], - .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; - result = queue.presentKHR( presentInfoKHR ); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], + .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; + result = queue.presentKHR( presentInfoKHR ); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + recreateSwapChain(); + return; + } else { + throw; + } } semaphoreIndex = (semaphoreIndex + 1) % presentCompleteSemaphore.size(); currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; diff --git a/attachments/22_descriptor_layout.cpp b/attachments/22_descriptor_layout.cpp index 1b6cafa7..008c2dee 100644 --- a/attachments/22_descriptor_layout.cpp +++ b/attachments/22_descriptor_layout.cpp @@ -133,6 +133,7 @@ class HelloTriangleApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -165,18 +166,7 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - try { - drawFrame(); - } catch (const vk::SystemError& e) { - if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; - } else { - // Rethrow other errors - throw; - } - } + drawFrame(); } device.waitIdle(); @@ -303,8 +293,9 @@ class HelloTriangleApplication { { return strcmp( availableDeviceExtension.extensionName, requiredDeviceExtension ) == 0; } ); } ); - auto features = device.template getFeatures2(); - bool supportsRequiredFeatures = features.template get().dynamicRendering && + auto features = device.template getFeatures2(); + bool supportsRequiredFeatures = features.template get().shaderDrawParameters && + features.template get().dynamicRendering && features.template get().extendedDynamicState; return supportsVulkan1_3 && supportsGraphics && supportsAllRequiredExtensions && supportsRequiredFeatures; @@ -338,11 +329,12 @@ class HelloTriangleApplication { throw std::runtime_error("Could not find a queue for graphics and present -> terminating"); } - // query for Vulkan 1.3 features - vk::StructureChain featureChain = { + // query for required features (Vulkan 1.1 and 1.3) + vk::StructureChain featureChain = { {}, // vk::PhysicalDeviceFeatures2 - {.synchronization2 = true, .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features - {.extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT + { .shaderDrawParameters = true }, // vk::PhysicalDeviceVulkan11Features + { .synchronization2 = true, .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features + { .extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT }; // create a Device @@ -378,6 +370,8 @@ class HelloTriangleApplication { } void createImageViews() { + swapChainImageViews.clear(); + vk::ImageViewCreateInfo imageViewCreateInfo{ .viewType = vk::ImageViewType::e2D, .format = swapChainImageFormat, .subresourceRange = { vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 } }; for ( auto image : swapChainImages ) @@ -670,14 +664,23 @@ class HelloTriangleApplication { queue.submit(submitInfo, *inFlightFences[currentFrame]); - const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], - .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; - result = queue.presentKHR( presentInfoKHR ); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], + .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; + result = queue.presentKHR( presentInfoKHR ); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + recreateSwapChain(); + return; + } else { + throw; + } } semaphoreIndex = (semaphoreIndex + 1) % presentCompleteSemaphore.size(); currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; diff --git a/attachments/23_descriptor_sets.cpp b/attachments/23_descriptor_sets.cpp index 07829c58..814cf302 100644 --- a/attachments/23_descriptor_sets.cpp +++ b/attachments/23_descriptor_sets.cpp @@ -56,9 +56,9 @@ struct Vertex { }; struct UniformBufferObject { - alignas(16) glm::mat4 model; - alignas(16) glm::mat4 view; - alignas(16) glm::mat4 proj; + glm::mat4 model; + glm::mat4 view; + glm::mat4 proj; }; const std::vector vertices = { @@ -136,6 +136,7 @@ class HelloTriangleApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -170,18 +171,7 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - try { - drawFrame(); - } catch (const vk::SystemError& e) { - if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; - } else { - // Rethrow other errors - throw; - } - } + drawFrame(); } device.waitIdle(); @@ -308,8 +298,9 @@ class HelloTriangleApplication { { return strcmp( availableDeviceExtension.extensionName, requiredDeviceExtension ) == 0; } ); } ); - auto features = device.template getFeatures2(); - bool supportsRequiredFeatures = features.template get().dynamicRendering && + auto features = device.template getFeatures2(); + bool supportsRequiredFeatures = features.template get().shaderDrawParameters && + features.template get().dynamicRendering && features.template get().extendedDynamicState; return supportsVulkan1_3 && supportsGraphics && supportsAllRequiredExtensions && supportsRequiredFeatures; @@ -343,11 +334,12 @@ class HelloTriangleApplication { throw std::runtime_error("Could not find a queue for graphics and present -> terminating"); } - // query for Vulkan 1.3 features - vk::StructureChain featureChain = { + // query for required features (Vulkan 1.1 and 1.3) + vk::StructureChain featureChain = { {}, // vk::PhysicalDeviceFeatures2 - {.synchronization2 = true, .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features - {.extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT + { .shaderDrawParameters = true }, // vk::PhysicalDeviceVulkan11Features + { .synchronization2 = true, .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features + { .extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT }; // create a Device @@ -383,6 +375,8 @@ class HelloTriangleApplication { } void createImageViews() { + swapChainImageViews.clear(); + vk::ImageViewCreateInfo imageViewCreateInfo{ .viewType = vk::ImageViewType::e2D, .format = swapChainImageFormat, .subresourceRange = { vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 } }; for ( auto image : swapChainImages ) @@ -695,14 +689,23 @@ class HelloTriangleApplication { queue.submit(submitInfo, *inFlightFences[currentFrame]); - const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], - .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; - result = queue.presentKHR( presentInfoKHR ); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], + .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; + result = queue.presentKHR( presentInfoKHR ); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + recreateSwapChain(); + return; + } else { + throw; + } } semaphoreIndex = (semaphoreIndex + 1) % presentCompleteSemaphore.size(); currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; diff --git a/attachments/24_texture_image.cpp b/attachments/24_texture_image.cpp index 68ee83a9..7c9645c6 100644 --- a/attachments/24_texture_image.cpp +++ b/attachments/24_texture_image.cpp @@ -59,9 +59,9 @@ struct Vertex { }; struct UniformBufferObject { - alignas(16) glm::mat4 model; - alignas(16) glm::mat4 view; - alignas(16) glm::mat4 proj; + glm::mat4 model; + glm::mat4 view; + glm::mat4 proj; }; const std::vector vertices = { @@ -142,6 +142,7 @@ class HelloTriangleApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -177,18 +178,7 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - try { - drawFrame(); - } catch (const vk::SystemError& e) { - if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; - } else { - // Rethrow other errors - throw; - } - } + drawFrame(); } device.waitIdle(); @@ -315,8 +305,9 @@ class HelloTriangleApplication { { return strcmp( availableDeviceExtension.extensionName, requiredDeviceExtension ) == 0; } ); } ); - auto features = device.template getFeatures2(); - bool supportsRequiredFeatures = features.template get().dynamicRendering && + auto features = device.template getFeatures2(); + bool supportsRequiredFeatures = features.template get().shaderDrawParameters && + features.template get().dynamicRendering && features.template get().extendedDynamicState; return supportsVulkan1_3 && supportsGraphics && supportsAllRequiredExtensions && supportsRequiredFeatures; @@ -350,11 +341,12 @@ class HelloTriangleApplication { throw std::runtime_error("Could not find a queue for graphics and present -> terminating"); } - // query for Vulkan 1.3 features - vk::StructureChain featureChain = { + // query for required features (Vulkan 1.1 and 1.3) + vk::StructureChain featureChain = { {}, // vk::PhysicalDeviceFeatures2 - {.synchronization2 = true, .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features - {.extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT + { .shaderDrawParameters = true }, // vk::PhysicalDeviceVulkan11Features + { .synchronization2 = true, .dynamicRendering = true }, // vk::PhysicalDeviceVulkan13Features + { .extendedDynamicState = true } // vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT }; // create a Device @@ -390,6 +382,8 @@ class HelloTriangleApplication { } void createImageViews() { + swapChainImageViews.clear(); + vk::ImageViewCreateInfo imageViewCreateInfo{ .viewType = vk::ImageViewType::e2D, .format = swapChainImageFormat, .subresourceRange = { vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 } }; for ( auto image : swapChainImages ) @@ -799,14 +793,23 @@ class HelloTriangleApplication { queue.submit(submitInfo, *inFlightFences[currentFrame]); - const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], - .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; - result = queue.presentKHR( presentInfoKHR ); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], + .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; + result = queue.presentKHR( presentInfoKHR ); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + recreateSwapChain(); + return; + } else { + throw; + } } semaphoreIndex = (semaphoreIndex + 1) % presentCompleteSemaphore.size(); currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; diff --git a/attachments/25_sampler.cpp b/attachments/25_sampler.cpp index 151dc891..3c48201e 100644 --- a/attachments/25_sampler.cpp +++ b/attachments/25_sampler.cpp @@ -59,9 +59,9 @@ struct Vertex { }; struct UniformBufferObject { - alignas(16) glm::mat4 model; - alignas(16) glm::mat4 view; - alignas(16) glm::mat4 proj; + glm::mat4 model; + glm::mat4 view; + glm::mat4 proj; }; const std::vector vertices = { @@ -144,6 +144,7 @@ class HelloTriangleApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -181,18 +182,7 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - try { - drawFrame(); - } catch (const vk::SystemError& e) { - if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; - } else { - // Rethrow other errors - throw; - } - } + drawFrame(); } device.waitIdle(); @@ -395,6 +385,8 @@ class HelloTriangleApplication { } void createImageViews() { + swapChainImageViews.clear(); + vk::ImageViewCreateInfo imageViewCreateInfo{ .viewType = vk::ImageViewType::e2D, .format = swapChainImageFormat, .subresourceRange = { vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 } }; for ( auto image : swapChainImages ) @@ -834,14 +826,23 @@ class HelloTriangleApplication { queue.submit(submitInfo, *inFlightFences[currentFrame]); - const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], - .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; - result = queue.presentKHR( presentInfoKHR ); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], + .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; + result = queue.presentKHR( presentInfoKHR ); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + recreateSwapChain(); + return; + } else { + throw; + } } semaphoreIndex = (semaphoreIndex + 1) % presentCompleteSemaphore.size(); currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; diff --git a/attachments/26_texture_mapping.cpp b/attachments/26_texture_mapping.cpp index e32e5c36..9a7938d1 100644 --- a/attachments/26_texture_mapping.cpp +++ b/attachments/26_texture_mapping.cpp @@ -61,9 +61,9 @@ struct Vertex { }; struct UniformBufferObject { - alignas(16) glm::mat4 model; - alignas(16) glm::mat4 view; - alignas(16) glm::mat4 proj; + glm::mat4 model; + glm::mat4 view; + glm::mat4 proj; }; const std::vector vertices = { @@ -146,6 +146,7 @@ class HelloTriangleApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -386,6 +387,8 @@ class HelloTriangleApplication { } void createImageViews() { + swapChainImageViews.clear(); + vk::ImageViewCreateInfo imageViewCreateInfo{ .viewType = vk::ImageViewType::e2D, .format = swapChainImageFormat, .subresourceRange = { vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 } }; for ( auto image : swapChainImages ) @@ -912,11 +915,9 @@ class HelloTriangleApplication { } } catch (const vk::SystemError& e) { if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during presentation" << std::endl; + recreateSwapChain(); + return; } else { - // Rethrow other errors throw; } } diff --git a/attachments/27_depth_buffering.cpp b/attachments/27_depth_buffering.cpp index 48bc3767..b8acf4c8 100644 --- a/attachments/27_depth_buffering.cpp +++ b/attachments/27_depth_buffering.cpp @@ -62,9 +62,9 @@ struct Vertex { }; struct UniformBufferObject { - alignas(16) glm::mat4 model; - alignas(16) glm::mat4 view; - alignas(16) glm::mat4 proj; + glm::mat4 model; + glm::mat4 view; + glm::mat4 proj; }; const std::vector vertices = { @@ -157,6 +157,7 @@ class HelloTriangleApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -1040,11 +1041,9 @@ class HelloTriangleApplication { } } catch (const vk::SystemError& e) { if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during presentation" << std::endl; + recreateSwapChain(); + return; } else { - // Rethrow other errors throw; } } diff --git a/attachments/28_model_loading.cpp b/attachments/28_model_loading.cpp index 89eaf538..68197444 100644 --- a/attachments/28_model_loading.cpp +++ b/attachments/28_model_loading.cpp @@ -160,6 +160,7 @@ class HelloTriangleApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -199,18 +200,7 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - try { - drawFrame(); - } catch (const vk::SystemError& e) { - if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; - } else { - // Rethrow other errors - throw; - } - } + drawFrame(); } device.waitIdle(); @@ -1084,14 +1074,23 @@ class HelloTriangleApplication { queue.submit(submitInfo, *inFlightFences[currentFrame]); - const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], - .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; - result = queue.presentKHR(presentInfoKHR); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], + .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; + result = queue.presentKHR(presentInfoKHR); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + recreateSwapChain(); + return; + } else { + throw; + } } semaphoreIndex = (semaphoreIndex + 1) % presentCompleteSemaphore.size(); currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; diff --git a/attachments/29_mipmapping.cpp b/attachments/29_mipmapping.cpp index 37c0b56e..d02dfe2c 100644 --- a/attachments/29_mipmapping.cpp +++ b/attachments/29_mipmapping.cpp @@ -161,6 +161,7 @@ class HelloTriangleApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -200,18 +201,7 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - try { - drawFrame(); - } catch (const vk::SystemError& e) { - if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; - } else { - // Rethrow other errors - throw; - } - } + drawFrame(); } device.waitIdle(); @@ -1150,14 +1140,23 @@ class HelloTriangleApplication { queue.submit(submitInfo, *inFlightFences[currentFrame]); - const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], - .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; - result = queue.presentKHR(presentInfoKHR); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], + .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; + result = queue.presentKHR(presentInfoKHR); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + recreateSwapChain(); + return; + } else { + throw; + } } semaphoreIndex = (semaphoreIndex + 1) % presentCompleteSemaphore.size(); currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; diff --git a/attachments/30_multisampling.cpp b/attachments/30_multisampling.cpp index 5a581319..673d042f 100644 --- a/attachments/30_multisampling.cpp +++ b/attachments/30_multisampling.cpp @@ -166,6 +166,7 @@ class HelloTriangleApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -207,18 +208,7 @@ class HelloTriangleApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - try { - drawFrame(); - } catch (const vk::SystemError& e) { - if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; - } else { - // Rethrow other errors - throw; - } - } + drawFrame(); } device.waitIdle(); @@ -1221,6 +1211,7 @@ class HelloTriangleApplication { .signalSemaphoreCount = 1, .pSignalSemaphores = &*renderFinishedSemaphore[imageIndex] }; queue.submit(submitInfo, *inFlightFences[currentFrame]); + try { const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; @@ -1233,15 +1224,12 @@ class HelloTriangleApplication { } } catch (const vk::SystemError& e) { if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during presentation" << std::endl; + recreateSwapChain(); + return; } else { - // Rethrow other errors throw; } } - semaphoreIndex = (semaphoreIndex + 1) % presentCompleteSemaphore.size(); currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; } diff --git a/attachments/31_compute_shader.cpp b/attachments/31_compute_shader.cpp index eee05ece..e80490c1 100644 --- a/attachments/31_compute_shader.cpp +++ b/attachments/31_compute_shader.cpp @@ -137,6 +137,7 @@ class ComputeShaderApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -174,22 +175,11 @@ class ComputeShaderApplication { void mainLoop() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); - try { - drawFrame(); - // We want to animate the particle system using the last frames time to get smooth, frame-rate independent animation - double currentTime = glfwGetTime(); - lastFrameTime = (currentTime - lastTime) * 1000.0; - lastTime = currentTime; - } catch (const vk::SystemError& e) { - if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during shutdown" << std::endl; - } else { - // Rethrow other errors - throw; - } - } + drawFrame(); + // We want to animate the particle system using the last frames time to get smooth, frame-rate independent animation + double currentTime = glfwGetTime(); + lastFrameTime = (currentTime - lastTime) * 1000.0; + lastTime = currentTime; } device.waitIdle(); @@ -882,11 +872,9 @@ class ComputeShaderApplication { } } catch (const vk::SystemError& e) { if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { - // Swapchain is out of date, this can happen during window close - // Just ignore and continue to close - std::cout << "Ignoring ErrorOutOfDateKHR during presentation" << std::endl; + recreateSwapChain(); + return; } else { - // Rethrow other errors throw; } } diff --git a/attachments/32_ecosystem_utilities.cpp b/attachments/32_ecosystem_utilities.cpp index 8375ad05..903a6b41 100644 --- a/attachments/32_ecosystem_utilities.cpp +++ b/attachments/32_ecosystem_utilities.cpp @@ -171,6 +171,7 @@ class HelloTriangleApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan Compatibility Example", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -1584,19 +1585,28 @@ class HelloTriangleApplication { queue.submit(submitInfo, *inFlightFences[currentFrame]); } - const vk::PresentInfoKHR presentInfoKHR{ - .waitSemaphoreCount = 1, - .pWaitSemaphores = &*renderFinishedSemaphore[currentFrame], - .swapchainCount = 1, - .pSwapchains = &*swapChain, - .pImageIndices = &imageIndex - }; - result = queue.presentKHR(presentInfoKHR); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ + .waitSemaphoreCount = 1, + .pWaitSemaphores = &*renderFinishedSemaphore[currentFrame], + .swapchainCount = 1, + .pSwapchains = &*swapChain, + .pImageIndices = &imageIndex + }; + result = queue.presentKHR(presentInfoKHR); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + recreateSwapChain(); + return; + } else { + throw; + } } currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; } From 914f3cb0edefa4bd6c1e910d662623702a1d2a3a Mon Sep 17 00:00:00 2001 From: swinston Date: Fri, 22 Aug 2025 17:12:04 -0700 Subject: [PATCH 5/7] Bring changes forward for resize. --- attachments/35_gltf_ktx.cpp | 27 ++++++++++++++-------- attachments/36_multiple_objects.cpp | 36 ++++++++++++++++++----------- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/attachments/35_gltf_ktx.cpp b/attachments/35_gltf_ktx.cpp index c2139878..5e880c17 100644 --- a/attachments/35_gltf_ktx.cpp +++ b/attachments/35_gltf_ktx.cpp @@ -302,6 +302,7 @@ class VulkanApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -1308,15 +1309,23 @@ class VulkanApplication { .signalSemaphoreCount = 1, .pSignalSemaphores = &*renderFinishedSemaphore[imageIndex] }; queue.submit(submitInfo, *inFlightFences[currentFrame]); - - const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], - .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; - result = queue.presentKHR(presentInfoKHR); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], + .swapchainCount = 1, .pSwapchains = &*swapChain, .pImageIndices = &imageIndex }; + result = queue.presentKHR(presentInfoKHR); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + recreateSwapChain(); + return; + } else { + throw; + } } currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; } diff --git a/attachments/36_multiple_objects.cpp b/attachments/36_multiple_objects.cpp index 85da0542..eb106b6d 100644 --- a/attachments/36_multiple_objects.cpp +++ b/attachments/36_multiple_objects.cpp @@ -348,6 +348,7 @@ class VulkanApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); glfwSetWindowUserPointer(window, this); @@ -1437,19 +1438,28 @@ class VulkanApplication { }; queue.submit(submitInfo, *inFlightFences[currentFrame]); - const vk::PresentInfoKHR presentInfoKHR{ - .waitSemaphoreCount = 1, - .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], - .swapchainCount = 1, - .pSwapchains = &*swapChain, - .pImageIndices = &imageIndex - }; - result = queue.presentKHR(presentInfoKHR); - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + try { + const vk::PresentInfoKHR presentInfoKHR{ + .waitSemaphoreCount = 1, + .pWaitSemaphores = &*renderFinishedSemaphore[imageIndex], + .swapchainCount = 1, + .pSwapchains = &*swapChain, + .pImageIndices = &imageIndex + }; + result = queue.presentKHR(presentInfoKHR); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + recreateSwapChain(); + return; + } else { + throw; + } } currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; } From eede8730229e76fbf7d3b721b10a8049526268d9 Mon Sep 17 00:00:00 2001 From: swinston Date: Sat, 23 Aug 2025 12:02:55 -0700 Subject: [PATCH 6/7] Recreate semaphores on swapchain resize and improve attachment transitions for dynamic rendering. --- attachments/33_vulkan_profiles.cpp | 115 +++++++++++++++++++---------- attachments/34_android.cpp | 82 ++++++++++++-------- 2 files changed, 127 insertions(+), 70 deletions(-) diff --git a/attachments/33_vulkan_profiles.cpp b/attachments/33_vulkan_profiles.cpp index 4dee36c6..43125d2a 100644 --- a/attachments/33_vulkan_profiles.cpp +++ b/attachments/33_vulkan_profiles.cpp @@ -162,6 +162,8 @@ class HelloTriangleApplication { void initWindow() { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan Profiles Demo", nullptr, nullptr); glfwSetWindowUserPointer(window, this); glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); @@ -221,23 +223,11 @@ class HelloTriangleApplication { } void cleanupSwapChain() { - colorImageView = nullptr; - colorImage = nullptr; - colorImageMemory = nullptr; - - depthImageView = nullptr; - depthImage = nullptr; - depthImageMemory = nullptr; - - for (auto& framebuffer : swapChainFramebuffers) { - framebuffer = nullptr; - } + swapChainFramebuffers.clear(); + swapChainImageViews.clear(); - for (auto& imageView : swapChainImageViews) { - imageView = nullptr; - } - - swapChain = nullptr; + // Semaphores tied to swapchain image indices need to be rebuilt on resize + presentCompleteSemaphore.clear(); } void cleanup() { @@ -268,6 +258,13 @@ class HelloTriangleApplication { createColorResources(); createDepthResources(); + + // Recreate per-swapchain-image present semaphores after resize + presentCompleteSemaphore.reserve(swapChainImages.size()); + vk::SemaphoreCreateInfo semaphoreInfo{}; + for (size_t i = 0; i < swapChainImages.size(); ++i) { + presentCompleteSemaphore.push_back(device.createSemaphore(semaphoreInfo)); + } } void createInstance() { @@ -1338,8 +1335,45 @@ class HelloTriangleApplication { void recordCommandBuffer(uint32_t imageIndex) { commandBuffers[currentFrame].begin({}); - // Transition the swapchain image to the correct layout for rendering - vk::ImageMemoryBarrier imageBarrier{ + // Transition the attachments to the correct layouts for dynamic rendering + // 1) Multisampled color attachment image -> ColorAttachmentOptimal + vk::ImageMemoryBarrier colorAttachmentBarrier{ + .srcAccessMask = vk::AccessFlagBits::eNone, + .dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, + .oldLayout = vk::ImageLayout::eUndefined, + .newLayout = vk::ImageLayout::eColorAttachmentOptimal, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = *colorImage, + .subresourceRange = { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + } + }; + + // 2) Depth attachment image -> DepthStencilAttachmentOptimal + vk::ImageMemoryBarrier depthAttachmentBarrier{ + .srcAccessMask = vk::AccessFlagBits::eNone, + .dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite, + .oldLayout = vk::ImageLayout::eUndefined, + .newLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = *depthImage, + .subresourceRange = { + .aspectMask = vk::ImageAspectFlagBits::eDepth, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + } + }; + + // 3) Resolve (swapchain) image -> ColorAttachmentOptimal + vk::ImageMemoryBarrier swapchainResolveBarrier{ .srcAccessMask = vk::AccessFlagBits::eNone, .dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, .oldLayout = vk::ImageLayout::eUndefined, @@ -1358,11 +1392,11 @@ class HelloTriangleApplication { commandBuffers[currentFrame].pipelineBarrier( vk::PipelineStageFlagBits::eTopOfPipe, - vk::PipelineStageFlagBits::eColorAttachmentOutput, + vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eEarlyFragmentTests, vk::DependencyFlagBits::eByRegion, std::array{}, std::array{}, - std::array{imageBarrier} + std::array{colorAttachmentBarrier, depthAttachmentBarrier, swapchainResolveBarrier} ); // Clear values for color and depth @@ -1548,28 +1582,29 @@ class HelloTriangleApplication { }; queue.submit(submitInfo, *inFlightFences[currentFrame]); - const vk::PresentInfoKHR presentInfoKHR{ - .waitSemaphoreCount = 1, - .pWaitSemaphores = &*presentCompleteSemaphore[imageIndex], - .swapchainCount = 1, - .pSwapchains = &*swapChain, - .pImageIndices = &imageIndex - }; - - vk::Result result; try { - result = queue.presentKHR(presentInfoKHR); - } catch (vk::OutOfDateKHRError&) { - result = vk::Result::eErrorOutOfDateKHR; - } - - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("failed to present swap chain image!"); + const vk::PresentInfoKHR presentInfoKHR{ + .waitSemaphoreCount = 1, + .pWaitSemaphores = &*presentCompleteSemaphore[imageIndex], + .swapchainCount = 1, + .pSwapchains = &*swapChain, + .pImageIndices = &imageIndex + }; + auto result = queue.presentKHR(presentInfoKHR); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + recreateSwapChain(); + return; + } else { + throw; + } } - currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; } diff --git a/attachments/34_android.cpp b/attachments/34_android.cpp index a4bf7396..f341e27d 100644 --- a/attachments/34_android.cpp +++ b/attachments/34_android.cpp @@ -243,6 +243,7 @@ class HelloTriangleApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan Cross-Platform", nullptr, nullptr); glfwSetWindowUserPointer(window, this); glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); @@ -1177,7 +1178,7 @@ class HelloTriangleApplication { // Create synchronization objects void createSyncObjects() { imageAvailableSemaphores.reserve(MAX_FRAMES_IN_FLIGHT); - renderFinishedSemaphores.reserve(MAX_FRAMES_IN_FLIGHT); + renderFinishedSemaphores.reserve(swapChainImages.size()); inFlightFences.reserve(MAX_FRAMES_IN_FLIGHT); vk::SemaphoreCreateInfo semaphoreInfo{}; @@ -1187,22 +1188,21 @@ class HelloTriangleApplication { for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { imageAvailableSemaphores.push_back(device.createSemaphore(semaphoreInfo)); - renderFinishedSemaphores.push_back(device.createSemaphore(semaphoreInfo)); inFlightFences.push_back(device.createFence(fenceInfo)); } + + for (size_t i = 0; i < swapChainImages.size(); i++) { + renderFinishedSemaphores.push_back(device.createSemaphore(semaphoreInfo)); + } } // Clean up swap chain void cleanupSwapChain() { - for (auto& framebuffer : swapChainFramebuffers) { - framebuffer = nullptr; - } + swapChainFramebuffers.clear(); + swapChainImageViews.clear(); - for (auto& imageView : swapChainImageViews) { - imageView = nullptr; - } - - swapChain = nullptr; + // Semaphores tied to swapchain image indices need to be rebuilt on resize + renderFinishedSemaphores.clear(); } // Record command buffer @@ -1281,30 +1281,34 @@ class HelloTriangleApplication { .commandBufferCount = 1, .pCommandBuffers = &*commandBuffers[currentFrame], .signalSemaphoreCount = 1, - .pSignalSemaphores = &*renderFinishedSemaphores[currentFrame] + .pSignalSemaphores = &*renderFinishedSemaphores[imageIndex] }; queue.submit(submitInfo, *inFlightFences[currentFrame]); - const vk::PresentInfoKHR presentInfoKHR{ - .waitSemaphoreCount = 1, - .pWaitSemaphores = &*renderFinishedSemaphores[currentFrame], - .swapchainCount = 1, - .pSwapchains = &*swapChain, - .pImageIndices = &imageIndex - }; - - vk::Result result; try { - result = queue.presentKHR(presentInfoKHR); - } catch (vk::OutOfDateKHRError&) { - result = vk::Result::eErrorOutOfDateKHR; - } + const vk::PresentInfoKHR presentInfoKHR{ + .waitSemaphoreCount = 1, + .pWaitSemaphores = &*renderFinishedSemaphores[imageIndex], + .swapchainCount = 1, + .pSwapchains = &*swapChain, + .pImageIndices = &imageIndex + }; - if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { - framebufferResized = false; - recreateSwapChain(); - } else if (result != vk::Result::eSuccess) { - throw std::runtime_error("Failed to present swap chain image"); + vk::Result result = queue.presentKHR(presentInfoKHR); + + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("Failed to present swap chain image"); + } + } catch (const vk::SystemError& e) { + if (e.code().value() == static_cast(vk::Result::eErrorOutOfDateKHR)) { + recreateSwapChain(); + return; + } else { + throw; + } } currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; @@ -1312,16 +1316,34 @@ class HelloTriangleApplication { // Recreate swap chain void recreateSwapChain() { +#if !PLATFORM_ANDROID + // On desktop, wait until the framebuffer has a non-zero size (e.g., when window is minimized) + int width = 0, height = 0; + if (window) { + glfwGetFramebufferSize(window, &width, &height); + while (width == 0 || height == 0) { + glfwGetFramebufferSize(window, &width, &height); + glfwWaitEvents(); + } + } +#endif // Wait for device to finish operations device.waitIdle(); // Clean up old swap chain cleanupSwapChain(); - // Create new swap chain + // Create new swap chain and dependent resources createSwapChain(); createImageViews(); createFramebuffers(); + + // Recreate per-swapchain-image present semaphores for presenting + renderFinishedSemaphores.reserve(swapChainImages.size()); + vk::SemaphoreCreateInfo semaphoreInfo{}; + for (size_t i = 0; i < swapChainImages.size(); ++i) { + renderFinishedSemaphores.push_back(device.createSemaphore(semaphoreInfo)); + } } // Get required extensions From 6cf147344fe4df78efe3325f97d829318a4958fb Mon Sep 17 00:00:00 2001 From: swinston Date: Sat, 23 Aug 2025 17:17:36 -0700 Subject: [PATCH 7/7] Handle framebuffer resize with swapchain recreation - Add `framebufferResizeCallback` to monitor window resize events. - Implement `recreateSwapChain` to handle swapchain rebuild during resize scenarios. - Adjust `drawFrame` logic to manage `ErrorOutOfDateKHR` and `SuboptimalKHR` results robustly. --- attachments/37_multithreading.cpp | 58 ++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/attachments/37_multithreading.cpp b/attachments/37_multithreading.cpp index b08274c6..e6aea889 100644 --- a/attachments/37_multithreading.cpp +++ b/attachments/37_multithreading.cpp @@ -189,7 +189,7 @@ class MultithreadedApplication { double lastFrameTime = 0.0; - // Removed resize-related variables and FSM state management as per simplification request + bool framebufferResized = false; double lastTime = 0.0f; @@ -240,7 +240,7 @@ class MultithreadedApplication { []( const auto & format ) { return format.format == vk::Format::eB8G8R8A8Srgb && format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear; } ); return formatIt != availableFormats.end() ? *formatIt : availableFormats[0]; } - + static vk::PresentModeKHR chooseSwapPresentMode(const std::vector& availablePresentModes) { assert(std::ranges::any_of(availablePresentModes, [](auto presentMode){ return presentMode == vk::PresentModeKHR::eFifo; })); return std::ranges::any_of(availablePresentModes, @@ -282,14 +282,22 @@ class MultithreadedApplication { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan Multithreading", nullptr, nullptr); glfwSetWindowUserPointer(window, this); + glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); lastTime = glfwGetTime(); } + static void framebufferResizeCallback(GLFWwindow* window, int, int) { + auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); + if (app) { + app->framebufferResized = true; + } + } + void initVulkan() { createInstance(); createSurface(); @@ -439,6 +447,28 @@ class MultithreadedApplication { swapChain = nullptr; } + void recreateSwapChain() { + int width = 0, height = 0; + glfwGetFramebufferSize(window, &width, &height); + while (width == 0 || height == 0) { + glfwGetFramebufferSize(window, &width, &height); + glfwWaitEvents(); + } + device.waitIdle(); + + cleanupSwapChain(); + + createSwapChain(); + createImageViews(); + createComputeDescriptorSetLayout(); + createGraphicsPipeline(); + createComputePipeline(); + createShaderStorageBuffers(); + createUniformBuffers(); + createDescriptorPool(); + createComputeDescriptorSets(); + } + void stopThreads() { shouldExit.store(true, std::memory_order_release); @@ -1075,10 +1105,22 @@ class MultithreadedApplication { // Wait for the previous frame to finish while (vk::Result::eTimeout == device.waitForFences(*inFlightFences[currentFrame], vk::True, UINT64_MAX)) ; - device.resetFences(*inFlightFences[currentFrame]); + + // If the framebuffer was resized, rebuild the swap chain before acquiring a new image + if (framebufferResized) { + recreateSwapChain(); + framebufferResized = false; + return; + } // Acquire the next image auto [result, imageIndex] = swapChain.acquireNextImage(UINT64_MAX, *imageAvailableSemaphores[currentFrame], nullptr); + if (result == vk::Result::eErrorOutOfDateKHR) { + recreateSwapChain(); + return; + } else if (result != vk::Result::eSuccess && result != vk::Result::eSuboptimalKHR) { + throw std::runtime_error("failed to acquire swap chain image!"); + } // Update timeline values for synchronization uint64_t computeWaitValue = timelineValue; @@ -1168,6 +1210,7 @@ class MultithreadedApplication { // Submit graphics work { std::lock_guard lock(queueSubmitMutex); + device.resetFences(*inFlightFences[currentFrame]); queue.submit(graphicsSubmitInfo, *inFlightFences[currentFrame]); } @@ -1194,6 +1237,13 @@ class MultithreadedApplication { }; result = queue.presentKHR(presentInfo); + if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) { + framebufferResized = false; + recreateSwapChain(); + return; + } else if (result != vk::Result::eSuccess) { + throw std::runtime_error("failed to present swap chain image!"); + } // Move to the next frame currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;