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:
parent
95b83d5ed3
commit
fdb25ed816
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue