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.
This commit is contained in:
ubuntu1804 2025-11-11 10:39:40 +08:00
parent 2c7119d190
commit e34cb5883a
2 changed files with 67 additions and 8 deletions

View File

@ -5,6 +5,7 @@
#include <QHideEvent> #include <QHideEvent>
#include <QResizeEvent> #include <QResizeEvent>
#include <QPaintEvent> #include <QPaintEvent>
#include <QCloseEvent>
#include <QWindow> #include <QWindow>
#include <QApplication> #include <QApplication>
#include <algorithm> #include <algorithm>
@ -52,6 +53,7 @@ VulkanWidget::VulkanWidget(QWidget *parent)
, m_lastLockFrameCount(0) , m_lastLockFrameCount(0)
, m_lockPaintFrameCount(0) , m_lockPaintFrameCount(0)
, m_lockCount(0) , m_lockCount(0)
, m_isClosing(false)
{ {
// Set widget attributes for native window // Set widget attributes for native window
setAttribute(Qt::WA_NativeWindow); setAttribute(Qt::WA_NativeWindow);
@ -483,9 +485,20 @@ bool VulkanWidget::createSwapchain()
qDebug() << "Widget size:" << width() << "x" << height(); qDebug() << "Widget size:" << width() << "x" << height();
qDebug() << "Device pixel ratio:" << devicePixelRatioF(); 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 // Query surface capabilities
VkSurfaceCapabilitiesKHR 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:" qDebug() << "Surface capabilities - current extent:"
<< capabilities.currentExtent.width << "x" << capabilities.currentExtent.height; << capabilities.currentExtent.width << "x" << capabilities.currentExtent.height;
@ -494,6 +507,16 @@ bool VulkanWidget::createSwapchain()
qDebug() << "Surface capabilities - max extent:" qDebug() << "Surface capabilities - max extent:"
<< capabilities.maxImageExtent.width << "x" << capabilities.maxImageExtent.height; << 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 // Query surface formats
uint32_t formatCount; uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, nullptr); vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, nullptr);
@ -576,9 +599,9 @@ bool VulkanWidget::createSwapchain()
createInfo.clipped = VK_TRUE; createInfo.clipped = VK_TRUE;
createInfo.oldSwapchain = VK_NULL_HANDLE; createInfo.oldSwapchain = VK_NULL_HANDLE;
VkResult result = vkCreateSwapchainKHR(m_device, &createInfo, nullptr, &m_swapchain); VkResult swapchainResult = vkCreateSwapchainKHR(m_device, &createInfo, nullptr, &m_swapchain);
if (result != VK_SUCCESS) { if (swapchainResult != VK_SUCCESS) {
qDebug() << "Failed to create swapchain, error code:" << result; qDebug() << "Failed to create swapchain, error code:" << swapchainResult;
return false; return false;
} }
@ -607,9 +630,9 @@ bool VulkanWidget::createSwapchain()
viewInfo.subresourceRange.layerCount = 1; viewInfo.subresourceRange.layerCount = 1;
VkImageView imageView; VkImageView imageView;
result = vkCreateImageView(m_device, &viewInfo, nullptr, &imageView); VkResult viewResult = vkCreateImageView(m_device, &viewInfo, nullptr, &imageView);
if (result != VK_SUCCESS) { if (viewResult != VK_SUCCESS) {
qDebug() << "Failed to create image view" << i << ", error code:" << result; qDebug() << "Failed to create image view" << i << ", error code:" << viewResult;
return false; return false;
} }
m_swapchainImageViews[i] = reinterpret_cast<void*>(imageView); m_swapchainImageViews[i] = reinterpret_cast<void*>(imageView);
@ -686,6 +709,12 @@ bool VulkanWidget::recreateSwapchain()
{ {
qDebug() << "Recreating swapchain..."; 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 // Wait for device to be idle
vkDeviceWaitIdle(m_device); vkDeviceWaitIdle(m_device);
@ -710,7 +739,8 @@ bool VulkanWidget::recreateSwapchain()
void VulkanWidget::renderFrame() void VulkanWidget::renderFrame()
{ {
if (!m_initialized) { // Don't render if closing or not initialized
if (!m_initialized || m_isClosing) {
return; return;
} }
@ -993,6 +1023,29 @@ void VulkanWidget::hideEvent(QHideEvent *event)
setRenderingEnabled(false); 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) void VulkanWidget::resizeEvent(QResizeEvent *event)
{ {
QWidget::resizeEvent(event); QWidget::resizeEvent(event);

View File

@ -62,6 +62,11 @@ protected:
*/ */
void hideEvent(QHideEvent *event) override; void hideEvent(QHideEvent *event) override;
/**
* @brief Qt事件
*/
void closeEvent(QCloseEvent *event) override;
/** /**
* @brief Qt事件 * @brief Qt事件
*/ */
@ -208,6 +213,7 @@ private:
bool m_renderingEnabled; bool m_renderingEnabled;
bool m_needsResize; bool m_needsResize;
bool m_needsLockedFrameUpdate; // 是否需要渲染锁屏帧(锁屏时只渲染一帧) bool m_needsLockedFrameUpdate; // 是否需要渲染锁屏帧(锁屏时只渲染一帧)
bool m_isClosing; // 标记窗口正在关闭,防止在销毁过程中继续渲染
int m_frameCount; int m_frameCount;
uint32_t m_queueFamilyIndex; uint32_t m_queueFamilyIndex;
uint32_t m_currentFrame; uint32_t m_currentFrame;