Link

cmake

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 命令代码