温馨提示:本文翻译自stackoverflow.com,查看原文请点击:c++ - Do I need to transfer ownership *back* to the transfer queue on next transfer?
c++ gpu synchronization vulkan

c++ - 下次转让时,我是否需要将所有权*返还*到转让队列中?

发布于 2020-05-08 12:04:00

我计划使用一个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

然后,我是否必须放弃传输队列再次复制该数据的功能这些示例似乎都没有提到它,但是我可能会错过它。我真的看不到如何为相同的绘制命令缓冲区添加另一个缓冲区屏障,因为即使我没有什么要转移的东西,它也会在下一次提交时停顿,所以我只需要提交另一个命令缓冲区即可在我提交下一个转移操作之前将所有权转移排队?

//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)

如果是这种情况,我不确定如何处理绘制和传输以及复制和绘制之间的信号量信号。刚开始时很容易,但是由于多个nflight框架而变得奇怪,因为绘制提交之间没有依赖关系。基本上,我想我需要设置我提交的最新绘图命令中的任何一个,以使用信号量来表示所有权的转移,这将表示副本,这将表示图形的所有权,以及是否在单独的线程上然后,我将检查此副本是否已提交,并且需要等待图形传输的所有权并重置提交的副本支票。但是我不确定下一帧没有这种依赖性,会发生什么,并且可以先于时间顺序在上一帧之前完成吗?

查看更多

提问者
whn
被浏览
20
krOoze 2020-02-20 09:26

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

旧规范:

注意

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

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

至于同步(这是与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缓冲)会更好。如果您有更多的缓冲区,则可以开始复制到一个缓冲区而不必担心它已经被其他对象使用。这意味着复制可以与平局并行发生,这将是非常好的。