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.
This commit is contained in:
ubuntu1804 2025-11-11 17:10:32 +08:00
parent 1845e6113e
commit 3c52aa1ca3
2 changed files with 62 additions and 5 deletions

View File

@ -8,6 +8,7 @@
#include <QCloseEvent> #include <QCloseEvent>
#include <QWindow> #include <QWindow>
#include <QApplication> #include <QApplication>
#include <QThread>
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
@ -35,6 +36,7 @@ VulkanWidget::VulkanWidget(QWidget *parent)
, m_surface(VK_NULL_HANDLE) , m_surface(VK_NULL_HANDLE)
, m_swapchain(VK_NULL_HANDLE) , m_swapchain(VK_NULL_HANDLE)
, m_commandPool(VK_NULL_HANDLE) , m_commandPool(VK_NULL_HANDLE)
, m_x11Display(nullptr)
, m_initialized(false) , m_initialized(false)
, m_renderingEnabled(false) , m_renderingEnabled(false)
, m_needsResize(false) , m_needsResize(false)
@ -342,12 +344,22 @@ bool VulkanWidget::createSurface()
#elif defined(__linux__) #elif defined(__linux__)
VkXlibSurfaceCreateInfoKHR createInfo = {}; VkXlibSurfaceCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
// Get X11 display - use default display
createInfo.dpy = XOpenDisplay(nullptr); // Reuse existing X11 display or open a new one
if (!createInfo.dpy) { Display* x11Display = static_cast<Display*>(m_x11Display);
qDebug() << "Failed to open X11 display"; if (!x11Display) {
return false; 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>(window->winId()); createInfo.window = static_cast<Window>(window->winId());
VkResult result = vkCreateXlibSurfaceKHR(m_instance, &createInfo, nullptr, &m_surface); VkResult result = vkCreateXlibSurfaceKHR(m_instance, &createInfo, nullptr, &m_surface);
@ -1072,6 +1084,15 @@ void VulkanWidget::cleanupVulkan()
vkDestroySurfaceKHR(m_instance, m_surface, nullptr); vkDestroySurfaceKHR(m_instance, m_surface, nullptr);
} }
// Close X11 display if it was opened (Linux only)
#ifdef __linux__
if (m_x11Display) {
XCloseDisplay(static_cast<Display*>(m_x11Display));
m_x11Display = nullptr;
qDebug() << "X11 display closed";
}
#endif
// Cleanup instance // Cleanup instance
if (m_instance != VK_NULL_HANDLE) { if (m_instance != VK_NULL_HANDLE) {
vkDestroyInstance(m_instance, nullptr); vkDestroyInstance(m_instance, nullptr);
@ -1303,7 +1324,22 @@ bool VulkanWidget::recreateDevice()
qDebug() << "Surface destroyed"; 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<Display*>(m_x11Display));
m_x11Display = nullptr;
qDebug() << "X11 display closed for recreation";
}
#endif
// Now recreate everything from surface onwards // 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 // Step 1: Recreate surface
if (!createSurface()) { if (!createSurface()) {
setError("Failed to recreate surface after device lost"); setError("Failed to recreate surface after device lost");
@ -1311,6 +1347,16 @@ bool VulkanWidget::recreateDevice()
} }
qDebug() << "Surface recreated"; 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 // Step 2: Recreate logical device
if (!createDevice()) { if (!createDevice()) {
setError("Failed to recreate device after device lost"); setError("Failed to recreate device after device lost");
@ -1319,6 +1365,16 @@ bool VulkanWidget::recreateDevice()
qDebug() << "Logical device recreated"; qDebug() << "Logical device recreated";
// Step 3: Recreate swapchain // 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()) { if (!createSwapchain()) {
setError("Failed to recreate swapchain after device lost"); setError("Failed to recreate swapchain after device lost");
return false; return false;

View File

@ -214,6 +214,7 @@ private:
VkSurfaceKHR m_surface; VkSurfaceKHR m_surface;
VkSwapchainKHR m_swapchain; VkSwapchainKHR m_swapchain;
VkCommandPool m_commandPool; VkCommandPool m_commandPool;
void* m_x11Display; // X11 Display pointer (Linux only), stored as void* to avoid X11 header dependency
std::vector<VkCommandBuffer> m_commandBuffers; std::vector<VkCommandBuffer> m_commandBuffers;
std::vector<VkSemaphore> m_imageAvailableSemaphores; std::vector<VkSemaphore> m_imageAvailableSemaphores;
std::vector<VkSemaphore> m_renderFinishedSemaphores; std::vector<VkSemaphore> m_renderFinishedSemaphores;