vulkan: handle swap chain surface loss errors.

This commit is contained in:
Sasha Szpakowski
2026-07-05 10:50:22 -03:00
parent 51114ee01c
commit 4758d54af6
2 changed files with 44 additions and 15 deletions
+35 -14
View File
@@ -606,7 +606,7 @@ void Graphics::present(void *screenshotCallbackdata)
{ {
VkExtent2D extent = chooseSwapExtent(capabilities); VkExtent2D extent = chooseSwapExtent(capabilities);
if (extent.width > 0 && extent.height > 0) if (extent.width > 0 && extent.height > 0)
swapChainRecreationRequested = true; swapChainRequestFlags |= SWAP_CHAIN_REQUEST_RECREATE;
} }
} }
@@ -625,9 +625,12 @@ void Graphics::present(void *screenshotCallbackdata)
} }
#endif #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(); recreateSwapChain();
} }
else if (result != VK_SUCCESS) 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 // 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 // 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. // MSAA state to get out of sync for a frame.
if (swapChainRecreationRequested) if (swapChainRequestFlags != SWAP_CHAIN_REQUEST_KEEP)
{ {
swapChainRecreationRequested = false;
submitGpuCommands(SUBMIT_NOPRESENT); submitGpuCommands(SUBMIT_NOPRESENT);
recreateSwapChain(); recreateSwapChain();
beginSwapChainFrame(); beginSwapChainFrame();
@@ -841,12 +843,7 @@ void Graphics::unSetMode()
created = false; created = false;
cleanupSwapChain(true); cleanupSwapChain(true);
cleanupSurface();
if (surface != VK_NULL_HANDLE)
{
vkDestroySurfaceKHR(instance, surface, nullptr);
surface = VK_NULL_HANDLE;
}
} }
void Graphics::setActive(bool enable) void Graphics::setActive(bool enable)
@@ -1386,8 +1383,15 @@ void Graphics::beginSwapChainFrame()
while (true) while (true)
{ {
VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); 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(); recreateSwapChain();
continue; continue;
} }
@@ -1996,6 +2000,15 @@ void Graphics::createSurface()
throw love::Exception("Failed to create Vulkan window surface: %s", SDL_GetError()); 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 Graphics::querySwapChainSupport(VkPhysicalDevice device)
{ {
SwapChainSupportDetails details; SwapChainSupportDetails details;
@@ -2984,7 +2997,7 @@ void Graphics::requestSwapchainRecreation()
{ {
if (swapChain != VK_NULL_HANDLE) if (swapChain != VK_NULL_HANDLE)
{ {
swapChainRecreationRequested = true; swapChainRequestFlags |= SWAP_CHAIN_REQUEST_RECREATE;
} }
} }
@@ -3566,13 +3579,21 @@ void Graphics::recreateSwapChain()
{ {
vkDeviceWaitIdle(device); 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(); createSwapChain();
createImageViews(); createImageViews();
createColorResources(); createColorResources();
createDepthResources(); createDepthResources();
swapChainRequestFlags = SWAP_CHAIN_REQUEST_KEEP;
transitionColorDepthLayouts = true; transitionColorDepthLayouts = true;
} }
+9 -1
View File
@@ -305,6 +305,13 @@ protected:
private: private:
enum SwapChainRequestFlags
{
SWAP_CHAIN_REQUEST_KEEP = 0,
SWAP_CHAIN_REQUEST_RECREATE = (1 << 0),
SWAP_CHAIN_REQUEST_RECREATE_SURFACE = (1 << 1),
};
struct SharedDescriptorPoolsRef struct SharedDescriptorPoolsRef
{ {
SharedDescriptorPools *pools = nullptr; SharedDescriptorPools *pools = nullptr;
@@ -319,6 +326,7 @@ private:
void createPipelineCache(); void createPipelineCache();
void initVMA(); void initVMA();
void createSurface(); void createSurface();
void cleanupSurface();
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device); SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device);
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR> &availableFormats); VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR> &availableFormats);
VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR> &availablePresentModes); VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR> &availablePresentModes);
@@ -405,7 +413,7 @@ private:
size_t currentFrame = 0; size_t currentFrame = 0;
uint32_t imageIndex = 0; uint32_t imageIndex = 0;
uint64 realFrameIndex = 0; uint64 realFrameIndex = 0;
bool swapChainRecreationRequested = false; uint32 swapChainRequestFlags = 0;
bool windowIsFullscreenExclusive = false; bool windowIsFullscreenExclusive = false;
bool transitionColorDepthLayouts = false; bool transitionColorDepthLayouts = false;
VmaAllocator vmaAllocator = VK_NULL_HANDLE; VmaAllocator vmaAllocator = VK_NULL_HANDLE;