使用哪种方式同步 vkQueueSubmit()?

问题描述


我有一个函数可以将数据从一个缓冲区复制到另一个缓冲区,我需要同步它的执行。
我有一个糟糕的选择:
void MainWindow::copyBuffer(VkBuffer srcBuffer,VkBuffer dstBuffer,VkDeviceSize size)
{
    VkCommandBuffer commandBuffer;
    vkAllocateCommandBuffers(logicalDevice,&alLocinfo,&commandBuffer);

    //Start recording
    vkBeginCommandBuffer(commandBuffer,&beginInfo);
    vkCmdcopyBuffer(commandBuffer,srcBuffer,dstBuffer,1,&copyRegion);
    vkEndCommandBuffer(commandBuffer);

    //Run command buffer
    vkQueueSubmit(graphicsQueue,&submitInfo,VK_NULL_HANDLE);
    //Waiting for completion
    vkQueueWaitIdle(graphicsQueue);

    vkFreeCommandBuffers(logicalDevice,commandPool,&commandBuffer);
}

这个选项不好,因为如果我想多次执行 copyBuffer() 函数,那么所有缓冲区将严格一次复制一个

我想为每个函数调用使用一个栅栏,以便多个调用可以并行运行。

到目前为止,我只有这样一个解决方案:

void MainWindow::copyBuffer(VkBuffer srcBuffer,&commandBuffer);
    
    //Create fence
    VkFenceCreateInfo fenceInfo{};
    fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
    fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;

    VkFence executionCompleteFence = VK_NULL_HANDLE;
    if (vkCreateFence(logicalDevice,&fenceInfo,VK_NULL_HANDLE,&executionCompleteFence) != VK_SUCCESS) {
        throw MakeErrorInfo("Failed to create fence");
    }

    //Start recording
    vkBeginCommandBuffer(commandBuffer,&copyRegion);

    vkEndCommandBuffer(commandBuffer);

    //Run command buffer
    vkQueueSubmit(graphicsQueue,VK_NULL_HANDLE);
    
    vkWaitForFences(logicalDevice,&executionCompleteFence,VK_TRUE,UINT64_MAX);
    vkResetFences(logicalDevice,&executionCompleteFence);

    vkFreeCommandBuffers(logicalDevice,&commandBuffer);
    vkDestroyFence(logicalDevice,executionCompleteFence,VK_NULL_HANDLE);
}

这些选项中哪个更好?
第二个选项写对了吗?

解决方法

这两个函数的坏处是一样的。它们都阻止 CPU 执行任何操作,直到传输完成。并且它们都可以用于潜在地将多个 CB 提交到同一帧中的同一个队列,但使用不同的提交命令。

如果您关心性能,这两种方法都不理想。

最终,您需要做的是让您的 copyBuffer 函数不实际执行复制。您应该有一个函数来构建命令缓冲区来进行复制。然后将该 CB 存储在一个位置,以便稍后与 其他 复制 CB 一起提交。或者更好的是,您可以只有一个复制 CB,每个命​​令添加到其中(帧中调用的第一个将创建 CB)。

在未来的某个时候,在您提交将使用此数据的工作之前,您需要提交传输操作。其工作方式取决于您是否在与将消耗它们的工作相同的队列中提交传输操作。

如果它们在同一个队列中,那么您需要做的就是在批处理结束时在命令缓冲区中有一个事件,该事件将传输操作与其接收器同步。如果你想更聪明,每个传输操作都可以有自己的事件,接收操作将等待。

在同队列转移中,您还希望确保在与您的其余工作相同的 vkQueueSubmit 调用中提交转移。或者换句话说,对于特定帧中的特定队列,您永远不应多次调用 vkQueueSubmit

如果您要处理单独的队列,则情况会发生变化。一点点。如果时间线信号量不是一种选择,则您需要在提交接收操作之前提交传输工作。这是因为传输批处理将需要向信号量发送接收操作将等待的信号。并且二进制信号量不能等待,直到发出信号它已提交到队列的操作。

但除此之外,其他一切都保持不变。当然,您不需要事件,因为您是通过信号量同步的。

,

这两个函数在语义上是相同的,并且执行完全相同的阻塞行为。

第二个稍微好一点。 vkQueueWaitIdle 是一种调试和热点外功能。它可能会导致隐藏的第二次提交以向隐式栅栏发出信号。

你不需要重置你随后摧毁的围栏。而且您正在创建它预先发出信号,这是一个错误。您也忘记将其传递给 vkQueueSubmit