From fdb25ed816b24f906fde61139e731ad2dcb85708 Mon Sep 17 00:00:00 2001 From: ubuntu1804 Date: Tue, 11 Nov 2025 09:24:32 +0800 Subject: [PATCH] Optimize geometry updates and cache text buffers Update geometry only periodically (every 5 frames) and reduce logging frequency. Destroy and null out old buffers before recreating them to avoid double frees and leaks. Cache lastRenderedText and only rebuild text vertex/index buffers when the content or size changes; reuse buffers across frames and clean them up on rebuild or shutdown. Replace vkDeviceWaitIdle in copyBuffer with fence-based wait (longer timeout) and remove noisy debug prints. Added members and cleanup for text buffers and index count --- src/vulkanrenderer.cpp | 165 +++++++++++++++++++++++++++-------------- src/vulkanrenderer.h | 8 ++ 2 files changed, 117 insertions(+), 56 deletions(-) diff --git a/src/vulkanrenderer.cpp b/src/vulkanrenderer.cpp index 68e3f09..76afdca 100644 --- a/src/vulkanrenderer.cpp +++ b/src/vulkanrenderer.cpp @@ -53,6 +53,11 @@ VulkanRenderer::VulkanRenderer() , m_fontTextureMemory(VK_NULL_HANDLE) , m_fontTextureView(VK_NULL_HANDLE) , m_fontSampler(VK_NULL_HANDLE) + , m_textVertexBuffer(VK_NULL_HANDLE) + , m_textVertexMemory(VK_NULL_HANDLE) + , m_textIndexBuffer(VK_NULL_HANDLE) + , m_textIndexMemory(VK_NULL_HANDLE) + , m_textIndexCount(0) , m_msaaImage(VK_NULL_HANDLE) , m_msaaImageMemory(VK_NULL_HANDLE) , m_msaaImageView(VK_NULL_HANDLE) @@ -372,6 +377,11 @@ void VulkanRenderer::cleanup() if (m_backgroundVertexMemory) vkFreeMemory(m_device, m_backgroundVertexMemory, nullptr); if (m_backgroundIndexBuffer) vkDestroyBuffer(m_device, m_backgroundIndexBuffer, nullptr); if (m_backgroundIndexMemory) vkFreeMemory(m_device, m_backgroundIndexMemory, nullptr); + + if (m_textVertexBuffer) vkDestroyBuffer(m_device, m_textVertexBuffer, nullptr); + if (m_textVertexMemory) vkFreeMemory(m_device, m_textVertexMemory, nullptr); + if (m_textIndexBuffer) vkDestroyBuffer(m_device, m_textIndexBuffer, nullptr); + if (m_textIndexMemory) vkFreeMemory(m_device, m_textIndexMemory, nullptr); if (m_circleVertexBuffer) vkDestroyBuffer(m_device, m_circleVertexBuffer, nullptr); if (m_circleVertexMemory) vkFreeMemory(m_device, m_circleVertexMemory, nullptr); @@ -487,31 +497,54 @@ void VulkanRenderer::recordCommandBuffer(VkCommandBuffer commandBuffer, paintingEnabled = false; } - // Update geometry buffers BEFORE recording commands - if (paintingEnabled) { + // 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) { std::vector circleVertices, waveVertices; std::vector circleIndices, waveIndices; - // Debug: Print dimensions used for geometry generation - static int geomDebugCounter = 0; - if (geomDebugCounter++ % 300 == 0) { // Every ~5 seconds at 60fps - std::cout << "VulkanRenderer geometry generation using: " - << m_width << "x" << m_height << std::endl; + 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); - // Update or create circle buffers + // 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; } + // 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; @@ -520,22 +553,17 @@ void VulkanRenderer::recordCommandBuffer(VkCommandBuffer commandBuffer, } 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(); - // Update or create wave buffers - if (m_waveVertexBuffer) { - vkDestroyBuffer(m_device, m_waveVertexBuffer, nullptr); - vkFreeMemory(m_device, m_waveVertexMemory, nullptr); - } - if (m_waveIndexBuffer) { - vkDestroyBuffer(m_device, m_waveIndexBuffer, nullptr); - vkFreeMemory(m_device, m_waveIndexMemory, nullptr); - } - + // 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; @@ -544,6 +572,10 @@ void VulkanRenderer::recordCommandBuffer(VkCommandBuffer commandBuffer, } 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; @@ -1889,10 +1921,7 @@ bool VulkanRenderer::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, uint64_t // 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) { @@ -1976,8 +2005,6 @@ bool VulkanRenderer::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, uint64_t return false; } - 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 @@ -1994,8 +2021,6 @@ bool VulkanRenderer::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, uint64_t vkFreeCommandBuffers(m_device, m_transferCommandPool, 1, &commandBuffer); return false; } - - std::cout << "copyBuffer completed successfully" << std::endl; // Cleanup fence vkDestroyFence(m_device, fence, nullptr); @@ -2636,6 +2661,36 @@ void VulkanRenderer::drawText(VkCommandBuffer commandBuffer, int frameCount, return; // Text rendering not available } + // 构建当前帧的文本内容(用于缓存比较) + std::string currentText = lockInfo + "|" + std::to_string(paintingEnabled) + "|" + + std::to_string(m_width) + "x" + std::to_string(m_height); + + // 检查文本是否变化,只在变化时重建 buffers + bool needsRebuild = (currentText != m_lastRenderedText) || (m_textVertexBuffer == VK_NULL_HANDLE); + + // 如果文本没有变化且 buffers 已存在,直接使用缓存的 buffers + if (!needsRebuild && m_textVertexBuffer != VK_NULL_HANDLE && m_textIndexBuffer != VK_NULL_HANDLE) { + // Bind text pipeline + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_textPipeline); + + // Bind descriptor sets for text rendering (if using textures) + if (!m_descriptorSets.empty()) { + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + m_textPipelineLayout, 0, 1, + &m_descriptorSets[0], 0, nullptr); + } + + // Draw text using cached buffers + VkBuffer vertexBuffers[] = {m_textVertexBuffer}; + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); + vkCmdBindIndexBuffer(commandBuffer, m_textIndexBuffer, 0, VK_INDEX_TYPE_UINT16); + vkCmdDrawIndexed(commandBuffer, m_textIndexCount, 1, 0, 0, 0); + return; + } + + // 文本变化了,需要重建 buffers + // Bind text pipeline vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_textPipeline); @@ -2743,7 +2798,7 @@ void VulkanRenderer::drawText(VkCommandBuffer commandBuffer, int frameCount, generateTextQuads(hint, hintX, m_height - 50.0f * scaleY, hintScale, yellow, vertices, indices); } - // Create temporary buffers for text + // Create/update text buffers if (vertices.empty() || indices.empty()) { if (textDebugCounter % 300 == 1) { std::cout << "No text vertices generated - skipping" << std::endl; @@ -2751,7 +2806,7 @@ void VulkanRenderer::drawText(VkCommandBuffer commandBuffer, int frameCount, return; } - if (textDebugCounter % 300 == 1) { + if (textDebugCounter % 300 == 1 || needsRebuild) { std::cout << "Drawing text with " << vertices.size() << " vertices, " << indices.size() << " indices" << std::endl; std::cout << "Window size: " << m_width << "x" << m_height << std::endl; @@ -2762,47 +2817,45 @@ void VulkanRenderer::drawText(VkCommandBuffer commandBuffer, int frameCount, } } - // Use static buffers for text rendering to avoid recreating every frame - static VkBuffer textVertexBuffer = VK_NULL_HANDLE; - static VkDeviceMemory textVertexMemory = VK_NULL_HANDLE; - static VkBuffer textIndexBuffer = VK_NULL_HANDLE; - static VkDeviceMemory textIndexMemory = VK_NULL_HANDLE; - - // Cleanup old buffers if they exist - if (textVertexBuffer != VK_NULL_HANDLE) { - vkDestroyBuffer(m_device, textVertexBuffer, nullptr); - vkFreeMemory(m_device, textVertexMemory, nullptr); - textVertexBuffer = VK_NULL_HANDLE; - textVertexMemory = VK_NULL_HANDLE; + // 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 (textIndexBuffer != VK_NULL_HANDLE) { - vkDestroyBuffer(m_device, textIndexBuffer, nullptr); - vkFreeMemory(m_device, textIndexMemory, nullptr); - textIndexBuffer = VK_NULL_HANDLE; - textIndexMemory = 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; } - if (!createVertexBuffer(vertices, textVertexBuffer, textVertexMemory)) { + if (!createVertexBuffer(vertices, m_textVertexBuffer, m_textVertexMemory)) { logError("Failed to create text vertex buffer - skipping text rendering"); + m_lastRenderedText.clear(); // 标记为无效,下次重试 return; } - if (!createIndexBuffer(indices, textIndexBuffer, textIndexMemory)) { + if (!createIndexBuffer(indices, m_textIndexBuffer, m_textIndexMemory)) { logError("Failed to create text index buffer - skipping text rendering"); - vkDestroyBuffer(m_device, textVertexBuffer, nullptr); - vkFreeMemory(m_device, textVertexMemory, nullptr); + 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; } - // Draw text - VkBuffer vertexBuffers[] = {textVertexBuffer}; + m_textIndexCount = indices.size(); + m_lastRenderedText = currentText; // 缓存当前文本内容 + + // Draw text with newly created buffers + VkBuffer vertexBuffers[] = {m_textVertexBuffer}; VkDeviceSize offsets[] = {0}; vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); - vkCmdBindIndexBuffer(commandBuffer, textIndexBuffer, 0, VK_INDEX_TYPE_UINT16); - vkCmdDrawIndexed(commandBuffer, indices.size(), 1, 0, 0, 0); - - // Note: Buffers will persist and be reused next frame - // They will be cleaned up when the function is called again or at shutdown + vkCmdBindIndexBuffer(commandBuffer, m_textIndexBuffer, 0, VK_INDEX_TYPE_UINT16); + vkCmdDrawIndexed(commandBuffer, m_textIndexCount, 1, 0, 0, 0); } void VulkanRenderer::logError(const char* message) diff --git a/src/vulkanrenderer.h b/src/vulkanrenderer.h index 303b4a5..5df68e7 100644 --- a/src/vulkanrenderer.h +++ b/src/vulkanrenderer.h @@ -368,6 +368,14 @@ private: VkImageView m_fontTextureView; VkSampler m_fontSampler; std::map m_charInfoMap; + + // 文本缓存,避免每帧重建 text buffers + std::string m_lastRenderedText; + VkBuffer m_textVertexBuffer; + VkDeviceMemory m_textVertexMemory; + VkBuffer m_textIndexBuffer; + VkDeviceMemory m_textIndexMemory; + uint32_t m_textIndexCount; // MSAA 资源 VkImage m_msaaImage;