From 3275850304a25b528f554ce2ff787b7465a3b832 Mon Sep 17 00:00:00 2001 From: jamylak Date: Wed, 25 Mar 2026 19:23:42 +1100 Subject: [PATCH 01/18] offline destructor --- include/offline_sdf_renderer.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/offline_sdf_renderer.h b/include/offline_sdf_renderer.h index 3515c5e..9450e68 100644 --- a/include/offline_sdf_renderer.h +++ b/include/offline_sdf_renderer.h @@ -95,6 +95,7 @@ class OfflineSDFRenderer : public SDFRenderer { OfflineSDFRenderer( const std::string &fragShaderPath, bool useToyTemplate = false, OfflineRenderOptions options = {}); + ~OfflineSDFRenderer(); void setup(); void renderFrames(); }; From 992ce890fb868a2444931a08ffe27380e7257927 Mon Sep 17 00:00:00 2001 From: jamylak Date: Wed, 25 Mar 2026 19:31:01 +1100 Subject: [PATCH 02/18] set counts to `0` on destroy in `vkutils` --- include/vkutils.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/vkutils.h b/include/vkutils.h index f3b778c..6a3c5ea 100644 --- a/include/vkutils.h +++ b/include/vkutils.h @@ -1438,6 +1438,7 @@ destroySwapchainImageViews(VkDevice device, vkDestroyImageView(device, swapchainImageViews.imageViews[i], nullptr); swapchainImageViews.imageViews[i] = VK_NULL_HANDLE; } + swapchainImageViews.count = 0; } static void destroyFrameBuffers(VkDevice device, @@ -1446,6 +1447,7 @@ static void destroyFrameBuffers(VkDevice device, vkDestroyFramebuffer(device, frameBuffers.framebuffers[i], nullptr); frameBuffers.framebuffers[i] = VK_NULL_HANDLE; } + frameBuffers.count = 0; } static void destroyFences(VkDevice device, Fences &fences) noexcept { @@ -1453,6 +1455,7 @@ static void destroyFences(VkDevice device, Fences &fences) noexcept { vkDestroyFence(device, fences.fences[i], nullptr); fences.fences[i] = VK_NULL_HANDLE; } + fences.count = 0; } static void destroySemaphores(VkDevice device, @@ -1461,6 +1464,7 @@ static void destroySemaphores(VkDevice device, vkDestroySemaphore(device, semaphores.semaphores[i], nullptr); semaphores.semaphores[i] = VK_NULL_HANDLE; } + semaphores.count = 0; } } // namespace vkutils From 1e5b1b5a0510c903d9d5eb2278383d155d95a1a9 Mon Sep 17 00:00:00 2001 From: jamylak Date: Wed, 25 Mar 2026 19:31:40 +1100 Subject: [PATCH 03/18] initial empty online values --- include/online_sdf_renderer.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/online_sdf_renderer.h b/include/online_sdf_renderer.h index 0f37e8e..46c525d 100644 --- a/include/online_sdf_renderer.h +++ b/include/online_sdf_renderer.h @@ -29,19 +29,19 @@ class OnlineSDFRenderer : public SDFRenderer { private: // GLFW Setup GLFWApplication app; - GLFWwindow *window; + GLFWwindow *window = nullptr; // Vulkan Setup - VkSurfaceKHR surface; - VkSurfaceFormatKHR swapchainFormat; + VkSurfaceKHR surface = VK_NULL_HANDLE; + VkSurfaceFormatKHR swapchainFormat{}; vkutils::Semaphores imageAvailableSemaphores; vkutils::Semaphores renderFinishedSemaphores; // Shader Modules. // Full screen quad vert shader + frag shader VkSwapchainKHR swapchain = VK_NULL_HANDLE; - VkSurfaceCapabilitiesKHR surfaceCapabilities; - VkExtent2D swapchainSize; + VkSurfaceCapabilitiesKHR surfaceCapabilities{}; + VkExtent2D swapchainSize{}; vkutils::SwapchainImages swapchainImages; vkutils::SwapchainImageViews swapchainImageViews; vkutils::FrameBuffers frameBuffers; From c53ef5013c507cd4a936133a433e8647da7a5e1a Mon Sep 17 00:00:00 2001 From: jamylak Date: Wed, 25 Mar 2026 19:31:44 +1100 Subject: [PATCH 04/18] online destructor --- include/online_sdf_renderer.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/online_sdf_renderer.h b/include/online_sdf_renderer.h index 46c525d..6ca3f16 100644 --- a/include/online_sdf_renderer.h +++ b/include/online_sdf_renderer.h @@ -74,6 +74,7 @@ class OnlineSDFRenderer : public SDFRenderer { OnlineSDFRenderer(const std::string &fragShaderPath, bool useToyTemplate = false, OnlineRenderOptions options = {}); + ~OnlineSDFRenderer(); void setup(); void gameLoop(); }; From fb0d66d47f354e049c3b8351b502abfd00ffaa4f Mon Sep 17 00:00:00 2001 From: jamylak Date: Wed, 25 Mar 2026 19:36:32 +1100 Subject: [PATCH 05/18] safely destroy pipeline --- src/offline_sdf_renderer.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/offline_sdf_renderer.cpp b/src/offline_sdf_renderer.cpp index 6d2949e..b3d8e43 100644 --- a/src/offline_sdf_renderer.cpp +++ b/src/offline_sdf_renderer.cpp @@ -486,9 +486,18 @@ void OfflineSDFRenderer::waitForSlotEncode(uint32_t slotIndex) { } void OfflineSDFRenderer::destroyPipeline() { - vkDestroyPipeline(logicalDevice, pipeline, nullptr); - vkDestroyPipelineLayout(logicalDevice, pipelineLayout, nullptr); - vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); + if (pipeline != VK_NULL_HANDLE) { + vkDestroyPipeline(logicalDevice, pipeline, nullptr); + pipeline = VK_NULL_HANDLE; + } + if (pipelineLayout != VK_NULL_HANDLE) { + vkDestroyPipelineLayout(logicalDevice, pipelineLayout, nullptr); + pipelineLayout = VK_NULL_HANDLE; + } + if (fragShaderModule != VK_NULL_HANDLE) { + vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); + fragShaderModule = VK_NULL_HANDLE; + } } void OfflineSDFRenderer::destroyRenderContext() { From 1b0e4b9eac6797082564aa7650db2cbc3c2fefa1 Mon Sep 17 00:00:00 2001 From: jamylak Date: Wed, 25 Mar 2026 20:12:17 +1100 Subject: [PATCH 06/18] online call destroy on destroy --- src/online_sdf_renderer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/online_sdf_renderer.cpp b/src/online_sdf_renderer.cpp index 005b873..ea89a82 100644 --- a/src/online_sdf_renderer.cpp +++ b/src/online_sdf_renderer.cpp @@ -21,6 +21,8 @@ OnlineSDFRenderer::OnlineSDFRenderer( : SDFRenderer(fragShaderPath, useToyTemplate, options.debugDumpPPMDir), options(std::move(options)) {} +OnlineSDFRenderer::~OnlineSDFRenderer() { destroy(); } + void OnlineSDFRenderer::setup() { glfwSetup(); vulkanSetup(); From 822621eb362bc70c2c44fcb62512562d7e03ce4c Mon Sep 17 00:00:00 2001 From: jamylak Date: Wed, 25 Mar 2026 20:12:34 +1100 Subject: [PATCH 07/18] safe online destroy pipeline --- src/online_sdf_renderer.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/online_sdf_renderer.cpp b/src/online_sdf_renderer.cpp index ea89a82..6b207c0 100644 --- a/src/online_sdf_renderer.cpp +++ b/src/online_sdf_renderer.cpp @@ -138,9 +138,18 @@ void OnlineSDFRenderer::createCommandBuffers() { } void OnlineSDFRenderer::destroyPipeline() { - vkDestroyPipeline(logicalDevice, pipeline, nullptr); - vkDestroyPipelineLayout(logicalDevice, pipelineLayout, nullptr); - vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); + if (pipeline != VK_NULL_HANDLE) { + vkDestroyPipeline(logicalDevice, pipeline, nullptr); + pipeline = VK_NULL_HANDLE; + } + if (pipelineLayout != VK_NULL_HANDLE) { + vkDestroyPipelineLayout(logicalDevice, pipelineLayout, nullptr); + pipelineLayout = VK_NULL_HANDLE; + } + if (fragShaderModule != VK_NULL_HANDLE) { + vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); + fragShaderModule = VK_NULL_HANDLE; + } } void OnlineSDFRenderer::destroyRenderContext() { From ca6139a42196bafee523122ce431f1be363ae899 Mon Sep 17 00:00:00 2001 From: jamylak Date: Wed, 25 Mar 2026 20:12:46 +1100 Subject: [PATCH 08/18] remove manual online destroy --- src/online_sdf_renderer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/online_sdf_renderer.cpp b/src/online_sdf_renderer.cpp index 6b207c0..a8d77b4 100644 --- a/src/online_sdf_renderer.cpp +++ b/src/online_sdf_renderer.cpp @@ -303,7 +303,6 @@ void OnlineSDFRenderer::gameLoop() { filewatcher->stopWatching(); spdlog::info("Done!"); - destroy(); } void OnlineSDFRenderer::destroy() { From 4eac4fec583221e8af45dc97cd44fd73ddd9c7ae Mon Sep 17 00:00:00 2001 From: jamylak Date: Wed, 25 Mar 2026 20:29:50 +1100 Subject: [PATCH 09/18] simplify offline destroy pipeline --- src/offline_sdf_renderer.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/offline_sdf_renderer.cpp b/src/offline_sdf_renderer.cpp index b3d8e43..cd86c76 100644 --- a/src/offline_sdf_renderer.cpp +++ b/src/offline_sdf_renderer.cpp @@ -486,18 +486,12 @@ void OfflineSDFRenderer::waitForSlotEncode(uint32_t slotIndex) { } void OfflineSDFRenderer::destroyPipeline() { - if (pipeline != VK_NULL_HANDLE) { - vkDestroyPipeline(logicalDevice, pipeline, nullptr); - pipeline = VK_NULL_HANDLE; - } - if (pipelineLayout != VK_NULL_HANDLE) { - vkDestroyPipelineLayout(logicalDevice, pipelineLayout, nullptr); - pipelineLayout = VK_NULL_HANDLE; - } - if (fragShaderModule != VK_NULL_HANDLE) { - vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); - fragShaderModule = VK_NULL_HANDLE; - } + vkDestroyPipeline(logicalDevice, pipeline, nullptr); + pipeline = VK_NULL_HANDLE; + vkDestroyPipelineLayout(logicalDevice, pipelineLayout, nullptr); + pipelineLayout = VK_NULL_HANDLE; + vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); + fragShaderModule = VK_NULL_HANDLE; } void OfflineSDFRenderer::destroyRenderContext() { From 926805c5e0f6fd3d463486b42512650c51a771f8 Mon Sep 17 00:00:00 2001 From: jamylak Date: Wed, 25 Mar 2026 20:31:02 +1100 Subject: [PATCH 10/18] online destroy pipeline --- src/online_sdf_renderer.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/online_sdf_renderer.cpp b/src/online_sdf_renderer.cpp index a8d77b4..eb90f74 100644 --- a/src/online_sdf_renderer.cpp +++ b/src/online_sdf_renderer.cpp @@ -138,18 +138,12 @@ void OnlineSDFRenderer::createCommandBuffers() { } void OnlineSDFRenderer::destroyPipeline() { - if (pipeline != VK_NULL_HANDLE) { - vkDestroyPipeline(logicalDevice, pipeline, nullptr); - pipeline = VK_NULL_HANDLE; - } - if (pipelineLayout != VK_NULL_HANDLE) { - vkDestroyPipelineLayout(logicalDevice, pipelineLayout, nullptr); - pipelineLayout = VK_NULL_HANDLE; - } - if (fragShaderModule != VK_NULL_HANDLE) { - vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); - fragShaderModule = VK_NULL_HANDLE; - } + vkDestroyPipeline(logicalDevice, pipeline, nullptr); + pipeline = VK_NULL_HANDLE; + vkDestroyPipelineLayout(logicalDevice, pipelineLayout, nullptr); + pipelineLayout = VK_NULL_HANDLE; + vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); + fragShaderModule = VK_NULL_HANDLE; } void OnlineSDFRenderer::destroyRenderContext() { From 616873501b95bb7c3bd6337429aa383894b1a7ee Mon Sep 17 00:00:00 2001 From: jamylak Date: Wed, 25 Mar 2026 20:33:30 +1100 Subject: [PATCH 11/18] offline destructor --- src/offline_sdf_renderer.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/offline_sdf_renderer.cpp b/src/offline_sdf_renderer.cpp index cd86c76..c817887 100644 --- a/src/offline_sdf_renderer.cpp +++ b/src/offline_sdf_renderer.cpp @@ -16,6 +16,11 @@ OfflineSDFRenderer::OfflineSDFRenderer( maxFrames(options.maxFrames), encodeSettings(std::move(options.encodeSettings)) {} +OfflineSDFRenderer::~OfflineSDFRenderer() { + stopEncoding(); + destroy(); +} + uint32_t OfflineSDFRenderer::validateRingSize(uint32_t value) { if (value == 0 || value > MAX_FRAME_SLOTS) { throw std::runtime_error("ringSize must be 1..MAX_FRAME_SLOTS"); From 31aff13eed1e4d2f246e8b5e9202317c20eac967 Mon Sep 17 00:00:00 2001 From: jamylak Date: Wed, 25 Mar 2026 20:33:41 +1100 Subject: [PATCH 12/18] remove old destroy --- src/offline_sdf_renderer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/offline_sdf_renderer.cpp b/src/offline_sdf_renderer.cpp index c817887..b2b527e 100644 --- a/src/offline_sdf_renderer.cpp +++ b/src/offline_sdf_renderer.cpp @@ -363,7 +363,6 @@ void OfflineSDFRenderer::renderFrames() { stopEncoding(); spdlog::info("Offline render done."); - destroy(); } void OfflineSDFRenderer::startEncoding() { From c2684bdc69dfc0edaa9b68b8006b035bd14d7c57 Mon Sep 17 00:00:00 2001 From: jamylak Date: Wed, 25 Mar 2026 21:06:08 +1100 Subject: [PATCH 13/18] destroy offline noexcept --- include/offline_sdf_renderer.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/offline_sdf_renderer.h b/include/offline_sdf_renderer.h index 9450e68..d5d6708 100644 --- a/include/offline_sdf_renderer.h +++ b/include/offline_sdf_renderer.h @@ -60,9 +60,10 @@ class OfflineSDFRenderer : public SDFRenderer { void setupRenderContext(); void createPipeline(); void createCommandBuffers(); - void destroyRenderContext(); - void destroyPipeline(); - void destroy(); + void destroyDeviceObjects() noexcept; + void destroyRenderContext() noexcept; + void destroyPipeline() noexcept; + void destroy() noexcept; void recordCommandBuffer(uint32_t slotIndex, uint32_t currentFrame); [[nodiscard]] PPMDebugFrame debugReadbackOffscreenImage(const RingSlot &slot); From 1adca23da3aab463309c18c761411b6a4aaf58a7 Mon Sep 17 00:00:00 2001 From: jamylak Date: Thu, 26 Mar 2026 18:54:04 +1100 Subject: [PATCH 14/18] fix destroy headers --- include/offline_sdf_renderer.h | 7 ++++--- include/online_sdf_renderer.h | 10 ++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/include/offline_sdf_renderer.h b/include/offline_sdf_renderer.h index d5d6708..b1ce280 100644 --- a/include/offline_sdf_renderer.h +++ b/include/offline_sdf_renderer.h @@ -61,8 +61,8 @@ class OfflineSDFRenderer : public SDFRenderer { void createPipeline(); void createCommandBuffers(); void destroyDeviceObjects() noexcept; - void destroyRenderContext() noexcept; - void destroyPipeline() noexcept; + void destroyRenderContextObjects() noexcept; + void destroyPipelineObjects() noexcept; void destroy() noexcept; void recordCommandBuffer(uint32_t slotIndex, uint32_t currentFrame); @@ -86,6 +86,7 @@ class OfflineSDFRenderer : public SDFRenderer { void startEncoding(); void stopEncoding(); + void stopEncodingNoexcept() noexcept; void enqueueEncode(uint32_t slotIndex, uint32_t frameIndex); void waitForSlotEncode(uint32_t slotIndex); void runEncoderLoop(); @@ -96,7 +97,7 @@ class OfflineSDFRenderer : public SDFRenderer { OfflineSDFRenderer( const std::string &fragShaderPath, bool useToyTemplate = false, OfflineRenderOptions options = {}); - ~OfflineSDFRenderer(); + ~OfflineSDFRenderer() noexcept; void setup(); void renderFrames(); }; diff --git a/include/online_sdf_renderer.h b/include/online_sdf_renderer.h index 6ca3f16..8441650 100644 --- a/include/online_sdf_renderer.h +++ b/include/online_sdf_renderer.h @@ -61,9 +61,11 @@ class OnlineSDFRenderer : public SDFRenderer { void createPipeline(); void tryRecreatePipeline(); void calcTimestamps(uint32_t imageIndex); - void destroyRenderContext(); - void destroyPipeline(); - void destroy(); + void destroyRenderContextOrThrow(); + void destroyDeviceObjects() noexcept; + void destroyRenderContextObjects() noexcept; + void destroyPipelineObjects() noexcept; + void destroy() noexcept; [[nodiscard]] vkutils::PushConstants getPushConstants(uint32_t currentFrame) noexcept; @@ -74,7 +76,7 @@ class OnlineSDFRenderer : public SDFRenderer { OnlineSDFRenderer(const std::string &fragShaderPath, bool useToyTemplate = false, OnlineRenderOptions options = {}); - ~OnlineSDFRenderer(); + ~OnlineSDFRenderer() noexcept; void setup(); void gameLoop(); }; From e1d66e6495337bb327ed1d12ecd98a78548e990b Mon Sep 17 00:00:00 2001 From: jamylak Date: Thu, 26 Mar 2026 18:55:29 +1100 Subject: [PATCH 15/18] destroy implementations --- src/offline_sdf_renderer.cpp | 60 ++++++++++++++++------------- src/online_sdf_renderer.cpp | 74 +++++++++++++++++++++++------------- 2 files changed, 80 insertions(+), 54 deletions(-) diff --git a/src/offline_sdf_renderer.cpp b/src/offline_sdf_renderer.cpp index b2b527e..b0efef2 100644 --- a/src/offline_sdf_renderer.cpp +++ b/src/offline_sdf_renderer.cpp @@ -16,8 +16,8 @@ OfflineSDFRenderer::OfflineSDFRenderer( maxFrames(options.maxFrames), encodeSettings(std::move(options.encodeSettings)) {} -OfflineSDFRenderer::~OfflineSDFRenderer() { - stopEncoding(); +OfflineSDFRenderer::~OfflineSDFRenderer() noexcept { + stopEncodingNoexcept(); destroy(); } @@ -462,6 +462,16 @@ void OfflineSDFRenderer::stopEncoding() { encoder.reset(); } +void OfflineSDFRenderer::stopEncodingNoexcept() noexcept { + try { + stopEncoding(); + } catch (const std::exception &e) { + spdlog::error("stopEncoding failed during teardown: {}", e.what()); + } catch (...) { + spdlog::error("stopEncoding failed during teardown: unknown error"); + } +} + void OfflineSDFRenderer::enqueueEncode(uint32_t slotIndex, uint32_t frameIndex) { std::unique_lock lock(encodeMutex); @@ -489,7 +499,7 @@ void OfflineSDFRenderer::waitForSlotEncode(uint32_t slotIndex) { throw std::runtime_error("FFmpeg encoder failed"); } -void OfflineSDFRenderer::destroyPipeline() { +void OfflineSDFRenderer::destroyPipelineObjects() noexcept { vkDestroyPipeline(logicalDevice, pipeline, nullptr); pipeline = VK_NULL_HANDLE; vkDestroyPipelineLayout(logicalDevice, pipelineLayout, nullptr); @@ -498,8 +508,7 @@ void OfflineSDFRenderer::destroyPipeline() { fragShaderModule = VK_NULL_HANDLE; } -void OfflineSDFRenderer::destroyRenderContext() { - VK_CHECK(vkDeviceWaitIdle(logicalDevice)); +void OfflineSDFRenderer::destroyRenderContextObjects() noexcept { for (size_t i = 0; i < ringSize; ++i) { RingSlot &slot = ringSlots[i]; if (slot.framebuffer != VK_NULL_HANDLE) { @@ -531,33 +540,30 @@ void OfflineSDFRenderer::destroyRenderContext() { } } -void OfflineSDFRenderer::destroy() { - VK_CHECK(vkDeviceWaitIdle(logicalDevice)); - vkutils::destroyFences(logicalDevice, fences); - destroyPipeline(); - destroyRenderContext(); - if (renderPass != VK_NULL_HANDLE) { - vkDestroyRenderPass(logicalDevice, renderPass, nullptr); - renderPass = VK_NULL_HANDLE; - } - if (queryPool != VK_NULL_HANDLE) { - vkDestroyQueryPool(logicalDevice, queryPool, nullptr); - queryPool = VK_NULL_HANDLE; - } - if (vertShaderModule != VK_NULL_HANDLE) { - vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr); - vertShaderModule = VK_NULL_HANDLE; - } - if (commandPool != VK_NULL_HANDLE) { - vkDestroyCommandPool(logicalDevice, commandPool, nullptr); - commandPool = VK_NULL_HANDLE; +void OfflineSDFRenderer::destroyDeviceObjects() noexcept { + const VkResult waitResult = vkDeviceWaitIdle(logicalDevice); + if (waitResult != VK_SUCCESS) { + spdlog::warn( + "vkDeviceWaitIdle failed during OfflineSDFRenderer teardown: {}", + static_cast(waitResult)); } + vkutils::destroyFences(logicalDevice, fences); + destroyPipelineObjects(); + destroyRenderContextObjects(); + vkDestroyRenderPass(logicalDevice, renderPass, nullptr); + vkDestroyQueryPool(logicalDevice, queryPool, nullptr); + vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr); + vkDestroyCommandPool(logicalDevice, commandPool, nullptr); +} + +void OfflineSDFRenderer::destroy() noexcept { if (logicalDevice != VK_NULL_HANDLE) { + destroyDeviceObjects(); vkDestroyDevice(logicalDevice, nullptr); - logicalDevice = VK_NULL_HANDLE; } + if (instance != VK_NULL_HANDLE) { + // Instance owns no other offline objects at this point. vkDestroyInstance(instance, nullptr); - instance = VK_NULL_HANDLE; } } diff --git a/src/online_sdf_renderer.cpp b/src/online_sdf_renderer.cpp index eb90f74..bc11b59 100644 --- a/src/online_sdf_renderer.cpp +++ b/src/online_sdf_renderer.cpp @@ -21,7 +21,7 @@ OnlineSDFRenderer::OnlineSDFRenderer( : SDFRenderer(fragShaderPath, useToyTemplate, options.debugDumpPPMDir), options(std::move(options)) {} -OnlineSDFRenderer::~OnlineSDFRenderer() { destroy(); } +OnlineSDFRenderer::~OnlineSDFRenderer() noexcept { destroy(); } void OnlineSDFRenderer::setup() { glfwSetup(); @@ -124,7 +124,7 @@ void OnlineSDFRenderer::tryRecreatePipeline() { } VK_CHECK(vkDeviceWaitIdle(logicalDevice)); - destroyPipeline(); + destroyPipelineObjects(); createPipelineLayoutCommon(); fragShaderModule = vkutils::createShaderModule(logicalDevice, fragSpirv); pipeline = vkutils::createGraphicsPipeline( @@ -137,7 +137,7 @@ void OnlineSDFRenderer::createCommandBuffers() { swapchainImages.count); } -void OnlineSDFRenderer::destroyPipeline() { +void OnlineSDFRenderer::destroyPipelineObjects() noexcept { vkDestroyPipeline(logicalDevice, pipeline, nullptr); pipeline = VK_NULL_HANDLE; vkDestroyPipelineLayout(logicalDevice, pipelineLayout, nullptr); @@ -146,11 +146,34 @@ void OnlineSDFRenderer::destroyPipeline() { fragShaderModule = VK_NULL_HANDLE; } -void OnlineSDFRenderer::destroyRenderContext() { - VK_CHECK(vkDeviceWaitIdle(logicalDevice)); - VK_CHECK(vkResetCommandPool(logicalDevice, commandPool, 0)); +void OnlineSDFRenderer::destroyRenderContextObjects() noexcept { vkutils::destroyFrameBuffers(logicalDevice, frameBuffers); vkutils::destroySwapchainImageViews(logicalDevice, swapchainImageViews); +} + +void OnlineSDFRenderer::destroyDeviceObjects() noexcept { + const VkResult waitResult = vkDeviceWaitIdle(logicalDevice); + if (waitResult != VK_SUCCESS) { + spdlog::warn("vkDeviceWaitIdle failed during OnlineSDFRenderer teardown: {}", + static_cast(waitResult)); + } + + vkutils::destroySemaphores(logicalDevice, imageAvailableSemaphores); + vkutils::destroySemaphores(logicalDevice, renderFinishedSemaphores); + vkutils::destroyFences(logicalDevice, fences); + destroyPipelineObjects(); + vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr); + destroyRenderContextObjects(); + vkDestroyRenderPass(logicalDevice, renderPass, nullptr); + vkDestroySwapchainKHR(logicalDevice, swapchain, nullptr); + vkDestroyQueryPool(logicalDevice, queryPool, nullptr); + vkDestroyCommandPool(logicalDevice, commandPool, nullptr); +} + +void OnlineSDFRenderer::destroyRenderContextOrThrow() { + VK_CHECK(vkDeviceWaitIdle(logicalDevice)); + VK_CHECK(vkResetCommandPool(logicalDevice, commandPool, 0)); + destroyRenderContextObjects(); // Swapchain gets destroyed after passing oldSwapchain to createSwapchain } @@ -199,7 +222,7 @@ void OnlineSDFRenderer::gameLoop() { pipelineUpdated.store(true, std::memory_order_relaxed); }); auto recreateSwapchain = [&]() { - destroyRenderContext(); + destroyRenderContextOrThrow(); setupRenderContext(); app.framebufferResized = false; frameIndex = 0; @@ -299,24 +322,21 @@ void OnlineSDFRenderer::gameLoop() { spdlog::info("Done!"); } -void OnlineSDFRenderer::destroy() { - VK_CHECK(vkDeviceWaitIdle(logicalDevice)); - vkutils::destroySemaphores(logicalDevice, imageAvailableSemaphores); - vkutils::destroySemaphores(logicalDevice, renderFinishedSemaphores); - vkutils::destroyFences(logicalDevice, fences); - vkDestroyPipeline(logicalDevice, pipeline, nullptr); - vkDestroyPipelineLayout(logicalDevice, pipelineLayout, nullptr); - vkDestroyShaderModule(logicalDevice, vertShaderModule, nullptr); - vkDestroyShaderModule(logicalDevice, fragShaderModule, nullptr); - vkutils::destroyFrameBuffers(logicalDevice, frameBuffers); - vkDestroyRenderPass(logicalDevice, renderPass, nullptr); - vkutils::destroySwapchainImageViews(logicalDevice, swapchainImageViews); - vkDestroySwapchainKHR(logicalDevice, swapchain, nullptr); - vkDestroyQueryPool(logicalDevice, queryPool, nullptr); - vkDestroyCommandPool(logicalDevice, commandPool, nullptr); - vkDestroyDevice(logicalDevice, nullptr); - vkDestroySurfaceKHR(instance, surface, nullptr); - vkDestroyInstance(instance, nullptr); - glfwDestroyWindow(window); - glfwTerminate(); +void OnlineSDFRenderer::destroy() noexcept { + if (logicalDevice != VK_NULL_HANDLE) { + destroyDeviceObjects(); + vkDestroyDevice(logicalDevice, nullptr); + } + + if (instance != VK_NULL_HANDLE) { + // The surface belongs to the instance, so destroy it first. + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); + } + + if (window != nullptr) { + // GLFW window teardown comes last after Vulkan is gone. + glfwDestroyWindow(window); + glfwTerminate(); + } } From b678cf6792ace320e0e16dce57b9282aa01822e1 Mon Sep 17 00:00:00 2001 From: jamylak Date: Thu, 26 Mar 2026 19:02:06 +1100 Subject: [PATCH 16/18] flatten destroy --- src/offline_sdf_renderer.cpp | 8 +++----- src/online_sdf_renderer.cpp | 10 ++++------ 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/offline_sdf_renderer.cpp b/src/offline_sdf_renderer.cpp index b0efef2..cc3b511 100644 --- a/src/offline_sdf_renderer.cpp +++ b/src/offline_sdf_renderer.cpp @@ -559,11 +559,9 @@ void OfflineSDFRenderer::destroyDeviceObjects() noexcept { void OfflineSDFRenderer::destroy() noexcept { if (logicalDevice != VK_NULL_HANDLE) { destroyDeviceObjects(); - vkDestroyDevice(logicalDevice, nullptr); } + vkDestroyDevice(logicalDevice, nullptr); - if (instance != VK_NULL_HANDLE) { - // Instance owns no other offline objects at this point. - vkDestroyInstance(instance, nullptr); - } + // Instance owns no other offline objects at this point. + vkDestroyInstance(instance, nullptr); } diff --git a/src/online_sdf_renderer.cpp b/src/online_sdf_renderer.cpp index bc11b59..9b9f48e 100644 --- a/src/online_sdf_renderer.cpp +++ b/src/online_sdf_renderer.cpp @@ -325,14 +325,12 @@ void OnlineSDFRenderer::gameLoop() { void OnlineSDFRenderer::destroy() noexcept { if (logicalDevice != VK_NULL_HANDLE) { destroyDeviceObjects(); - vkDestroyDevice(logicalDevice, nullptr); } + vkDestroyDevice(logicalDevice, nullptr); - if (instance != VK_NULL_HANDLE) { - // The surface belongs to the instance, so destroy it first. - vkDestroySurfaceKHR(instance, surface, nullptr); - vkDestroyInstance(instance, nullptr); - } + // The surface belongs to the instance, so destroy it first. + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); if (window != nullptr) { // GLFW window teardown comes last after Vulkan is gone. From 9ab6eeea2d7c0021c82e74e623e45c4ab93258bb Mon Sep 17 00:00:00 2001 From: jamylak Date: Thu, 26 Mar 2026 20:12:11 +1100 Subject: [PATCH 17/18] flatten destroys --- src/offline_sdf_renderer.cpp | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/src/offline_sdf_renderer.cpp b/src/offline_sdf_renderer.cpp index cc3b511..513a156 100644 --- a/src/offline_sdf_renderer.cpp +++ b/src/offline_sdf_renderer.cpp @@ -511,30 +511,20 @@ void OfflineSDFRenderer::destroyPipelineObjects() noexcept { void OfflineSDFRenderer::destroyRenderContextObjects() noexcept { for (size_t i = 0; i < ringSize; ++i) { RingSlot &slot = ringSlots[i]; - if (slot.framebuffer != VK_NULL_HANDLE) { - vkDestroyFramebuffer(logicalDevice, slot.framebuffer, nullptr); - slot.framebuffer = VK_NULL_HANDLE; - } - if (slot.imageView != VK_NULL_HANDLE) { - vkDestroyImageView(logicalDevice, slot.imageView, nullptr); - slot.imageView = VK_NULL_HANDLE; - } - if (slot.image != VK_NULL_HANDLE) { - vkDestroyImage(logicalDevice, slot.image, nullptr); - slot.image = VK_NULL_HANDLE; - } - if (slot.imageMemory != VK_NULL_HANDLE) { - vkFreeMemory(logicalDevice, slot.imageMemory, nullptr); - slot.imageMemory = VK_NULL_HANDLE; - } - if (slot.stagingBuffer.buffer != VK_NULL_HANDLE || - slot.stagingBuffer.memory != VK_NULL_HANDLE) { - if (slot.mappedData) { - vkUnmapMemory(logicalDevice, slot.stagingBuffer.memory); - slot.mappedData = nullptr; - } - vkutils::destroyReadbackBuffer(logicalDevice, slot.stagingBuffer); + vkDestroyFramebuffer(logicalDevice, slot.framebuffer, nullptr); + slot.framebuffer = VK_NULL_HANDLE; + vkDestroyImageView(logicalDevice, slot.imageView, nullptr); + slot.imageView = VK_NULL_HANDLE; + vkDestroyImage(logicalDevice, slot.image, nullptr); + slot.image = VK_NULL_HANDLE; + vkFreeMemory(logicalDevice, slot.imageMemory, nullptr); + slot.imageMemory = VK_NULL_HANDLE; + + if (slot.mappedData) { + vkUnmapMemory(logicalDevice, slot.stagingBuffer.memory); + slot.mappedData = nullptr; } + vkutils::destroyReadbackBuffer(logicalDevice, slot.stagingBuffer); slot.pendingReadback = false; slot.pendingEncode = false; } From 1ed072d0727442f58d3ecb50228ebff167e183ae Mon Sep 17 00:00:00 2001 From: jamylak Date: Thu, 26 Mar 2026 20:13:38 +1100 Subject: [PATCH 18/18] simplify --- src/offline_sdf_renderer.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/offline_sdf_renderer.cpp b/src/offline_sdf_renderer.cpp index 513a156..265bce2 100644 --- a/src/offline_sdf_renderer.cpp +++ b/src/offline_sdf_renderer.cpp @@ -512,21 +512,14 @@ void OfflineSDFRenderer::destroyRenderContextObjects() noexcept { for (size_t i = 0; i < ringSize; ++i) { RingSlot &slot = ringSlots[i]; vkDestroyFramebuffer(logicalDevice, slot.framebuffer, nullptr); - slot.framebuffer = VK_NULL_HANDLE; vkDestroyImageView(logicalDevice, slot.imageView, nullptr); - slot.imageView = VK_NULL_HANDLE; vkDestroyImage(logicalDevice, slot.image, nullptr); - slot.image = VK_NULL_HANDLE; vkFreeMemory(logicalDevice, slot.imageMemory, nullptr); - slot.imageMemory = VK_NULL_HANDLE; if (slot.mappedData) { vkUnmapMemory(logicalDevice, slot.stagingBuffer.memory); - slot.mappedData = nullptr; } vkutils::destroyReadbackBuffer(logicalDevice, slot.stagingBuffer); - slot.pendingReadback = false; - slot.pendingEncode = false; } }