Vulkan教程翻译之五 创建 Command Buffer

原文链接:https://vulkan.lunarg.com/doc/sdk/1.2.131.2/windows/tutorial/html/04-init_command_buffer.html

创建 Command Buffer

这一章节的代码文件是 04-init_command_buffer.cpp。

基础的 Command Buffer 操作

在其他图形API里,应用也许会通过API调用来设置某个属性,比如调用 glLineWidth() 设置线条宽度。在底层,驱动把API调用转换成GPU特定的命令,并且该命令放到 command buffer 里。驱动还通过创建和销毁 command buffer 来对它们进行管理,这些对应用是不可见的。最终,驱动“submits”该 command buffer 给GPU去执行命令。

在 Vulkan 里,你创建完一个 command buffer,然后通过一个简单的Vulkan API调用 vkCmdSetLineWidth() 就可以把一个命令添加到该 command buffer里。因为每一个GPU都有它自己的“指令集”,驱动仍然需要做一点工作来生成GPU特定指令来设置线条宽度。

在这里,驱动确定合适的二进制GPU指令插入到 command buffer里,来指示GPU使用线条宽度 5 绘制后续的线条。你不需要了解实际的 command buffer 内容,因为驱动为你做了GPU编程这部分。

Command Buffer pools

下一步是学习如何获取一个 command buffer。查看一下代码库里这一章节的 04-init_command_buffer.cpp 文件。

因为创建和销毁单独的 command buffer 开销很大,Vulkan使用 command buffer pool 来管理 command buffer。使用 command buffer pool 的动机包括:

  1. 一些应用使用短命的 command buffer,这意味着它们被频繁地创建和销毁。专用的池分配器通常可以更有效地处理这些分配模式。
  2. command buffer 内存的特殊点在于它必须对CPU和GPU都可见。在许多系统里,对处理器(CPU或GPU)的内存映射只能使用大的存储颗粒才能完成,这造成了小的 command buffer 会浪费很多内存。
  3. 内存映射是开销很大的,因为它通常涉及页表修改和TLB缓冲失效。比单独映射每一块 command buffer 更好的做法是,一次映射一个更大的 command buffer pool,然后在它里面再分配独立的子缓冲区。

Command Buffer Pools 和 Queue Families

驱动使用适合于读取该 command buffer 内存的GPU硬件的内存分配属性分配一块command buffer pool。这些属性例子包括内存对齐要求和缓冲行为。

如果按照 physical device queue families 的描述,GPU硬件上存在多个硬件queue,那么驱动可能就需要依据不同的内存分配属性来分配 command buffer pools,用来指定给每一个GPU硬件 queue。只要驱动知道了该 command buffer 所使用的 queue 所在的 queue family,驱动就能替你处理这些细节。

这也意味着一个 command buffer pool 只能与一个 queue family 相关联。这个要求体现在API设计里,就是你创建 command buffer pool 的代码里指定 queue family 索引的地方:

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

请注意,在上一章节中你创建 device 的时候,你决定了使用什么样的 queue。实际上,你必须为每一个应用将要用到的 queue family 创建一个 command buffer pool。因为你在创建 device 的时候只指定了一个 queue family,一个 command buffer pool 就能满足该示例。

创建 Command Buffer

一旦 command buffer pool 创建了之后,从这个池里分配 command buffer 是很容易的:

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调用是设计用来更方便地在单次调用里分配多个 command buffer。这对于需要多个 command buffer 的应用是很有用的。但是示例很简单只需要一个。

使用 Command Buffer

你创建完 command buffer 以后,就可以通过调用 vkBeginCommandBuffer() 来开始“recording”。调用该函数使 command buffer 进入“recording”状态,并且允许你调用众多“vkCmd*”函数之一来插入命令到该 command buffer里。你已经见过了这一章节里的例子 vkCmdSetLineWidth()。另一个例子是 vkCmdDraw(),它告诉GPU去绘制某些顶点。在你完成了插入命令到 command buffer 后,调用 vkEndCommandBuffer() 来表明你已经完成了,并且把 command buffer 退出了“recording”状态,使它准备好了被使用。

在之后的章节中,你会看到实际的填充 command buffer 的代码。

完成 command buffer 录入并不会使GPU做任何事情。为了使GPU去处理一个 command buffer,你必须用 vkQueueSubmit() 把它提交到GPU的 queue 里。在你可以提交 command buffer 给GPU之前还有许多事情要做,这将在本教程的最后章节中实现。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值