Support triple buffering and improve buffer sync
Increase MAX_FRAMES_IN_FLIGHT to 3 and cap swapchain images accordingly Replace vkDeviceWaitIdle with per-copy fence, extend wait timeout and add diagnostic logging and counters Add stronger error logging and cleanup on vertex/index buffer creation and copy failures; reset handles to VK_NULL_HANDLE on failure
This commit is contained in:
parent
90d770dd85
commit
95b83d5ed3
|
|
@ -513,9 +513,15 @@ void VulkanRenderer::recordCommandBuffer(VkCommandBuffer commandBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!createVertexBuffer(circleVertices, m_circleVertexBuffer, m_circleVertexMemory)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
if (!createIndexBuffer(circleIndices, m_circleIndexBuffer, m_circleIndexMemory)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
m_circleIndexCount = circleIndices.size();
|
m_circleIndexCount = circleIndices.size();
|
||||||
|
|
@ -531,9 +537,15 @@ void VulkanRenderer::recordCommandBuffer(VkCommandBuffer commandBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!createVertexBuffer(waveVertices, m_waveVertexBuffer, m_waveVertexMemory)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
if (!createIndexBuffer(waveIndices, m_waveIndexBuffer, m_waveIndexMemory)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
m_waveIndexCount = waveIndices.size();
|
m_waveIndexCount = waveIndices.size();
|
||||||
|
|
@ -1759,8 +1771,13 @@ bool VulkanRenderer::createVertexBuffer(const std::vector<Vertex>& vertices,
|
||||||
|
|
||||||
if (!copyBuffer(stagingBuffer, buffer, bufferSize)) {
|
if (!copyBuffer(stagingBuffer, buffer, bufferSize)) {
|
||||||
logError("Failed to copy staging buffer to vertex buffer");
|
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);
|
vkDestroyBuffer(m_device, stagingBuffer, nullptr);
|
||||||
vkFreeMemory(m_device, stagingBufferMemory, nullptr);
|
vkFreeMemory(m_device, stagingBufferMemory, nullptr);
|
||||||
|
buffer = VK_NULL_HANDLE;
|
||||||
|
memory = VK_NULL_HANDLE;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1811,8 +1828,13 @@ bool VulkanRenderer::createIndexBuffer(const std::vector<uint16_t>& indices,
|
||||||
|
|
||||||
if (!copyBuffer(stagingBuffer, buffer, bufferSize)) {
|
if (!copyBuffer(stagingBuffer, buffer, bufferSize)) {
|
||||||
logError("Failed to copy staging buffer to index buffer");
|
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);
|
vkDestroyBuffer(m_device, stagingBuffer, nullptr);
|
||||||
vkFreeMemory(m_device, stagingBufferMemory, nullptr);
|
vkFreeMemory(m_device, stagingBufferMemory, nullptr);
|
||||||
|
buffer = VK_NULL_HANDLE;
|
||||||
|
memory = VK_NULL_HANDLE;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1864,14 +1886,14 @@ bool VulkanRenderer::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, uint64_t
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for device to be idle to avoid queue contention
|
// Note: 移除 vkDeviceWaitIdle,改用 fence 精确同步
|
||||||
// This ensures no other operations are using the queue
|
// vkDeviceWaitIdle 在三缓冲场景下会导致等待失败
|
||||||
VkResult idleResult = vkDeviceWaitIdle(m_device);
|
// 因为双缓冲的 fence 机制只能确保 2 帧的同步
|
||||||
if (idleResult != VK_SUCCESS) {
|
|
||||||
logError("Failed to wait for device idle before buffer copy");
|
static int copyCount = 0;
|
||||||
return false;
|
std::cout << "copyBuffer called (#" << copyCount++ << "), size=" << size
|
||||||
}
|
<< " bytes" << std::endl;
|
||||||
|
|
||||||
// Create a one-time command pool if not exists
|
// Create a one-time command pool if not exists
|
||||||
if (m_transferCommandPool == VK_NULL_HANDLE) {
|
if (m_transferCommandPool == VK_NULL_HANDLE) {
|
||||||
VkCommandPoolCreateInfo poolInfo = {};
|
VkCommandPoolCreateInfo poolInfo = {};
|
||||||
|
|
@ -1947,25 +1969,33 @@ bool VulkanRenderer::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, uint64_t
|
||||||
|
|
||||||
result = vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, fence);
|
result = vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, fence);
|
||||||
if (result != VK_SUCCESS) {
|
if (result != VK_SUCCESS) {
|
||||||
|
std::cerr << "vkQueueSubmit failed with error code: " << result << std::endl;
|
||||||
logError("Failed to submit copy command");
|
logError("Failed to submit copy command");
|
||||||
vkDestroyFence(m_device, fence, nullptr);
|
vkDestroyFence(m_device, fence, nullptr);
|
||||||
vkFreeCommandBuffers(m_device, m_transferCommandPool, 1, &commandBuffer);
|
vkFreeCommandBuffers(m_device, m_transferCommandPool, 1, &commandBuffer);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the fence with a reasonable timeout (1 second)
|
std::cout << "Waiting for fence (timeout=5s)..." << std::endl;
|
||||||
result = vkWaitForFences(m_device, 1, &fence, VK_TRUE, 1000000000); // 1 second in nanoseconds
|
|
||||||
|
// 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) {
|
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);
|
vkDestroyFence(m_device, fence, nullptr);
|
||||||
vkFreeCommandBuffers(m_device, m_transferCommandPool, 1, &commandBuffer);
|
vkFreeCommandBuffers(m_device, m_transferCommandPool, 1, &commandBuffer);
|
||||||
return false;
|
return false;
|
||||||
} else if (result != VK_SUCCESS) {
|
} else if (result != VK_SUCCESS) {
|
||||||
|
std::cerr << "vkWaitForFences failed with error code: " << result << std::endl;
|
||||||
logError("Failed to wait for fence after copy");
|
logError("Failed to wait for fence after copy");
|
||||||
vkDestroyFence(m_device, fence, nullptr);
|
vkDestroyFence(m_device, fence, nullptr);
|
||||||
vkFreeCommandBuffers(m_device, m_transferCommandPool, 1, &commandBuffer);
|
vkFreeCommandBuffers(m_device, m_transferCommandPool, 1, &commandBuffer);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cout << "copyBuffer completed successfully" << std::endl;
|
||||||
|
|
||||||
// Cleanup fence
|
// Cleanup fence
|
||||||
vkDestroyFence(m_device, fence, nullptr);
|
vkDestroyFence(m_device, fence, nullptr);
|
||||||
|
|
@ -2753,14 +2783,14 @@ void VulkanRenderer::drawText(VkCommandBuffer commandBuffer, int frameCount,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!createVertexBuffer(vertices, textVertexBuffer, textVertexMemory)) {
|
if (!createVertexBuffer(vertices, textVertexBuffer, textVertexMemory)) {
|
||||||
|
logError("Failed to create text vertex buffer - skipping text rendering");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!createIndexBuffer(indices, textIndexBuffer, textIndexMemory)) {
|
if (!createIndexBuffer(indices, textIndexBuffer, textIndexMemory)) {
|
||||||
|
logError("Failed to create text index buffer - skipping text rendering");
|
||||||
vkDestroyBuffer(m_device, textVertexBuffer, nullptr);
|
vkDestroyBuffer(m_device, textVertexBuffer, nullptr);
|
||||||
vkFreeMemory(m_device, textVertexMemory, nullptr);
|
vkFreeMemory(m_device, textVertexMemory, nullptr);
|
||||||
textVertexBuffer = VK_NULL_HANDLE;
|
|
||||||
textVertexMemory = VK_NULL_HANDLE;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -389,8 +389,8 @@ private:
|
||||||
// 错误回调
|
// 错误回调
|
||||||
void (*m_errorCallback)(const char*);
|
void (*m_errorCallback)(const char*);
|
||||||
|
|
||||||
// 最大帧数
|
// 最大帧数(支持三缓冲)
|
||||||
static const int MAX_FRAMES_IN_FLIGHT = 2;
|
static const int MAX_FRAMES_IN_FLIGHT = 3;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VULKANRENDERER_H
|
#endif // VULKANRENDERER_H
|
||||||
|
|
@ -478,11 +478,16 @@ bool VulkanWidget::createSwapchain()
|
||||||
|
|
||||||
qDebug() << "Final swapchain extent:" << m_surfaceWidth << "x" << m_surfaceHeight;
|
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;
|
uint32_t imageCount = capabilities.minImageCount + 1;
|
||||||
if (capabilities.maxImageCount > 0 && imageCount > capabilities.maxImageCount) {
|
if (capabilities.maxImageCount > 0 && imageCount > capabilities.maxImageCount) {
|
||||||
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 = {};
|
VkSwapchainCreateInfoKHR createInfo = {};
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||||
|
|
@ -539,8 +544,8 @@ bool VulkanWidget::createSwapchain()
|
||||||
m_swapchainImageViews[i] = reinterpret_cast<void*>(imageView);
|
m_swapchainImageViews[i] = reinterpret_cast<void*>(imageView);
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Swapchain created successfully with" << imageCount << "images, size:"
|
qDebug() << "Swapchain created successfully with" << imageCount << "images (MAX_FRAMES_IN_FLIGHT="
|
||||||
<< m_surfaceWidth << "x" << m_surfaceHeight;
|
<< MAX_FRAMES_IN_FLIGHT << "), size:" << m_surfaceWidth << "x" << m_surfaceHeight;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -261,8 +261,8 @@ private:
|
||||||
int m_lockPaintFrameCount;
|
int m_lockPaintFrameCount;
|
||||||
int m_lockCount;
|
int m_lockCount;
|
||||||
|
|
||||||
// 常量
|
// 常量(支持三缓冲)
|
||||||
static const int MAX_FRAMES_IN_FLIGHT = 2;
|
static const int MAX_FRAMES_IN_FLIGHT = 3;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VULKANWIDGET_H
|
#endif // VULKANWIDGET_H
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue