我是否需要在下一次转移时将所有权*转移回转移队列

Do I need to transfer ownership *back* to the transfer queue on next transfer?

本文关键字:转移 一次 所有权 队列 是否      更新时间:2023-10-16

我计划使用其中一个vulkan同步示例作为如何处理不频繁更新的统一缓冲区的参考。具体来说,我看到了这个:

vkBeginCommandBuffer(...);
// Submission guarantees the host write being complete, as per
// https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#synchronization-submission-host-writes
// So no need for a barrier before the transfer
// Copy the staging buffer contents to the vertex buffer
VkBufferCopy vertexCopyRegion = {
.srcOffset = stagingMemoryOffset,
.dstOffset = vertexMemoryOffset,
.size      = vertexDataSize};
vkCmdCopyBuffer(
commandBuffer,
stagingBuffer,
vertexBuffer,
1,
&vertexCopyRegion);

// If the graphics queue and transfer queue are the same queue
if (isUnifiedGraphicsAndTransferQueue)
{
// If there is a semaphore signal + wait between this being submitted and
// the vertex buffer being used, then skip this pipeline barrier.
// Pipeline barrier before using the vertex data
// Note that this can apply to all buffers uploaded in the same way, so
// ideally batch all copies before this.
VkMemoryBarrier memoryBarrier = {
...
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,                           
.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT};
vkCmdPipelineBarrier(
...
VK_PIPELINE_STAGE_TRANSFER_BIT ,      // srcStageMask
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,   // dstStageMask
1,                                    // memoryBarrierCount
&memoryBarrier,                       // pMemoryBarriers
...);

vkEndCommandBuffer(...);
vkQueueSubmit(unifiedQueue, ...);
}
else
{
// Pipeline barrier to start a queue ownership transfer after the copy
VkBufferMemoryBarrier bufferMemoryBarrier = {
...
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,                           
.dstAccessMask = 0,
.srcQueueFamilyIndex = transferQueueFamilyIndex,
.dstQueueFamilyIndex = graphicsQueueFamilyIndex,
.buffer = vertexBuffer,
...};
vkCmdPipelineBarrier(
...
VK_PIPELINE_STAGE_TRANSFER_BIT ,      // srcStageMask
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, // dstStageMask
1,                                    // bufferMemoryBarrierCount
&bufferMemoryBarrier,                 // pBufferMemoryBarriers
...);

vkEndCommandBuffer(...);
// Ensure a semaphore is signalled here which will be waited on by the graphics queue.
vkQueueSubmit(transferQueue, ...);
// Record a command buffer for the graphics queue.
vkBeginCommandBuffer(...);
// Pipeline barrier before using the vertex buffer, after finalising the ownership transfer
VkBufferMemoryBarrier bufferMemoryBarrier = {
...
.srcAccessMask = 0,                           
.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT,
.srcQueueFamilyIndex = transferQueueFamilyIndex,
.dstQueueFamilyIndex = graphicsQueueFamilyIndex,
.buffer = vertexBuffer,
...};
vkCmdPipelineBarrier(
...
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,    // srcStageMask
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT,   // dstStageMask
...
1,                                    // bufferMemoryBarrierCount
&bufferMemoryBarrier,                 // pBufferMemoryBarriers
...);

vkEndCommandBuffer(...);
vkQueueSubmit(graphicsQueue, ...);
}

在这个例子中,我将其简化为:

map updated buffer which is host coherent
perform transfer in transfer queue to device local memory
make sure to put a buffer memory barrier to handle the queue ownership transfer
perform normal draw commands
make sure to put a buffer memory barrier to handle receiving of buffer in queue ownership

然后我是否必须恢复传输队列再次复制该数据的能力?似乎没有一个例子提到它,但我可能错过了它。我真的看不出添加另一个缓冲区屏障对同一个draw命令缓冲区会如何工作,因为即使我没有任何东西要传输,它也会在下一次提交时停滞,所以在提交下一次传输操作之前,我只需要提交另一个命令缓冲区来进行队列所有权传输吗?

//begin with transfer ownership
submit(copy)
submit(ownership to graphics)
submit(draw)
submit(ownership to transfer)
submit(copy)
submit(ownership to graphics)
submit(draw)
submit(draw)
submit(draw)
submit(ownership to transfer)
submit(copy)
submit(ownership to graphics)
submit(draw)

如果是这样的话,我不确定如何处理draw和transfer以及copy和draw之间的信号量信号。一开始这很容易,但后来因为多个帧而变得奇怪,因为绘制提交之间没有依赖关系。基本上,我想我需要设置我提交的最新绘图命令,以便有一个信号量来表示所有权的转移,这将表示副本,这将表明图形的所有权,如果它在一个单独的线程上,我将检查这个副本是否已提交,并且需要等待图形传输的所有权并重新设置提交副本的检查。但我不确定下一帧会发生什么,因为下一帧没有这种依赖性,并且可能在上一帧之前完成?

您可以在任何队列族上使用Resource(无需传输(,只要您不介意数据变得未定义即可。你仍然需要一个信号灯来确保没有记忆障碍。

旧规格:

注意

如果应用程序在从一个队列系列转移到另一个队列家族时不需要资源的内容保持有效,则应跳过所有权转移。

示例没有提到它,因为它们只是示例。

至于同步(这是与QFOT分开的问题(,作为vkQueueSubmit一部分的信号量信号覆盖了以前提交顺序中的所有内容。因此,当你提交副本时,你会让它在信号灯上等待,最后一次提交的抽奖已经发出信号。这意味着绘制和该队列上的任何先前绘制都已完成,然后才能在另一个队列上开始复制。

然后,您通过副本发出信号量,并在提交的第一次下一次绘制时等待它。这意味着在绘图(以及任何后续绘图(在图形队列上读取之前,副本已完成写入。

例如:

submit(copy, release ownership from tranfer, semaphore signal)
submit(semaphore wait, acquire ownership to graphics, draw)
submit(draw)
submit(draw)
submit(draw)
submit(draw)
submit(draw, semaphore signal)
submit(semaphore wait, copy, release ownership from tranfer, semaphore signal)
submit(semaphore wait, acquire ownership to graphics, draw)
submit(draw)
submit(draw)
etc

尽管需要注意的是,上面的方法实际上序列化了这两种访问,因此它可能是次优的。采用双缓冲(或通常为N缓冲(可能会更好。如果你有更多的缓冲区,你可以开始复制到一个缓冲区,而不用担心它已经被其他人使用了。这意味着一个副本可以在并行的情况下进行绘制,这将是非常棒的。