Vulkan:如何在单独的线程中记录命令缓冲区?

时间:2018-02-05 03:24:27

标签: c++ vulkan

我没有正确理解如何在Vulkan中的单独线程上并行化工作。

要开始发布vkCmd * s,您需要开始渲染通道。开始渲染传递的调用需要对帧缓冲区的引用。但是,不保证vkAcquireNextImageKHR()以循环方式返回图像索引。因此,在三缓冲设置中,如果当前图像索引为0,我不能只绑定帧缓冲区1并开始为下一帧发出绘制调用,因为下一次调用vkAcquireNextImageKHR()可能会返回图像索引2。 / p>

记录命令的正确方法是什么,而不必提前指定帧缓冲区?

4 个答案:

答案 0 :(得分:5)

您可以使用多个线程使用辅助命令缓冲区为同一个renderpass生成绘制命令。并且您可以在同一帧中并行生成不同渲染通道的工作 - 只有最后一遍(通常是后处理通过)取决于特定的交换链图像,所有阴影通道,gbuffer /阴影/光照通道,以及除了最后一个后处理传递没有。这不是必需的,但是在你已经准备好开始生成最终的渲染通道之后,甚至在你已经生成了许多先前的通道之后,甚至不调用vkAcquireNextImageKHR通常是个好主意。

答案 1 :(得分:4)

您有一个或多个想要按帧执行的渲染过程。每个人都有一个或多个子通道,您想要将其投入工作。因此,您的主渲染线程将为这些子通道生成一个或多个辅助命令缓冲区,并将该辅助CB序列传递给提交线程。

提交线程将创建渲染的主CB。它开始/结束渲染过程,并进入每个子过程,它执行在该特定子过程的渲染线程上创建的辅助CB。

因此每个线程都在创建自己的命令缓冲区。提交线程是处理VkFramebuffer对象的线程,因为它开始渲染传递。它也是获取交换链图像的那个,等等。渲染线程是使辅助CB完成所有实际工作的线程。

是的,你仍然会在提交帖子上做一些CB建设,但它总体来说应该是非常简约的。这也用于从渲染线程中抽象出渲染目标的细节,以便处理交换链的代码可以本地化到提交线程。这为您提供了更大的灵活性。

例如,如果你想三重缓冲,并没有按swapchain'吨实际上允许,那么你的提交线程可以创建自己的额外的图像,然后从它的内部的图像复制到真正的swapchain。渲染线程的代码根本不必受到干扰而允许这样做。

答案 2 :(得分:2)

您可以在任何线程中调用vkAcquireNextImageKHR。只要您确保访问交换链,您传递给它的信号量和围栏是同步的。

没有任何限制你在任何线程中调用它,包括录制线程。

您也可以一次获取多张图像。假设你已经创造了足够的。换句话说,允许在呈现当前图像之前获取下一个图像。

答案 3 :(得分:1)

首先,要明确:

  

要开始发布vkCmd * s,您需要开始渲染传递。

这不一定是真的。在命令缓冲区中您可以记录多个不同的命令,所有命令都以vkCmd开头。这些命令中只有一些需要记录在渲染过程中 - 与绘图连接的那些。有些命令无法在渲染过程中调用(例如调度计算着色器)。但这只是一个解决问题的旁注。

接下来的事情 - 提到三重缓冲。在Vulkan中,图像的显示方式取决于支持的当前模式。不同的硬件供应商,甚至不同的驱动程序版本,可能会提供不同的现有模式,因此在一个硬件上您可能会获得与三重缓冲(MAILBOX)最相似的现有模式,但在其他模式上您可能无法获得它。当前模式会影响演示引擎允许您从交换链中获取图像的方式,然后在屏幕上显示它们。但是正如您所指出的,您不能依赖于返回图像的顺序,因此您不应该将您的应用程序设计为在所有平台上始终具有相同的行为。

但要回答你的问题 - 最简单,最天真的方法是在帧的开头调用vkAcquireNextImageKHR(),记录使用它返回的图像的命令缓冲区,提交这些命令缓冲区并显示图像。您可以在需要在命令缓冲区内使用之前根据需要创建帧缓冲区:创建一个使用适当图像的帧缓冲区(与vkAcquireNextImageKHR()函数返回的索引关联的帧缓冲区)并在提交命令缓冲区之后当他们停止使用它时,你会摧毁它。 Vulkan Cookbook中提供了这种行为:herehere

更合适的方法是为所有可用的交换链图像准备帧缓冲区,并在帧期间采用适当的帧缓冲区。但是,当您重新创建交换链时,您需要记住重新创建它们。

更高级的方案会推迟交换链获取,直到确实需要它为止。 vkAcquireNextImageKHR()函数调用可能会阻止您的应用程序(等到图像可用),因此在准备帧时应尽可能晚地调用它。这就是为什么你应该首先记录不需要引用交换链图像的命令缓冲区(例如那些在延迟着色算法中将几何体渲染到G缓冲区中的命令缓冲区)。之后当你想在屏幕上显示图像时(比如一些后期处理技术)你只需采用上面描述的方法:获取图像,准备适当的命令缓冲区并呈现图像。

您还可以预先录制引用特定交换链图像的命令缓冲区。如果您知道图像的来源将始终相同(如上面提到的G缓冲区),您可以拥有一组命令缓冲区,始终执行从此数据到所有交换链图像的一些后处理/类似副本的操作 - 一个每个交换链图像的命令缓冲区。然后,在帧期间,如果设置了所有数据,则获取图像,检查哪个预先录制的命令缓冲区是合适的,并提交与获取的图像相关联的图像。

有多种方法可以实现您的需求,所有这些方法都取决于许多因素 - 性能,平台,您希望实现的特定目标,您在应用程序中执行的操作类型,同步机制您实现的以及许多其他方面。你需要找出最适合你的东西。但最后 - 如果要在屏幕上显示图像,则需要在命令缓冲区中引用交换链图像。我建议首先从最简单的选项开始,然后,当您习惯它时,您可以改进您的实现,以获得更高的性能,灵活性,更轻松的代码维护等。