Vulkan Tutorial 4 创建命令缓冲区

本文深入探讨Vulkan中命令缓冲区的操作与管理,包括如何使用命令缓冲池优化资源分配,以及创建和使用命令缓冲区的具体步骤。文章还介绍了命令缓冲区在不同GPU硬件上的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基本命令缓冲区操作

OpenGL里面设置线宽的时候,是通过调用glLineWidth()。如下图所示,驱动程序将这个API调用转换为特定于GPU的指令,并且放入命令缓冲区。驱动程序负责管理这个缓冲区的创建和销毁,并且负责将命令缓冲发送给GPU去处理缓冲里面指令,应用程序不需要关心这些操作。

在Vulkan里面,调用vkCmdSetLineWidth()来设置线宽。此函数实际上是新增一个指令到命令缓冲区。因为不同的GPU都有特定的指令集,因此,驱动程序需要将这些指令翻译为GPU可识别的指令。

 

命令缓冲池(Command Buffer Pools)

在Vulkan里面,创建和销毁命令缓冲区的代价比较高昂,因此,Vulkan提出了命令缓冲池的概念。命令缓冲池负责管理命令缓冲区。一下是官网给出的使用命令缓冲池的原因:

  1. 应用程序可能只是很短暂的使用命令缓冲区。如果没有缓冲池,为了节约资源,可能会频繁的创建和销毁命令缓冲区。此处类似内存池的概念。
  2. 命令缓冲区是一块特殊的内存,此内存需要被CPU和GPU同时存取。某些系统里面,内存到处理器的映射需要用到很大一块内存,因此,一个很小的命令缓冲区可能需要使用很大一块内存来映射。
  3. 内存映射的代价很昂贵,因为内存映射会改变页表以及使TLB(Translation Lookaside Buffer)失效。因此需要一次性映射一个比较大的内存,而不是每次都映射一小块。关于TLB的解释,参考https://baike.baidu.com/item/TLB/2339981?fr=aladdin

命令缓冲池和队列族

硬件负责读取命令缓冲内存,而不同的GPU硬件有自己的内存分配属性,驱动程序需要根据这个属性来创建命令缓冲池。

如果一个GPU有多个硬件队列(上一节有讲解),并且这些硬件队列具有不同的内存分配属性,那么,驱动程序需要为这些硬件队列单独创建对应内存分配属性的命令缓冲池。从官方文档来看,一个硬件队列,应该就是一个队列族。如果硬件队列有自己特有的内存分配属性,那么,一个内存缓冲池则只能对应一个硬件队列,也就是一个队列族。

创建命令缓冲池的函数声明如下:

VKAPI_ATTR VkResult VKAPI_CALL vkCreateCommandPool(
    VkDevice                                    device,
    const VkCommandPoolCreateInfo*              pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkCommandPool*                              pCommandPool
);

创建命令缓冲池的方法如下:

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);

因为每个队列族的内存分配属性都不一样,所以需要为每一个队列族都创建一个命令缓冲池,以上只为一个队列族创建了缓冲池。

创建命令缓冲区

创建命令缓冲区函数声明如下:

VKAPI_ATTR VkResult VKAPI_CALL vkAllocateCommandBuffers(
    VkDevice                                    device,
    const VkCommandBufferAllocateInfo*          pAllocateInfo,
    VkCommandBuffer*                            pCommandBuffers
);

 创建命令缓冲区的代码也很简单:

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);

使用命令缓冲区

对命令缓冲区的使用必须以vkBeginCommandBuffer()开头,以vkEndCommandBuffer()函数结尾。vkBeginCommandBuffer()函数将命令缓冲区的状态改变为“录制”状态,也就是记录命令。vkEndCommandBuffer()函数告诉Vulkan,操作已经完成,命令缓冲区当前的“录制”状态将变为“准备”状态。在调用这两个函数之间,可以调用一些列vkCmd*函数往命令缓冲区插入命令,比如:vkCmdSetLineWidth()和vkCmdDraw()。

命令缓冲区进入“准备”状态后,GPU此时还不会做任何事情。为了让GPU开始执行这些指令,必须调用vkQueueSubmit()函数,将命令缓冲区提交到GPU的队列中。在将命令缓冲区提交到队列之前,还有很多事情要做,此是后话。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值