From 9786baed86eb6020fb7e79c8d728ca603b85f231 Mon Sep 17 00:00:00 2001 From: ubuntu1804 Date: Tue, 11 Nov 2025 09:33:12 +0800 Subject: [PATCH] Use host-visible dynamic buffers for geometry Move geometry and text to single host-visible vertex/index buffers created once and updated via vkMapMemory (no staging). Add createDynamic*/updateDynamicBuffer helpers. Reduce vkWaitForFences timeout from 5s to 100ms and skip/log on timeout to avoid blocking. --- src/vulkanrenderer.cpp | 222 +++++++++++++++++++++-------------------- src/vulkanrenderer.h | 15 +++ 2 files changed, 131 insertions(+), 106 deletions(-) diff --git a/src/vulkanrenderer.cpp b/src/vulkanrenderer.cpp index 76afdca..cb6196f 100644 --- a/src/vulkanrenderer.cpp +++ b/src/vulkanrenderer.cpp @@ -497,90 +497,59 @@ void VulkanRenderer::recordCommandBuffer(VkCommandBuffer commandBuffer, paintingEnabled = false; } - // Update geometry buffers periodically (every 5 frames) to maintain animation - // 修复:不要每帧重建 buffers,但需要定期更新以保持动画效果 - static int geometryUpdateCounter = 0; - bool shouldUpdateGeometry = (paintingEnabled && - (m_circleVertexBuffer == VK_NULL_HANDLE || - (++geometryUpdateCounter % 5) == 0)); - - if (shouldUpdateGeometry) { + // 根本性修复:使用 host-visible buffers 进行动态更新 + // 这样可以直接映射内存更新,无需 staging buffer 和命令队列同步 + if (paintingEnabled) { std::vector circleVertices, waveVertices; std::vector circleIndices, waveIndices; - static int geomCreateCounter = 0; - if (geomCreateCounter++ % 60 == 0) { // Log every 60 updates (~12 seconds at 60fps) - std::cout << "Updating geometry buffers (#" << geomCreateCounter - << ") for: " << m_width << "x" << m_height << std::endl; - } - // 生成动态几何体(使用当前动画参数) generateRotatingCircles(circleVertices, circleIndices, rotationAngle); generateWaveEffect(waveVertices, waveIndices, wavePhase); - // Cleanup old buffers before creating new ones - if (m_circleVertexBuffer) { - vkDestroyBuffer(m_device, m_circleVertexBuffer, nullptr); - vkFreeMemory(m_device, m_circleVertexMemory, nullptr); - m_circleVertexBuffer = VK_NULL_HANDLE; - m_circleVertexMemory = VK_NULL_HANDLE; - } - if (m_circleIndexBuffer) { - vkDestroyBuffer(m_device, m_circleIndexBuffer, nullptr); - vkFreeMemory(m_device, m_circleIndexMemory, nullptr); - m_circleIndexBuffer = VK_NULL_HANDLE; - m_circleIndexMemory = VK_NULL_HANDLE; - } - if (m_waveVertexBuffer) { - vkDestroyBuffer(m_device, m_waveVertexBuffer, nullptr); - vkFreeMemory(m_device, m_waveVertexMemory, nullptr); - m_waveVertexBuffer = VK_NULL_HANDLE; - m_waveVertexMemory = VK_NULL_HANDLE; - } - if (m_waveIndexBuffer) { - vkDestroyBuffer(m_device, m_waveIndexBuffer, nullptr); - vkFreeMemory(m_device, m_waveIndexMemory, nullptr); - m_waveIndexBuffer = VK_NULL_HANDLE; - m_waveIndexMemory = VK_NULL_HANDLE; + // 首次创建 buffers(使用 host-visible 内存) + if (m_circleVertexBuffer == VK_NULL_HANDLE) { + VkDeviceSize circleVertexSize = sizeof(Vertex) * 1024; // 预分配足够空间 + VkDeviceSize circleIndexSize = sizeof(uint16_t) * 2048; + VkDeviceSize waveVertexSize = sizeof(Vertex) * 1024; + VkDeviceSize waveIndexSize = sizeof(uint16_t) * 2048; + + if (!createDynamicVertexBuffer(circleVertexSize, m_circleVertexBuffer, m_circleVertexMemory)) { + logError("Failed to create dynamic circle vertex buffer"); + return; + } + if (!createDynamicIndexBuffer(circleIndexSize, m_circleIndexBuffer, m_circleIndexMemory)) { + logError("Failed to create dynamic circle index buffer"); + return; + } + if (!createDynamicVertexBuffer(waveVertexSize, m_waveVertexBuffer, m_waveVertexMemory)) { + logError("Failed to create dynamic wave vertex buffer"); + return; + } + if (!createDynamicIndexBuffer(waveIndexSize, m_waveIndexBuffer, m_waveIndexMemory)) { + logError("Failed to create dynamic wave index buffer"); + return; + } + + std::cout << "Dynamic geometry buffers created (host-visible, no staging)" << std::endl; } - // Create circle buffers (only once) - 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; + // 直接更新 buffer 内容(通过内存映射,无需命令队列) + if (!circleVertices.empty() && !circleIndices.empty()) { + updateDynamicBuffer(m_circleVertexMemory, circleVertices.data(), + sizeof(Vertex) * circleVertices.size()); + updateDynamicBuffer(m_circleIndexMemory, circleIndices.data(), + sizeof(uint16_t) * circleIndices.size()); + m_circleIndexCount = circleIndices.size(); } - if (!createIndexBuffer(circleIndices, m_circleIndexBuffer, m_circleIndexMemory)) { - logError("Failed to create circle index buffer - skipping geometry rendering"); - vkDestroyBuffer(m_device, m_circleVertexBuffer, nullptr); - vkFreeMemory(m_device, m_circleVertexMemory, nullptr); - m_circleVertexBuffer = VK_NULL_HANDLE; - m_circleVertexMemory = VK_NULL_HANDLE; - m_circleIndexBuffer = VK_NULL_HANDLE; - m_circleIndexMemory = VK_NULL_HANDLE; - return; - } - m_circleIndexCount = circleIndices.size(); - // Create wave buffers (only once) - 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 (!waveVertices.empty() && !waveIndices.empty()) { + updateDynamicBuffer(m_waveVertexMemory, waveVertices.data(), + sizeof(Vertex) * waveVertices.size()); + updateDynamicBuffer(m_waveIndexMemory, waveIndices.data(), + sizeof(uint16_t) * waveIndices.size()); + m_waveIndexCount = waveIndices.size(); } - if (!createIndexBuffer(waveIndices, m_waveIndexBuffer, m_waveIndexMemory)) { - logError("Failed to create wave index buffer - skipping geometry rendering"); - vkDestroyBuffer(m_device, m_waveVertexBuffer, nullptr); - vkFreeMemory(m_device, m_waveVertexMemory, nullptr); - m_waveVertexBuffer = VK_NULL_HANDLE; - m_waveVertexMemory = VK_NULL_HANDLE; - m_waveIndexBuffer = VK_NULL_HANDLE; - m_waveIndexMemory = VK_NULL_HANDLE; - return; - } - m_waveIndexCount = waveIndices.size(); } // Update uniform buffer @@ -1876,6 +1845,39 @@ bool VulkanRenderer::createIndexBuffer(const std::vector& indices, return true; } +bool VulkanRenderer::createDynamicVertexBuffer(uint64_t size, VkBuffer& buffer, VkDeviceMemory& memory) +{ + // 创建 host-visible 的 vertex buffer,可以直接映射更新,无需 staging buffer + return createBuffer(size, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + buffer, memory); +} + +bool VulkanRenderer::createDynamicIndexBuffer(uint64_t size, VkBuffer& buffer, VkDeviceMemory& memory) +{ + // 创建 host-visible 的 index buffer,可以直接映射更新,无需 staging buffer + return createBuffer(size, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + buffer, memory); +} + +bool VulkanRenderer::updateDynamicBuffer(VkDeviceMemory memory, const void* data, uint64_t size) +{ + // 直接映射内存并更新,无需命令队列和同步 + void* mappedData = nullptr; + VkResult result = vkMapMemory(m_device, memory, 0, size, 0, &mappedData); + if (result != VK_SUCCESS || mappedData == nullptr) { + logError("Failed to map dynamic buffer memory"); + return false; + } + + memcpy(mappedData, data, size); + vkUnmapMemory(m_device, memory); + return true; +} + bool VulkanRenderer::createBuffer(uint64_t size, uint32_t usage, uint32_t properties, VkBuffer& buffer, VkDeviceMemory& memory) { @@ -2005,12 +2007,12 @@ bool VulkanRenderer::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, uint64_t return false; } - // 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 + // Wait for the fence with short timeout (100ms) to avoid blocking + // 使用短超时快速失败,避免长时间阻塞渲染循环导致设备丢失 + result = vkWaitForFences(m_device, 1, &fence, VK_TRUE, 100000000ULL); // 100 ms in nanoseconds if (result == VK_TIMEOUT) { - 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"); + std::cerr << "TIMEOUT: vkWaitForFences exceeded 100ms - GPU busy, will retry later" << std::endl; + logError("Timeout waiting for fence after copy (100ms) - skipping this buffer update"); vkDestroyFence(m_device, fence, nullptr); vkFreeCommandBuffers(m_device, m_transferCommandPool, 1, &commandBuffer); return false; @@ -2817,39 +2819,47 @@ void VulkanRenderer::drawText(VkCommandBuffer commandBuffer, int frameCount, } } - // Cleanup old buffers if they exist (only when rebuilding) - if (m_textVertexBuffer != VK_NULL_HANDLE) { - vkDestroyBuffer(m_device, m_textVertexBuffer, nullptr); - vkFreeMemory(m_device, m_textVertexMemory, nullptr); - m_textVertexBuffer = VK_NULL_HANDLE; - m_textVertexMemory = VK_NULL_HANDLE; - } - if (m_textIndexBuffer != VK_NULL_HANDLE) { - vkDestroyBuffer(m_device, m_textIndexBuffer, nullptr); - vkFreeMemory(m_device, m_textIndexMemory, nullptr); - m_textIndexBuffer = VK_NULL_HANDLE; - m_textIndexMemory = VK_NULL_HANDLE; + // 首次创建 text buffers(使用 host-visible 内存) + if (m_textVertexBuffer == VK_NULL_HANDLE) { + VkDeviceSize textVertexSize = sizeof(Vertex) * 4096; // 预分配足够空间 + VkDeviceSize textIndexSize = sizeof(uint16_t) * 6144; + + if (!createDynamicVertexBuffer(textVertexSize, m_textVertexBuffer, m_textVertexMemory)) { + logError("Failed to create dynamic text vertex buffer"); + m_lastRenderedText.clear(); + return; + } + if (!createDynamicIndexBuffer(textIndexSize, m_textIndexBuffer, m_textIndexMemory)) { + logError("Failed to create dynamic text index buffer"); + vkDestroyBuffer(m_device, m_textVertexBuffer, nullptr); + vkFreeMemory(m_device, m_textVertexMemory, nullptr); + m_textVertexBuffer = VK_NULL_HANDLE; + m_textVertexMemory = VK_NULL_HANDLE; + m_lastRenderedText.clear(); + return; + } + + std::cout << "Dynamic text buffers created (host-visible, no staging)" << std::endl; } - if (!createVertexBuffer(vertices, m_textVertexBuffer, m_textVertexMemory)) { - logError("Failed to create text vertex buffer - skipping text rendering"); - m_lastRenderedText.clear(); // 标记为无效,下次重试 - return; + // 直接更新 buffer 内容(通过内存映射,无需命令队列) + if (!vertices.empty() && !indices.empty()) { + if (!updateDynamicBuffer(m_textVertexMemory, vertices.data(), + sizeof(Vertex) * vertices.size())) { + logError("Failed to update text vertex buffer"); + m_lastRenderedText.clear(); + return; + } + if (!updateDynamicBuffer(m_textIndexMemory, indices.data(), + sizeof(uint16_t) * indices.size())) { + logError("Failed to update text index buffer"); + m_lastRenderedText.clear(); + return; + } + m_textIndexCount = indices.size(); + m_lastRenderedText = currentText; // 缓存当前文本内容 } - if (!createIndexBuffer(indices, m_textIndexBuffer, m_textIndexMemory)) { - logError("Failed to create text index buffer - skipping text rendering"); - vkDestroyBuffer(m_device, m_textVertexBuffer, nullptr); - vkFreeMemory(m_device, m_textVertexMemory, nullptr); - m_textVertexBuffer = VK_NULL_HANDLE; - m_textVertexMemory = VK_NULL_HANDLE; - m_lastRenderedText.clear(); // 标记为无效,下次重试 - return; - } - - m_textIndexCount = indices.size(); - m_lastRenderedText = currentText; // 缓存当前文本内容 - // Draw text with newly created buffers VkBuffer vertexBuffers[] = {m_textVertexBuffer}; VkDeviceSize offsets[] = {0}; diff --git a/src/vulkanrenderer.h b/src/vulkanrenderer.h index 5df68e7..91159da 100644 --- a/src/vulkanrenderer.h +++ b/src/vulkanrenderer.h @@ -228,6 +228,21 @@ private: */ bool copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, uint64_t size); + /** + * @brief 创建 host-visible 动态顶点缓冲区(可直接映射更新) + */ + bool createDynamicVertexBuffer(uint64_t size, VkBuffer& buffer, VkDeviceMemory& memory); + + /** + * @brief 创建 host-visible 动态索引缓冲区(可直接映射更新) + */ + bool createDynamicIndexBuffer(uint64_t size, VkBuffer& buffer, VkDeviceMemory& memory); + + /** + * @brief 更新动态缓冲区内容(通过内存映射) + */ + bool updateDynamicBuffer(VkDeviceMemory memory, const void* data, uint64_t size); + /** * @brief 查找内存类型 */