Vulkan:如何安全地替换 commandBuffer

Vulkan: how to safely replace a commandBuffer

当我的场景更新时(例如引入新模型),我使用函数 buildCommandBuffers.

将我的绘图命令缓冲区重新记录到新的命令缓冲区 (tempBuffer) 中

我有一个 drawCmdBuffers 的数组,用于主要绘制例程。

使事情稍微复杂化的是,我的 buildCommandBuffers 函数可能会在单独的线程中从计时器调用,因此渲染函数可能仍在循环中进行此更新。这是因为我的场景更新可能需要一段时间,我需要在创建命令缓冲区时锁定场景内存(这是一个单独的问题..但是添加的每个网格都会触发场景更新,我需要能够缓冲这些更新).

我对每个命令缓冲区都有一个栅栏,所以一旦我记录到我的“tempBuffer”中,我就会执行以下操作:

        VK_CHECK_RESULT(vkEndCommandBuffer(tempBuffers[i]));        
        // wait for this command buffer to finish (we might be re-recording)
        if (imagesInFlight[_image_index] != VK_NULL_HANDLE) 
        {
            vkWaitForFences(device, 1, &imagesInFlight[_image_index], VK_TRUE, UINT64_MAX);
        }
        // destroy in use command buffer
        
        vkFreeCommandBuffers(device, cmdPool, 1, &drawCmdBuffers[i]);
        drawCmdBuffers[i] = tempBuffers[i];

这里安全吗?或者我可以做些什么来在更新时停止访问 drawCmdBuffers[i]

看起来您需要 CPU-CPU 同步(通常是互斥锁)来访问 imagesInFlight[] 数组和 drawCommandBuffers[] 或其中的各个元素.否则,您可能正在等待最近一帧的围栏,而另一个线程正在为下一帧提交命令缓冲区——在这种情况下,即使您的等待完成,GPU 仍将使用命令缓冲区。

假设您确实需要重用命令缓冲区而不是每帧重新记录它们,一种方法是让 buildCommandBuffers 线程只构建一个 drawCmdBuffers 的更改列表。类似于:

渲染线程:

for each frame:
  acquire mutex for command buffer edit list
  for (i, cmdbuf) in command buffer edit list:
    push drawCmdBuffers[i] and most recent frame fence to a deferred-destroy queue
    drawCmdBuffers[i] = cmdbuf;
  clear edit list
  release mutex
  submit render commands for frame, using drawCmdBuffers

更新线程:

on_timer:
  acquire mutex for command buffer edit list
  while fence at front of deferred-destroy queue has signaled:
    free command buffer at front of queue
    pop queue
  release mutex
  for each command buffer that needs to be replaced:
    record command buffer
    acquire mutex for command buffer edit list
    append (cmdbuf_index, cmdbuf) to edit list
    release mutex

这样只有渲染线程直接访问drawCmdBuffers,所以不需要保护。您只有一个从计时器线程到渲染线程的编辑队列,以及一个命令缓冲区队列,可以从渲染线程释放到计时器线程(或其他一些实际上在栅栏上等待的线程,而不仅仅是轮询他们在每个计时器上)。在任何给定时刻(除了持有互斥锁的短暂时间),命令缓冲区句柄仅在这些列表之一中。