From 3c52aa1ca33059c348c9fad9a1685c668f08d14d Mon Sep 17 00:00:00 2001 From: ubuntu1804 Date: Tue, 11 Nov 2025 17:10:32 +0800 Subject: [PATCH] Manage X11 display and harden surface recovery Reuse and store the X11 Display pointer (as void*) so createSurface doesn't repeatedly open displays. Close the display during cleanup and before device recreation. Add a short 200ms delay in recreateDevice to let the GPU driver stabilize, and explicitly recheck surface support and query surface capabilities before recreating the swapchain. --- src/vulkanwidget.cpp | 66 ++++++++++++++++++++++++++++++++++++++++---- src/vulkanwidget.h | 1 + 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/src/vulkanwidget.cpp b/src/vulkanwidget.cpp index 6ca02c1..aff8e66 100644 --- a/src/vulkanwidget.cpp +++ b/src/vulkanwidget.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,7 @@ VulkanWidget::VulkanWidget(QWidget *parent) , m_surface(VK_NULL_HANDLE) , m_swapchain(VK_NULL_HANDLE) , m_commandPool(VK_NULL_HANDLE) + , m_x11Display(nullptr) , m_initialized(false) , m_renderingEnabled(false) , m_needsResize(false) @@ -342,12 +344,22 @@ bool VulkanWidget::createSurface() #elif defined(__linux__) VkXlibSurfaceCreateInfoKHR createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - // Get X11 display - use default display - createInfo.dpy = XOpenDisplay(nullptr); - if (!createInfo.dpy) { - qDebug() << "Failed to open X11 display"; - return false; + + // Reuse existing X11 display or open a new one + Display* x11Display = static_cast(m_x11Display); + if (!x11Display) { + x11Display = XOpenDisplay(nullptr); + if (!x11Display) { + qDebug() << "Failed to open X11 display"; + return false; + } + m_x11Display = x11Display; + qDebug() << "X11 display opened and stored"; + } else { + qDebug() << "Reusing existing X11 display"; } + + createInfo.dpy = x11Display; createInfo.window = static_cast(window->winId()); VkResult result = vkCreateXlibSurfaceKHR(m_instance, &createInfo, nullptr, &m_surface); @@ -1071,6 +1083,15 @@ void VulkanWidget::cleanupVulkan() if (m_surface != VK_NULL_HANDLE) { vkDestroySurfaceKHR(m_instance, m_surface, nullptr); } + + // Close X11 display if it was opened (Linux only) +#ifdef __linux__ + if (m_x11Display) { + XCloseDisplay(static_cast(m_x11Display)); + m_x11Display = nullptr; + qDebug() << "X11 display closed"; + } +#endif // Cleanup instance if (m_instance != VK_NULL_HANDLE) { @@ -1303,7 +1324,22 @@ bool VulkanWidget::recreateDevice() qDebug() << "Surface destroyed"; } + // Close and reset X11 display (Linux only) - will be reopened when creating new surface +#ifdef __linux__ + if (m_x11Display) { + XCloseDisplay(static_cast(m_x11Display)); + m_x11Display = nullptr; + qDebug() << "X11 display closed for recreation"; + } +#endif + // Now recreate everything from surface onwards + + // Important: Add a small delay to let the GPU driver fully reset + // This prevents VK_ERROR_SURFACE_LOST_KHR on some systems + qDebug() << "Waiting for GPU driver to stabilize..."; + QThread::msleep(200); // 200ms delay + // Step 1: Recreate surface if (!createSurface()) { setError("Failed to recreate surface after device lost"); @@ -1311,6 +1347,16 @@ bool VulkanWidget::recreateDevice() } qDebug() << "Surface recreated"; + // Verify the physical device still supports this surface + VkBool32 surfaceSupported = VK_FALSE; + vkGetPhysicalDeviceSurfaceSupportKHR(m_physicalDevice, m_queueFamilyIndex, m_surface, &surfaceSupported); + if (!surfaceSupported) { + qDebug() << "Physical device no longer supports the surface after recovery"; + setError("Surface not supported by physical device after recovery"); + return false; + } + qDebug() << "Surface support verified"; + // Step 2: Recreate logical device if (!createDevice()) { setError("Failed to recreate device after device lost"); @@ -1319,6 +1365,16 @@ bool VulkanWidget::recreateDevice() qDebug() << "Logical device recreated"; // Step 3: Recreate swapchain + // Query surface capabilities again to ensure they're current + VkSurfaceCapabilitiesKHR surfaceCaps; + VkResult capsResult = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_physicalDevice, m_surface, &surfaceCaps); + if (capsResult != VK_SUCCESS) { + qDebug() << "Failed to query surface capabilities, error:" << capsResult; + setError("Failed to query surface capabilities after device lost"); + return false; + } + qDebug() << "Surface capabilities validated"; + if (!createSwapchain()) { setError("Failed to recreate swapchain after device lost"); return false; diff --git a/src/vulkanwidget.h b/src/vulkanwidget.h index 8e925b6..8e46475 100644 --- a/src/vulkanwidget.h +++ b/src/vulkanwidget.h @@ -214,6 +214,7 @@ private: VkSurfaceKHR m_surface; VkSwapchainKHR m_swapchain; VkCommandPool m_commandPool; + void* m_x11Display; // X11 Display pointer (Linux only), stored as void* to avoid X11 header dependency std::vector m_commandBuffers; std::vector m_imageAvailableSemaphores; std::vector m_renderFinishedSemaphores;