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
This commit is contained in:
ubuntu1804 2025-11-11 09:24:32 +08:00
parent 95b83d5ed3
commit fdb25ed816
2 changed files with 117 additions and 56 deletions

View File

@ -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<Vertex> circleVertices, waveVertices;
std::vector<uint16_t> 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)

View File

@ -368,6 +368,14 @@ private:
VkImageView m_fontTextureView;
VkSampler m_fontSampler;
std::map<char, CharInfo> 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;