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.
This commit is contained in:
ubuntu1804 2025-11-11 09:33:12 +08:00
parent fdb25ed816
commit 9786baed86
2 changed files with 131 additions and 106 deletions

View File

@ -497,90 +497,59 @@ void VulkanRenderer::recordCommandBuffer(VkCommandBuffer commandBuffer,
paintingEnabled = false; paintingEnabled = false;
} }
// Update geometry buffers periodically (every 5 frames) to maintain animation // 根本性修复:使用 host-visible buffers 进行动态更新
// 修复:不要每帧重建 buffers但需要定期更新以保持动画效果 // 这样可以直接映射内存更新,无需 staging buffer 和命令队列同步
static int geometryUpdateCounter = 0; if (paintingEnabled) {
bool shouldUpdateGeometry = (paintingEnabled &&
(m_circleVertexBuffer == VK_NULL_HANDLE ||
(++geometryUpdateCounter % 5) == 0));
if (shouldUpdateGeometry) {
std::vector<Vertex> circleVertices, waveVertices; std::vector<Vertex> circleVertices, waveVertices;
std::vector<uint16_t> circleIndices, waveIndices; std::vector<uint16_t> 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); generateRotatingCircles(circleVertices, circleIndices, rotationAngle);
generateWaveEffect(waveVertices, waveIndices, wavePhase); generateWaveEffect(waveVertices, waveIndices, wavePhase);
// Cleanup old buffers before creating new ones // 首次创建 buffers使用 host-visible 内存)
if (m_circleVertexBuffer) { if (m_circleVertexBuffer == VK_NULL_HANDLE) {
vkDestroyBuffer(m_device, m_circleVertexBuffer, nullptr); VkDeviceSize circleVertexSize = sizeof(Vertex) * 1024; // 预分配足够空间
vkFreeMemory(m_device, m_circleVertexMemory, nullptr); VkDeviceSize circleIndexSize = sizeof(uint16_t) * 2048;
m_circleVertexBuffer = VK_NULL_HANDLE; VkDeviceSize waveVertexSize = sizeof(Vertex) * 1024;
m_circleVertexMemory = VK_NULL_HANDLE; VkDeviceSize waveIndexSize = sizeof(uint16_t) * 2048;
}
if (m_circleIndexBuffer) { if (!createDynamicVertexBuffer(circleVertexSize, m_circleVertexBuffer, m_circleVertexMemory)) {
vkDestroyBuffer(m_device, m_circleIndexBuffer, nullptr); logError("Failed to create dynamic circle vertex buffer");
vkFreeMemory(m_device, m_circleIndexMemory, nullptr); return;
m_circleIndexBuffer = VK_NULL_HANDLE; }
m_circleIndexMemory = VK_NULL_HANDLE; if (!createDynamicIndexBuffer(circleIndexSize, m_circleIndexBuffer, m_circleIndexMemory)) {
} logError("Failed to create dynamic circle index buffer");
if (m_waveVertexBuffer) { return;
vkDestroyBuffer(m_device, m_waveVertexBuffer, nullptr); }
vkFreeMemory(m_device, m_waveVertexMemory, nullptr); if (!createDynamicVertexBuffer(waveVertexSize, m_waveVertexBuffer, m_waveVertexMemory)) {
m_waveVertexBuffer = VK_NULL_HANDLE; logError("Failed to create dynamic wave vertex buffer");
m_waveVertexMemory = VK_NULL_HANDLE; return;
} }
if (m_waveIndexBuffer) { if (!createDynamicIndexBuffer(waveIndexSize, m_waveIndexBuffer, m_waveIndexMemory)) {
vkDestroyBuffer(m_device, m_waveIndexBuffer, nullptr); logError("Failed to create dynamic wave index buffer");
vkFreeMemory(m_device, m_waveIndexMemory, nullptr); return;
m_waveIndexBuffer = VK_NULL_HANDLE; }
m_waveIndexMemory = VK_NULL_HANDLE;
std::cout << "Dynamic geometry buffers created (host-visible, no staging)" << std::endl;
} }
// Create circle buffers (only once) // 直接更新 buffer 内容(通过内存映射,无需命令队列)
if (!createVertexBuffer(circleVertices, m_circleVertexBuffer, m_circleVertexMemory)) { if (!circleVertices.empty() && !circleIndices.empty()) {
logError("Failed to create circle vertex buffer - skipping geometry rendering"); updateDynamicBuffer(m_circleVertexMemory, circleVertices.data(),
m_circleVertexBuffer = VK_NULL_HANDLE; sizeof(Vertex) * circleVertices.size());
m_circleVertexMemory = VK_NULL_HANDLE; updateDynamicBuffer(m_circleIndexMemory, circleIndices.data(),
return; 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 (!waveVertices.empty() && !waveIndices.empty()) {
if (!createVertexBuffer(waveVertices, m_waveVertexBuffer, m_waveVertexMemory)) { updateDynamicBuffer(m_waveVertexMemory, waveVertices.data(),
logError("Failed to create wave vertex buffer - skipping geometry rendering"); sizeof(Vertex) * waveVertices.size());
m_waveVertexBuffer = VK_NULL_HANDLE; updateDynamicBuffer(m_waveIndexMemory, waveIndices.data(),
m_waveVertexMemory = VK_NULL_HANDLE; sizeof(uint16_t) * waveIndices.size());
return; 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 // Update uniform buffer
@ -1876,6 +1845,39 @@ bool VulkanRenderer::createIndexBuffer(const std::vector<uint16_t>& indices,
return true; 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, bool VulkanRenderer::createBuffer(uint64_t size, uint32_t usage, uint32_t properties,
VkBuffer& buffer, VkDeviceMemory& memory) VkBuffer& buffer, VkDeviceMemory& memory)
{ {
@ -2005,12 +2007,12 @@ bool VulkanRenderer::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, uint64_t
return false; return false;
} }
// Wait for the fence with extended timeout for triple buffering (5 seconds) // Wait for the fence with short timeout (100ms) to avoid blocking
// 三缓冲场景下需要更长的等待时间,因为可能有多帧在 GPU 执行 // 使用短超时快速失败,避免长时间阻塞渲染循环导致设备丢失
result = vkWaitForFences(m_device, 1, &fence, VK_TRUE, 5000000000ULL); // 5 seconds in nanoseconds result = vkWaitForFences(m_device, 1, &fence, VK_TRUE, 100000000ULL); // 100 ms in nanoseconds
if (result == VK_TIMEOUT) { if (result == VK_TIMEOUT) {
std::cerr << "TIMEOUT: vkWaitForFences exceeded 5 seconds - GPU may be overloaded" << std::endl; std::cerr << "TIMEOUT: vkWaitForFences exceeded 100ms - GPU busy, will retry later" << std::endl;
logError("Timeout waiting for fence after copy (5s) - queue may be busy with triple buffering"); logError("Timeout waiting for fence after copy (100ms) - skipping this buffer update");
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;
@ -2817,39 +2819,47 @@ void VulkanRenderer::drawText(VkCommandBuffer commandBuffer, int frameCount,
} }
} }
// Cleanup old buffers if they exist (only when rebuilding) // 首次创建 text buffers使用 host-visible 内存)
if (m_textVertexBuffer != VK_NULL_HANDLE) { if (m_textVertexBuffer == VK_NULL_HANDLE) {
vkDestroyBuffer(m_device, m_textVertexBuffer, nullptr); VkDeviceSize textVertexSize = sizeof(Vertex) * 4096; // 预分配足够空间
vkFreeMemory(m_device, m_textVertexMemory, nullptr); VkDeviceSize textIndexSize = sizeof(uint16_t) * 6144;
m_textVertexBuffer = VK_NULL_HANDLE;
m_textVertexMemory = VK_NULL_HANDLE; if (!createDynamicVertexBuffer(textVertexSize, m_textVertexBuffer, m_textVertexMemory)) {
} logError("Failed to create dynamic text vertex buffer");
if (m_textIndexBuffer != VK_NULL_HANDLE) { m_lastRenderedText.clear();
vkDestroyBuffer(m_device, m_textIndexBuffer, nullptr); return;
vkFreeMemory(m_device, m_textIndexMemory, nullptr); }
m_textIndexBuffer = VK_NULL_HANDLE; if (!createDynamicIndexBuffer(textIndexSize, m_textIndexBuffer, m_textIndexMemory)) {
m_textIndexMemory = VK_NULL_HANDLE; 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)) { // 直接更新 buffer 内容(通过内存映射,无需命令队列)
logError("Failed to create text vertex buffer - skipping text rendering"); if (!vertices.empty() && !indices.empty()) {
m_lastRenderedText.clear(); // 标记为无效,下次重试 if (!updateDynamicBuffer(m_textVertexMemory, vertices.data(),
return; 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 // Draw text with newly created buffers
VkBuffer vertexBuffers[] = {m_textVertexBuffer}; VkBuffer vertexBuffers[] = {m_textVertexBuffer};
VkDeviceSize offsets[] = {0}; VkDeviceSize offsets[] = {0};

View File

@ -228,6 +228,21 @@ private:
*/ */
bool copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, uint64_t size); 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 * @brief
*/ */