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

C++ 我是否需要在下次转移时将所有权*转移回*转移队列?,c++,synchronization,gpu,vulkan,C++,Synchronization,Gpu,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

我计划使用其中一个作为如何处理不经常更新的统一缓冲区的参考。具体地说,我在看这个:

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命令缓冲区起作用,因为即使我没有任何东西要传输,它也会在下次提交时暂停,所以在提交下一次传输操作之前,我是否只需要提交另一个命令缓冲区以进行队列所有权传输


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

只要不介意数据未定义,就可以在任何队列族上使用资源(无需传输)。您仍然需要一个信号量来确保没有内存危险

Ye olde规格:

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

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

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

然后通过副本向信号量发送信号,并在提交的下一张图纸上等待信号量。这意味着在绘图(以及任何后续绘图)读取图形队列中的副本之前,副本已完成写入

e、 g:


尽管注意到上述方法实际上序列化了这两种访问,但它可能是次优的。采用双缓冲(或通常为N缓冲)可能更好。如果你有更多的缓冲区,你可以开始复制到一个缓冲区,而不用担心它已经被其他东西使用了。这意味着复制可以与绘制并行进行,这将非常好。

Ahhh,因此,由于我不需要在绘制和复制之间保持有效的资源,我可以跳过尝试将所有权转移到传输队列中的副本,但所有权是否需要在之后转移?在我的场景中,它是否仍然需要在绘图功能中定义(假设它是一些数据,如对象的颜色参数,仅从用户端定期设置,不经常更新)雅皮蒂是的。您需要从副本到使用该副本结果的绘图执行QFOT(即,在传输qf上执行“释放”屏障,然后执行信号量,然后在图形qf上匹配“获取”屏障)。抽签后,您只能(而且应该)将缓冲区“偷”回传输qf。这是通过信号量将两个访问分开来实现的。(然后数据有未定义的内容,但您不在乎是否仍要覆盖它们。但信号灯仍必须在那里,因此绘图在副本尝试写入缓冲区之前完成读取。)如果飞行中有多个绘图,该如何工作?传输副本可以在另一个已经提交的绘图已经运行的情况下完成,尽管该副本和下一个绘图命令之间有信号量,但这不会导致问题吗?@whn您的应用程序应该使用信号量以一种不会发生内存危险的方式对事情进行良好排序。如果您将内容复制到缓冲区,同时某个绘图可以读取它,则这是不同步的。您需要使用sync序列化这些访问,或者需要使用一些N缓冲。同步两个不同队列的唯一方法是使用信号灯(或者更残酷地使用围栏)。@plasmacel Yes。“窃取”只是省略了两个QFOT屏障,这导致数据在新队列中未定义。然后在新队列上,您只需开始使用资源;i、 e.在您的情况下,您可以使用常规屏障将布局从
VK\u IMAGE\u layout\u UNDEFINED
更改为您想要的任何布局。
//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)
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