Vulkan创建一个命令缓冲区

创建一个命令缓冲区

本节的代码是 04-init_command_buffer.cpp

基本的命令缓冲区操作

在其他图形API中,应用程序可以通过像glLineWidth()这样的API调用来设置诸如线宽之类的属性。在后台,驱动程序将这个API调用转换为特定于gpu特定的命令,并将该命令放入一个命令缓冲区中。出于应用程序的考虑,驱动程序也通过创建和销毁命令缓冲区来管理命令缓冲区。最终,驱动程序“向GPU提交”命令缓冲区来处理这些命令。

在Vulkan中,你创建一个命令缓冲区,并创建一个类似的Vulkan API调用vkCmdSetLineWidth()来向命令缓冲区添加一个命令。由于每个GPU都有自己的“指令集”,所以驱动程序仍然需要做一些工作来生成特定于GPU特异性的指令来设置线宽。

这里写图片描述

驱动程序确定适当的二进制GPU指令插入到命令缓冲区中,以指示GPU使用线宽5来绘制后面的线。您不需要看到实际的命令缓冲区内容,因为驱动程序正在为您做这部分工作。

命令缓冲池

下一步是学习如何获得一个命令缓冲区。先看下 04-init_command_buffer.cpp

因为创建和销毁单独的命令缓冲区的代价比较昂贵,所以Vulkan使用命令传冲池来管理命令缓冲。使用命令缓冲池的动机包括:

  1. 一些应用程序使用短生命周期的命令缓冲区,这意味着它们经常被创建和销毁。专业的池分配器通常可以更有效地处理这些分配模式。

  2. 命令缓冲区内存是特别的,因为它必须对CPU和GPU都可见。在许多系统中,内存到处理器(CPU或GPU)的映射只能使用大型的粒度来完成,这意味着一个小型的命令缓冲区可能会浪费大量的内存。

  3. 内存映射很昂贵,因为它通常涉及修改页表和使TLB缓存失效。最好是映射一个更大的命令缓冲池,并在其中分配单个命令缓冲区,而不是分别映射每个命令缓冲区。

命令缓冲池和队列家族

驱动程序使用内存分配属性来分配一个命令缓冲池,这些属性适合于读取命令缓冲区内存的GPU硬件。这种属性的例子包括内存对齐需求和缓存行为。

扫描二维码关注公众号,回复: 2554224 查看本文章

如果在GPU硬件中有多个硬件队列,就像物理设备队列家族所描述的那样,那么驱动程序可能需要分配具有不同内存分配属性的命令缓冲池到每个具体的GPU硬件队列。这些细节由驱动程序为您处理,只需要它知道包含命令缓冲区将使用的硬件队列的家族。

This also means that a command buffer pool can be associated with only one queue family. This requirement shows up in the API design via the code needed to create a command buffer pool, where you specify the queue family index
这也意味着命令缓冲池只能与一个队列家族相关联。在创建一个命令缓冲池时,您需要指定队列家族索引:

VkCommandPoolCreateInfo cmd_pool_info = {};
cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
cmd_pool_info.pNext = NULL;
cmd_pool_info.queueFamilyIndex = info.graphics_queue_family_index;
cmd_pool_info.flags = 0;

res = vkCreateCommandPool(info.device, &cmd_pool_info, NULL, &info.cmd_pool);

注意,当您在上一节中创建设备时,您决定了要使用什么队列。实际上,您必须为应用程序打算使用的每个独特队列家族创建一个命令缓冲池。由于您在创建设备时只指定了一个队列家族,所以对于这些示例来说,一个命令缓冲池就足够了。

创建命令缓冲区

一旦创建了一个命令缓冲池,就可以轻松地从该池中分配一个命令缓冲区:

VkCommandBufferAllocateInfo cmd = {};
cmd.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmd.pNext = NULL;
cmd.commandPool = info.cmd_pool;
cmd.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
cmd.commandBufferCount = 1;

res = vkAllocateCommandBuffers(info.device, &cmd, &info.cmd);

注意,这个API调用的设计目的是为了让在一此调用中分配多个命令缓冲区变得容易。这对于需要许多命令缓冲区的应用程序是有用的。但这次的示例很简单,只需要一个。

使用命令缓冲区

一旦您创建了一个命令缓冲区,您就可以通过调用vkBeginCommandBuffer()来开始“记录”它。调用这个函数将命令缓冲区放入“录制”状态,并允许您调用许多“vkCmd”功能,将命令插入到命令缓冲区中。您已经在本节中看到了vkCmdSetLineWidth()示例。另一个例子是vkCmdDraw(),它告诉GPU绘制一些顶点。当您完成将命令插入到命令缓冲区中时,您将调用vkEndCommandBuffer() 来表明您已经完成了,并将命令缓冲区置为非记录状态且使它可用。

您将在后面的章节中看到填充命令缓冲区的代码。

完成命令缓冲区记录并不能使GPU做任何事情。为了让GPU处理一个命令缓冲区,您必须使用vkQueueSubmit()将其提交到GPU队列中。在向GPU提交一个命令缓冲区之前,还有很多事情要做,这将在本教程的最后一节中进行。

© Copyright 2016 LunarG, Inc

猜你喜欢

转载自blog.csdn.net/hccloud/article/details/81409413
今日推荐