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:
parent
fdb25ed816
commit
9786baed86
|
|
@ -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<Vertex> circleVertices, waveVertices;
|
||||
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);
|
||||
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<uint16_t>& 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};
|
||||
|
|
|
|||
|
|
@ -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 查找内存类型
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue