From ae2e68f4141a867448ecf0333f5797896442ed14 Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Mon, 7 Apr 2025 20:56:22 -0300 Subject: [PATCH] vulkan: populate oldSwapchain field during swap chain creation. Also fix crash if window is already closed, when quitting. Fixes #2174 --- src/modules/graphics/vulkan/Graphics.cpp | 48 ++++++++++++++++++++---- src/modules/graphics/vulkan/Graphics.h | 2 +- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index d7cf49bb9..c249877bd 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -815,8 +815,13 @@ void Graphics::unSetMode() created = false; - cleanupSwapChain(); - vkDestroySurfaceKHR(instance, surface, nullptr); + cleanupSwapChain(true); + + if (surface != VK_NULL_HANDLE) + { + vkDestroySurfaceKHR(instance, surface, nullptr); + surface = VK_NULL_HANDLE; + } } void Graphics::setActive(bool enable) @@ -2021,18 +2026,34 @@ void Graphics::createSwapChain() createInfo.compositeAlpha = chooseCompositeAlpha(swapChainSupport.capabilities); createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; - createInfo.oldSwapchain = VK_NULL_HANDLE; + createInfo.oldSwapchain = swapChain; + + VkSwapchainKHR newSwapChain = VK_NULL_HANDLE; + VkResult result = vkCreateSwapchainKHR(device, &createInfo, nullptr, &newSwapChain); - VkResult result = vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain); if (result != VK_SUCCESS) throw love::Exception("Failed to create Vulkan swap chain: %s", Vulkan::getErrorString(result)); + if (swapChain != VK_NULL_HANDLE) + { + vkDestroySwapchainKHR(device, swapChain, nullptr); + swapChain = VK_NULL_HANDLE; + } + + swapChain = newSwapChain; + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); } else { + if (swapChain != VK_NULL_HANDLE) + { + vkDestroySwapchainKHR(device, swapChain, nullptr); + swapChain = VK_NULL_HANDLE; + } + // Use a fake backbuffer. Creation is deferred until startRecordingGraphicsCommands // because newTexture needs an active command buffer to do its initial // layout transitions. @@ -3424,19 +3445,27 @@ void Graphics::cleanup() } } -void Graphics::cleanupSwapChain() +void Graphics::cleanupSwapChain(bool destroySwapChainObject) { if (colorImage) { cleanupFramebuffers(colorImageView, swapChainPixelFormat); + vkDestroyImageView(device, colorImageView, nullptr); + colorImageView = VK_NULL_HANDLE; + vmaDestroyImage(vmaAllocator, colorImage, colorImageAllocation); + colorImage = VK_NULL_HANDLE; } if (depthImage) { cleanupFramebuffers(depthImageView, depthStencilPixelFormat); + vkDestroyImageView(device, depthImageView, nullptr); + depthImageView = VK_NULL_HANDLE; + vmaDestroyImage(vmaAllocator, depthImage, depthImageAllocation); + depthImage = VK_NULL_HANDLE; } for (const auto &swapChainImageView : swapChainImageViews) { @@ -3444,18 +3473,21 @@ void Graphics::cleanupSwapChain() vkDestroyImageView(device, swapChainImageView, nullptr); } swapChainImageViews.clear(); - vkDestroySwapchainKHR(device, swapChain, nullptr); swapChainImages.clear(); fakeBackbuffer.set(nullptr); - swapChain = VK_NULL_HANDLE; + if (destroySwapChainObject && swapChain != VK_NULL_HANDLE) + { + vkDestroySwapchainKHR(device, swapChain, nullptr); + swapChain = VK_NULL_HANDLE; + } } void Graphics::recreateSwapChain() { vkDeviceWaitIdle(device); - cleanupSwapChain(); + cleanupSwapChain(false); createSwapChain(); createImageViews(); diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index ec5d95a40..02311a469 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -337,7 +337,7 @@ private: void createCommandBuffers(); void createSyncObjects(); void cleanup(); - void cleanupSwapChain(); + void cleanupSwapChain(bool destroySwapChainObject); void recreateSwapChain(); void initDynamicState(); void beginSwapChainFrame();