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
,所以不需要保护。您只有一个从计时器线程到渲染线程的编辑队列,以及一个命令缓冲区队列,可以从渲染线程释放到计时器线程(或其他一些实际上在栅栏上等待的线程,而不仅仅是轮询他们在每个计时器上)。在任何给定时刻(除了持有互斥锁的短暂时间),命令缓冲区句柄仅在这些列表之一中。
当我的场景更新时(例如引入新模型),我使用函数 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
,所以不需要保护。您只有一个从计时器线程到渲染线程的编辑队列,以及一个命令缓冲区队列,可以从渲染线程释放到计时器线程(或其他一些实际上在栅栏上等待的线程,而不仅仅是轮询他们在每个计时器上)。在任何给定时刻(除了持有互斥锁的短暂时间),命令缓冲区句柄仅在这些列表之一中。