From e34cb5883a8fe83ebad18c6217e9e1224dbc74cb Mon Sep 17 00:00:00 2001 From: ubuntu1804 Date: Tue, 11 Nov 2025 10:39:40 +0800 Subject: [PATCH] Prevent rendering during close and validate surface Add m_isClosing flag and closeEvent to stop rendering, stop the render timer and wait for device idle. Guard swapchain creation and recreation to abort when closing. Check Vulkan return values and detect invalid surface capabilities to avoid crashes during window teardown. --- src/vulkanwidget.cpp | 69 +++++++++++++++++++++++++++++++++++++++----- src/vulkanwidget.h | 6 ++++ 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/src/vulkanwidget.cpp b/src/vulkanwidget.cpp index 85383f6..cab5a73 100644 --- a/src/vulkanwidget.cpp +++ b/src/vulkanwidget.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,7 @@ VulkanWidget::VulkanWidget(QWidget *parent) , m_lastLockFrameCount(0) , m_lockPaintFrameCount(0) , m_lockCount(0) + , m_isClosing(false) { // Set widget attributes for native window setAttribute(Qt::WA_NativeWindow); @@ -483,9 +485,20 @@ bool VulkanWidget::createSwapchain() qDebug() << "Widget size:" << width() << "x" << height(); qDebug() << "Device pixel ratio:" << devicePixelRatioF(); + // Don't create swapchain if closing + if (m_isClosing) { + qDebug() << "Aborting swapchain creation - widget is closing"; + return false; + } + // Query surface capabilities VkSurfaceCapabilitiesKHR capabilities; - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_physicalDevice, m_surface, &capabilities); + VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_physicalDevice, m_surface, &capabilities); + + if (result != VK_SUCCESS) { + qDebug() << "Failed to get surface capabilities, error code:" << result; + return false; + } qDebug() << "Surface capabilities - current extent:" << capabilities.currentExtent.width << "x" << capabilities.currentExtent.height; @@ -493,6 +506,16 @@ bool VulkanWidget::createSwapchain() << capabilities.minImageExtent.width << "x" << capabilities.minImageExtent.height; qDebug() << "Surface capabilities - max extent:" << capabilities.maxImageExtent.width << "x" << capabilities.maxImageExtent.height; + + // Validate surface capabilities - detect invalid/corrupted values + // This can happen when the window is being destroyed + if (capabilities.currentExtent.width > 16384 || capabilities.currentExtent.height > 16384 || + capabilities.minImageExtent.width > 16384 || capabilities.minImageExtent.height > 16384 || + capabilities.maxImageExtent.width > 1000000 || capabilities.maxImageExtent.height > 1000000) { + qDebug() << "ERROR: Invalid surface capabilities detected - surface may be destroyed"; + qDebug() << "This usually happens when the window is being closed"; + return false; + } // Query surface formats uint32_t formatCount; @@ -576,9 +599,9 @@ bool VulkanWidget::createSwapchain() createInfo.clipped = VK_TRUE; createInfo.oldSwapchain = VK_NULL_HANDLE; - VkResult result = vkCreateSwapchainKHR(m_device, &createInfo, nullptr, &m_swapchain); - if (result != VK_SUCCESS) { - qDebug() << "Failed to create swapchain, error code:" << result; + VkResult swapchainResult = vkCreateSwapchainKHR(m_device, &createInfo, nullptr, &m_swapchain); + if (swapchainResult != VK_SUCCESS) { + qDebug() << "Failed to create swapchain, error code:" << swapchainResult; return false; } @@ -607,9 +630,9 @@ bool VulkanWidget::createSwapchain() viewInfo.subresourceRange.layerCount = 1; VkImageView imageView; - result = vkCreateImageView(m_device, &viewInfo, nullptr, &imageView); - if (result != VK_SUCCESS) { - qDebug() << "Failed to create image view" << i << ", error code:" << result; + VkResult viewResult = vkCreateImageView(m_device, &viewInfo, nullptr, &imageView); + if (viewResult != VK_SUCCESS) { + qDebug() << "Failed to create image view" << i << ", error code:" << viewResult; return false; } m_swapchainImageViews[i] = reinterpret_cast(imageView); @@ -685,6 +708,12 @@ bool VulkanWidget::createSyncObjects() bool VulkanWidget::recreateSwapchain() { qDebug() << "Recreating swapchain..."; + + // Don't recreate if closing + if (m_isClosing) { + qDebug() << "Aborting swapchain recreation - widget is closing"; + return false; + } // Wait for device to be idle vkDeviceWaitIdle(m_device); @@ -710,7 +739,8 @@ bool VulkanWidget::recreateSwapchain() void VulkanWidget::renderFrame() { - if (!m_initialized) { + // Don't render if closing or not initialized + if (!m_initialized || m_isClosing) { return; } @@ -993,6 +1023,29 @@ void VulkanWidget::hideEvent(QHideEvent *event) setRenderingEnabled(false); } +void VulkanWidget::closeEvent(QCloseEvent *event) +{ + qDebug() << "VulkanWidget closeEvent - stopping rendering and cleaning up"; + + // Set closing flag to prevent any further rendering attempts + m_isClosing = true; + + // Stop the render timer immediately + if (m_renderTimer) { + m_renderTimer->stop(); + qDebug() << "Render timer stopped on close"; + } + + // Wait for device to be idle before accepting close event + if (m_device != VK_NULL_HANDLE) { + vkDeviceWaitIdle(m_device); + qDebug() << "Vulkan device idle on close"; + } + + // Accept the close event + QWidget::closeEvent(event); +} + void VulkanWidget::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); diff --git a/src/vulkanwidget.h b/src/vulkanwidget.h index 16ee5d5..540a4a2 100644 --- a/src/vulkanwidget.h +++ b/src/vulkanwidget.h @@ -62,6 +62,11 @@ protected: */ void hideEvent(QHideEvent *event) override; + /** + * @brief Qt事件:窗口关闭时 + */ + void closeEvent(QCloseEvent *event) override; + /** * @brief Qt事件:组件大小改变时 */ @@ -208,6 +213,7 @@ private: bool m_renderingEnabled; bool m_needsResize; bool m_needsLockedFrameUpdate; // 是否需要渲染锁屏帧(锁屏时只渲染一帧) + bool m_isClosing; // 标记窗口正在关闭,防止在销毁过程中继续渲染 int m_frameCount; uint32_t m_queueFamilyIndex; uint32_t m_currentFrame;