diff --git a/src/vulkanrenderer.cpp b/src/vulkanrenderer.cpp index 2a093b2..68e3f09 100644 --- a/src/vulkanrenderer.cpp +++ b/src/vulkanrenderer.cpp @@ -513,9 +513,15 @@ void VulkanRenderer::recordCommandBuffer(VkCommandBuffer commandBuffer, } if (!createVertexBuffer(circleVertices, m_circleVertexBuffer, m_circleVertexMemory)) { + logError("Failed to create circle vertex buffer - skipping geometry rendering"); + m_circleVertexBuffer = VK_NULL_HANDLE; + m_circleVertexMemory = VK_NULL_HANDLE; return; } if (!createIndexBuffer(circleIndices, m_circleIndexBuffer, m_circleIndexMemory)) { + logError("Failed to create circle index buffer - skipping geometry rendering"); + m_circleIndexBuffer = VK_NULL_HANDLE; + m_circleIndexMemory = VK_NULL_HANDLE; return; } m_circleIndexCount = circleIndices.size(); @@ -531,9 +537,15 @@ void VulkanRenderer::recordCommandBuffer(VkCommandBuffer commandBuffer, } if (!createVertexBuffer(waveVertices, m_waveVertexBuffer, m_waveVertexMemory)) { + logError("Failed to create wave vertex buffer - skipping geometry rendering"); + m_waveVertexBuffer = VK_NULL_HANDLE; + m_waveVertexMemory = VK_NULL_HANDLE; return; } if (!createIndexBuffer(waveIndices, m_waveIndexBuffer, m_waveIndexMemory)) { + logError("Failed to create wave index buffer - skipping geometry rendering"); + m_waveIndexBuffer = VK_NULL_HANDLE; + m_waveIndexMemory = VK_NULL_HANDLE; return; } m_waveIndexCount = waveIndices.size(); @@ -1759,8 +1771,13 @@ bool VulkanRenderer::createVertexBuffer(const std::vector& vertices, if (!copyBuffer(stagingBuffer, buffer, bufferSize)) { logError("Failed to copy staging buffer to vertex buffer"); + // 清理已创建的目标 buffer 和 staging buffer + vkDestroyBuffer(m_device, buffer, nullptr); + vkFreeMemory(m_device, memory, nullptr); vkDestroyBuffer(m_device, stagingBuffer, nullptr); vkFreeMemory(m_device, stagingBufferMemory, nullptr); + buffer = VK_NULL_HANDLE; + memory = VK_NULL_HANDLE; return false; } @@ -1811,8 +1828,13 @@ bool VulkanRenderer::createIndexBuffer(const std::vector& indices, if (!copyBuffer(stagingBuffer, buffer, bufferSize)) { logError("Failed to copy staging buffer to index buffer"); + // 清理已创建的目标 buffer 和 staging buffer + vkDestroyBuffer(m_device, buffer, nullptr); + vkFreeMemory(m_device, memory, nullptr); vkDestroyBuffer(m_device, stagingBuffer, nullptr); vkFreeMemory(m_device, stagingBufferMemory, nullptr); + buffer = VK_NULL_HANDLE; + memory = VK_NULL_HANDLE; return false; } @@ -1864,14 +1886,14 @@ bool VulkanRenderer::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, uint64_t return false; } - // Wait for device to be idle to avoid queue contention - // This ensures no other operations are using the queue - VkResult idleResult = vkDeviceWaitIdle(m_device); - if (idleResult != VK_SUCCESS) { - logError("Failed to wait for device idle before buffer copy"); - return false; - } - + // Note: 移除 vkDeviceWaitIdle,改用 fence 精确同步 + // vkDeviceWaitIdle 在三缓冲场景下会导致等待失败 + // 因为双缓冲的 fence 机制只能确保 2 帧的同步 + + static int copyCount = 0; + std::cout << "copyBuffer called (#" << copyCount++ << "), size=" << size + << " bytes" << std::endl; + // Create a one-time command pool if not exists if (m_transferCommandPool == VK_NULL_HANDLE) { VkCommandPoolCreateInfo poolInfo = {}; @@ -1947,25 +1969,33 @@ bool VulkanRenderer::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, uint64_t result = vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, fence); if (result != VK_SUCCESS) { + std::cerr << "vkQueueSubmit failed with error code: " << result << std::endl; logError("Failed to submit copy command"); vkDestroyFence(m_device, fence, nullptr); vkFreeCommandBuffers(m_device, m_transferCommandPool, 1, &commandBuffer); return false; } - // Wait for the fence with a reasonable timeout (1 second) - result = vkWaitForFences(m_device, 1, &fence, VK_TRUE, 1000000000); // 1 second in nanoseconds + std::cout << "Waiting for fence (timeout=5s)..." << std::endl; + + // Wait for the fence with extended timeout for triple buffering (5 seconds) + // 三缓冲场景下需要更长的等待时间,因为可能有多帧在 GPU 执行 + result = vkWaitForFences(m_device, 1, &fence, VK_TRUE, 5000000000ULL); // 5 seconds in nanoseconds if (result == VK_TIMEOUT) { - logError("Timeout waiting for fence after copy - queue may be busy"); + std::cerr << "TIMEOUT: vkWaitForFences exceeded 5 seconds - GPU may be overloaded" << std::endl; + logError("Timeout waiting for fence after copy (5s) - queue may be busy with triple buffering"); vkDestroyFence(m_device, fence, nullptr); vkFreeCommandBuffers(m_device, m_transferCommandPool, 1, &commandBuffer); return false; } else if (result != VK_SUCCESS) { + std::cerr << "vkWaitForFences failed with error code: " << result << std::endl; logError("Failed to wait for fence after copy"); vkDestroyFence(m_device, fence, nullptr); vkFreeCommandBuffers(m_device, m_transferCommandPool, 1, &commandBuffer); return false; } + + std::cout << "copyBuffer completed successfully" << std::endl; // Cleanup fence vkDestroyFence(m_device, fence, nullptr); @@ -2753,14 +2783,14 @@ void VulkanRenderer::drawText(VkCommandBuffer commandBuffer, int frameCount, } if (!createVertexBuffer(vertices, textVertexBuffer, textVertexMemory)) { + logError("Failed to create text vertex buffer - skipping text rendering"); return; } if (!createIndexBuffer(indices, textIndexBuffer, textIndexMemory)) { + logError("Failed to create text index buffer - skipping text rendering"); vkDestroyBuffer(m_device, textVertexBuffer, nullptr); vkFreeMemory(m_device, textVertexMemory, nullptr); - textVertexBuffer = VK_NULL_HANDLE; - textVertexMemory = VK_NULL_HANDLE; return; } diff --git a/src/vulkanrenderer.h b/src/vulkanrenderer.h index 2b9b337..303b4a5 100644 --- a/src/vulkanrenderer.h +++ b/src/vulkanrenderer.h @@ -389,8 +389,8 @@ private: // 错误回调 void (*m_errorCallback)(const char*); - // 最大帧数 - static const int MAX_FRAMES_IN_FLIGHT = 2; + // 最大帧数(支持三缓冲) + static const int MAX_FRAMES_IN_FLIGHT = 3; }; #endif // VULKANRENDERER_H \ No newline at end of file diff --git a/src/vulkanwidget.cpp b/src/vulkanwidget.cpp index bebf6ab..dbf6485 100644 --- a/src/vulkanwidget.cpp +++ b/src/vulkanwidget.cpp @@ -478,11 +478,16 @@ bool VulkanWidget::createSwapchain() qDebug() << "Final swapchain extent:" << m_surfaceWidth << "x" << m_surfaceHeight; - // Determine image count + // Determine image count - limit to MAX_FRAMES_IN_FLIGHT for proper synchronization uint32_t imageCount = capabilities.minImageCount + 1; if (capabilities.maxImageCount > 0 && imageCount > capabilities.maxImageCount) { imageCount = capabilities.maxImageCount; } + // 确保不超过 MAX_FRAMES_IN_FLIGHT,避免三缓冲同步问题 + if (imageCount > MAX_FRAMES_IN_FLIGHT) { + qDebug() << "Limiting swapchain images from" << imageCount << "to" << MAX_FRAMES_IN_FLIGHT; + imageCount = MAX_FRAMES_IN_FLIGHT; + } VkSwapchainCreateInfoKHR createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; @@ -539,8 +544,8 @@ bool VulkanWidget::createSwapchain() m_swapchainImageViews[i] = reinterpret_cast(imageView); } - qDebug() << "Swapchain created successfully with" << imageCount << "images, size:" - << m_surfaceWidth << "x" << m_surfaceHeight; + qDebug() << "Swapchain created successfully with" << imageCount << "images (MAX_FRAMES_IN_FLIGHT=" + << MAX_FRAMES_IN_FLIGHT << "), size:" << m_surfaceWidth << "x" << m_surfaceHeight; return true; } diff --git a/src/vulkanwidget.h b/src/vulkanwidget.h index fc48090..a113955 100644 --- a/src/vulkanwidget.h +++ b/src/vulkanwidget.h @@ -261,8 +261,8 @@ private: int m_lockPaintFrameCount; int m_lockCount; - // 常量 - static const int MAX_FRAMES_IN_FLIGHT = 2; + // 常量(支持三缓冲) + static const int MAX_FRAMES_IN_FLIGHT = 3; }; #endif // VULKANWIDGET_H