diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index ddd05ab06..9aacff4a7 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -606,7 +606,7 @@ void Graphics::present(void *screenshotCallbackdata) { VkExtent2D extent = chooseSwapExtent(capabilities); if (extent.width > 0 && extent.height > 0) - swapChainRecreationRequested = true; + swapChainRequestFlags |= SWAP_CHAIN_REQUEST_RECREATE; } } @@ -625,9 +625,12 @@ void Graphics::present(void *screenshotCallbackdata) } #endif - if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || swapChainRecreationRequested) + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_SURFACE_LOST_KHR + || swapChainRequestFlags != SWAP_CHAIN_REQUEST_KEEP) { - swapChainRecreationRequested = false; + swapChainRequestFlags |= SWAP_CHAIN_REQUEST_RECREATE; + if (result == VK_ERROR_SURFACE_LOST_KHR) + swapChainRequestFlags |= SWAP_CHAIN_REQUEST_RECREATE_SURFACE; recreateSwapChain(); } else if (result != VK_SUCCESS) @@ -666,9 +669,8 @@ void Graphics::backbufferChanged(const BackbufferSettings &settings) // Don't wait until the next frame starts to recreate the swapchain - doing so // will cause a 1 frame delay in the backbuffer size on resize, and it can cause // MSAA state to get out of sync for a frame. - if (swapChainRecreationRequested) + if (swapChainRequestFlags != SWAP_CHAIN_REQUEST_KEEP) { - swapChainRecreationRequested = false; submitGpuCommands(SUBMIT_NOPRESENT); recreateSwapChain(); beginSwapChainFrame(); @@ -841,12 +843,7 @@ void Graphics::unSetMode() created = false; cleanupSwapChain(true); - - if (surface != VK_NULL_HANDLE) - { - vkDestroySurfaceKHR(instance, surface, nullptr); - surface = VK_NULL_HANDLE; - } + cleanupSurface(); } void Graphics::setActive(bool enable) @@ -1386,8 +1383,15 @@ void Graphics::beginSwapChainFrame() while (true) { VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); - if (result == VK_ERROR_OUT_OF_DATE_KHR) + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_ERROR_SURFACE_LOST_KHR) { + swapChainRequestFlags |= SWAP_CHAIN_REQUEST_RECREATE; + if (result == VK_ERROR_SURFACE_LOST_KHR) + { + // TODO: do we need to worry about an infinite loop here if the surface keeps getting lost? + // What should happen if creation fails? + swapChainRequestFlags |= SWAP_CHAIN_REQUEST_RECREATE_SURFACE; + } recreateSwapChain(); continue; } @@ -1996,6 +2000,15 @@ void Graphics::createSurface() throw love::Exception("Failed to create Vulkan window surface: %s", SDL_GetError()); } +void Graphics::cleanupSurface() +{ + if (surface != VK_NULL_HANDLE) + { + vkDestroySurfaceKHR(instance, surface, nullptr); + surface = VK_NULL_HANDLE; + } +} + SwapChainSupportDetails Graphics::querySwapChainSupport(VkPhysicalDevice device) { SwapChainSupportDetails details; @@ -2984,7 +2997,7 @@ void Graphics::requestSwapchainRecreation() { if (swapChain != VK_NULL_HANDLE) { - swapChainRecreationRequested = true; + swapChainRequestFlags |= SWAP_CHAIN_REQUEST_RECREATE; } } @@ -3566,13 +3579,21 @@ void Graphics::recreateSwapChain() { vkDeviceWaitIdle(device); - cleanupSwapChain(false); + bool destroySwapChainObject = (swapChainRequestFlags & SWAP_CHAIN_REQUEST_RECREATE_SURFACE) != 0; + cleanupSwapChain(destroySwapChainObject); + + if ((swapChainRequestFlags & SWAP_CHAIN_REQUEST_RECREATE_SURFACE) != 0) + { + cleanupSurface(); + createSurface(); + } createSwapChain(); createImageViews(); createColorResources(); createDepthResources(); + swapChainRequestFlags = SWAP_CHAIN_REQUEST_KEEP; transitionColorDepthLayouts = true; } diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index e8a79d045..395cbc259 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -305,6 +305,13 @@ protected: private: + enum SwapChainRequestFlags + { + SWAP_CHAIN_REQUEST_KEEP = 0, + SWAP_CHAIN_REQUEST_RECREATE = (1 << 0), + SWAP_CHAIN_REQUEST_RECREATE_SURFACE = (1 << 1), + }; + struct SharedDescriptorPoolsRef { SharedDescriptorPools *pools = nullptr; @@ -319,6 +326,7 @@ private: void createPipelineCache(); void initVMA(); void createSurface(); + void cleanupSurface(); SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device); VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector &availableFormats); VkPresentModeKHR chooseSwapPresentMode(const std::vector &availablePresentModes); @@ -405,7 +413,7 @@ private: size_t currentFrame = 0; uint32_t imageIndex = 0; uint64 realFrameIndex = 0; - bool swapChainRecreationRequested = false; + uint32 swapChainRequestFlags = 0; bool windowIsFullscreenExclusive = false; bool transitionColorDepthLayouts = false; VmaAllocator vmaAllocator = VK_NULL_HANDLE;