Vulkan 命令执行
与 OpenGL 或 DirectX pre-11 不同,在 Vulkan 中,所有 GPU 命令都必须通过命令缓冲区。命令缓冲区从命令池分配,并在队列上执行。
执行命令的一般流程是
- 您从
VkCommandPool
分配一个VkCommandBuffer
- 您使用
VkCmdXXXXX
函数将命令记录到命令缓冲区中。 - 您将命令缓冲区提交到
VkQueue
,这将开始执行命令。
可以多次提交相同的命令缓冲区。在教程和示例中,通常的做法是记录一次命令,然后在渲染循环中每帧提交它们。在本教程中,我们将每帧记录命令,因为它更贴近真实渲染引擎的工作方式。
在 Vulkan 中记录命令相对廉价。大部分工作都在 vkQueueSubmit
调用中完成,其中命令在驱动程序中进行验证并转换为真实的 GPU 命令。
命令缓冲区非常重要的一部分是它们可以并行记录。您可以从不同的线程安全地记录不同的命令缓冲区。为此,您需要每个线程 1 个 VkCommandPool
和 1 个 VkCommandBuffer
(最低限度),并确保每个线程仅使用其自己的命令缓冲区和命令池。完成此操作后,可以在其中一个线程中提交命令缓冲区。vkQueueSubmit
不是线程安全的,一次只能有一个线程推送命令。
VkQueue
Vulkan 中的队列是 GPU 的“执行端口”。每个 GPU 都有多个可用的队列,您甚至可以同时使用它们来执行不同的命令流。提交到不同队列的命令可能会同时执行。如果您正在执行与主帧循环不太相关的后台工作,这将非常有用。您可以专门为所述后台工作创建一个 VkQueue
,并使其与正常渲染分离。
Vulkan 中的所有队列都来自队列族。队列族是队列的“类型”,以及它支持的命令类型。本指南中我们需要的是一个同时支持图形和呈现的队列。这意味着同一个队列将用于渲染和将图像显示到屏幕上。
不同的 GPU 支持不同的队列族。例如,来自 Vulkan Hardware Info 的 NVidia GT 750ti https://vulkan.gpuinfo.org/displayreport.php?id=8859#queuefamilies。它有 2 个队列族,一个支持最多 16 个具有所有功能的队列,然后一个族可以支持 1 个仅用于传输的队列。
Vulkan 中的仅传输队列族非常有趣,因为它们用于执行后台资源上传,并且通常可以与渲染完全异步运行。因此,如果您想从后台线程将资源上传到 GPU,则使用仅传输队列将是一个不错的选择。
VkCommandPool
VkCommandPool
是从 VkDevice
创建的,您需要此命令池将从中创建命令的队列族的索引。
将 VkCommandPool
视为 VkCommandBuffer
的后台分配器。您可以从给定的池中分配任意数量的 VkCommandBuffer
,但一次只能从一个线程记录命令。如果您想要多线程命令记录,则需要更多 VkCommandPool
对象。
命令通常会少量分配,然后在每次重置。为此的快速路径是重置整个命令池,这将重置从中分配的所有命令缓冲区。也可以直接重置它们,但是如果您从一个池中创建多个命令缓冲区,则重置池会更快。
VkCommandBuffer
GPU 的所有命令都记录在 VkCommandBuffer 中。所有将执行 GPU 工作的函数在命令缓冲区提交到 GPU 之前都不会执行任何操作。
命令缓冲区以就绪状态启动。在就绪状态下,您可以调用 vkBeginCommandBuffer()
将其置于记录状态。现在,您可以开始使用 vkCmdXXXXX
函数向其中输入命令。完成后,调用 vkEndCommandBuffer()
以完成记录命令并将其置于可执行状态,在该状态下,它可以提交到 GPU。
要提交命令缓冲区,您可以调用 vkQueueSubmit()
,同时使用命令和要提交到的队列。vkQueueSubmit
也接受一起提交多个命令缓冲区。任何提交的命令缓冲区都将置于挂起状态。
提交命令缓冲区后,它仍然“存活”,并且正在被 GPU 消耗,此时重置命令缓冲区尚不安全。您需要确保 GPU 已完成执行该命令缓冲区中的所有命令,然后才能重置并重新使用它
要重置命令缓冲区,请使用 vkResetCommandBuffer()
。
有关命令缓冲区生命周期的更多详细信息,请参阅 Vulkan 规范中有关它们的章节 https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap6.html#commandbuffers-lifecycle。
下一步:Vulkan 命令代码