c++ - 使用哪种方式来同步 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, ©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);
}
这些选项中哪个更好?
第二个选项写对了吗?
解决方案
这两个功能都以同样的方式不好。它们都阻止 CPU 做任何事情,直到传输完成。它们都可以用于潜在地将多个 CB 提交到同一帧中的同一队列,但使用不同的提交命令。
如果性能是您关心的事情,那么两者都不是可取的。
最终,您需要做的是让您的copyBuffer
功能不实际执行复制。你应该有一个函数来构建一个命令缓冲区来进行复制。然后将该 CB 存储在稍后与其他复制 CB一起提交的位置。或者更好的是,您可以只有一个复制 CB,每个命令添加到其中(帧中调用的第一个将创建 CB)。
在将来的某个时候,在您提交将使用此数据的工作之前,您需要提交传输操作。其工作方式取决于您是否在与将使用它们的工作相同的队列上提交传输操作。
如果它们在同一个队列中,那么您需要做的就是在批处理末尾的命令缓冲区中设置一个事件,以将传输操作与其接收器同步。如果您想更聪明一点,每个传输操作都可以有自己的事件,接收操作将等待该事件。
在同队列传输中,您还需要确保在与其他工作相同的vkQueueSubmit
调用中提交传输。或者换一种说法,你永远不应该对特定帧中的特定队列进行多次调用。vkQueueSubmit
如果您正在处理单独的队列,那么情况就会改变。一点点。如果时间线信号量不是一个选项,您需要在提交接收操作之前提交您的传输工作。这是因为传输批处理需要发出信号量,接收操作将等待。并且不能等待二进制信号量,直到发出信号的操作已提交到队列。
但除此之外,其他一切都保持不变。当然,您不需要事件,因为您是通过信号量进行同步的。
推荐阅读
- javascript - webpack 无法解析 d.ts 文件
- wordpress - 重定向规则在 wp_redirect 函数中不起作用
- android - 创建新应用并在生成签名包方面需要帮助
- r-exams - 单个 R/考试练习中的不同类型的任务
- tailwind-css - Sveltekit Tailwind 配置变量不起作用
- python - TypeError: __array__() 采用 1 个位置参数,但在 Windows 10 的 venv 中安装 Tensorflow 2.5 后给出了 2 个
- sqlite - 如何在 intellij J 2021 的 grails 3.3.9 中连接 Sqlite 数据源
- windows - 导入的哪些变体允许 exe 连接到 Internet?
- javascript - 为什么 promise 的结果在执行后仍然是 `undefined`?
- javascript - 如何获取数组中具有相同值的所有项目?