我有一个函数将数据从一个缓冲区复制到另一个缓冲区,我需要同步它的执行。
我有一个很糟糕的选择:
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, ©Region);
vkEndCommandBuffer(commandBuffer);
//Run command buffer
vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
//Waiting for completion
vkQueueWaitIdle(graphicsQueue);
vkFreeCommandBuffers(logicalDevice, commandPool, 1, &commandBuffer);
}
这个选项是不好的,因为如果我想多次执行copyBuffer()函数,那么所有的缓冲区都将严格地一次复制一次。
我想对每个函数调用使用一个栅栏,以便多个调用可以并行运行。
到目前为止,我只有这样一个解决办法:
void MainWindow::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
{
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(logicalDevice, &allocInfo, &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, &beginInfo);
vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region);
vkEndCommandBuffer(commandBuffer);
//Run command buffer
vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
vkWaitForFences(logicalDevice, 1, &executionCompleteFence, VK_TRUE, UINT64_MAX);
vkResetFences(logicalDevice, 1, &executionCompleteFence);
vkFreeCommandBuffers(logicalDevice, commandPool, 1, &commandBuffer);
vkDestroyFence(logicalDevice, executionCompleteFence, VK_NULL_HANDLE);
}
这些选项中哪一种更好?
第二个选项写得正确吗?
发布于 2021-06-02 00:59:04
这两个函数在相同的方面都是坏的。在传输完成之前,它们都阻止CPU执行任何操作。而且,它们都可以用于向同一帧中的同一个队列提交多个CBs,但提交命令不同。
如果性能是你关心的事情,这两种情况都是不可取的。
最终,您需要做的是让您的copyBuffer
函数不实际执行副本。您应该有一个函数,它构建一个命令缓冲区来执行副本。然后将该CB存储在稍后与其他复制CBs一起提交的地方。或者更好的是,您可以只有一个复制CB,每个命令都添加到其中(框架中的第一个调用将创建CB)。
在将来的某个时候,在提交将使用这些数据的工作之前,您需要提交传输操作。它的工作方式取决于您是否在同一队列上提交传输操作,以及是否使用它们。
如果它们在同一个队列上,那么您所需要做的就是在批处理结束时在命令缓冲区中有一个事件,该事件使传输操作与接收方同步。如果您想变得更聪明,每个传输操作都可以有自己的事件,接收操作将等待该事件。
在相同队列的传输中,您还希望确保在与其他工作相同的vkQueueSubmit
调用中提交传输。或者换一种说法,对于特定帧中的特定队列,您不应该不止一次地调用vkQueueSubmit
。
如果您处理的是不同的队列,那么事情就会发生变化。有一点。如果时间线信号量不是一个选项,您需要在提交接收操作之前提交您的传输工作。这是因为传输批处理需要向接收操作等待的信号量发送信号。并且二进制信号量不能等到发出信号的操作提交到队列。
但除此之外,其他一切都保持不变。当然,您不需要事件,因为您正在通过信号量进行同步。
发布于 2021-06-02 00:11:33
这两个函数在语义上是相同的,并且执行完全相同的阻塞行为。
第二种情况略好一些。vkQueueWaitIdle
是一种调试和脱离热点的特性.它可能会招致隐藏的第二次提交,以向隐式围栏发出信号。
你不需要重置栅栏,你随后摧毁无论如何。您正在创建预信号,这是一个bug。你还忘了把它传递给vkQueueSubmit
。
https://stackoverflow.com/questions/67796344
复制相似问题