[译]Vulkan教程(18)命令buffers
[译]Vulkan教程(18)命令buffers
Command buffers 命令buffer
Commands in Vulkan, like drawing operations and memory transfers, are not executed directly using function calls. You have to record all of the operations you want to perform in command buffer objects. The advantage of this is that all of the hard work of setting up the drawing commands can be done in advance and in multiple threads. After that, you just have to tell Vulkan to execute the commands in the main loop.
Vulkan中的命令,例如绘制操作和内存转移,不是直接通过函数调用来执行的。你必须几所有你想要的操作到命令buffer对象里。这样做的优势是,所有的设置绘制命令的艰苦努力都可以提前完成,还能多线程完成。之后,你只需告诉Vulkan去执行主循环中的命令即可。
Command pools 命令池
We have to create a command pool before we can create command buffers. Command pools manage the memory that is used to store the buffers and command buffers are allocated from them. Add a new class member to store a VkCommandPool:
我们必须创建命令池before我们能创建命令buffer。命令池管理内存that用于保存buffer,命令buffer从命令池中申请。添加新类成员to保存VkCommandPool:
VkCommandPool commandPool;
Then create a new function createCommandPool and call it from initVulkan after the framebuffers were created.
然后创建一个新函数createCommandPool ,在initVulkan 中调用它after帧缓存创建后。
void initVulkan() {
createInstance();
setupDebugCallback();
createSurface();
pickPhysicalDevice();
createLogicalDevice();
createSwapChain();
createImageViews();
createRenderPass();
createGraphicsPipeline();
createFramebuffers();
createCommandPool();
}
...
void createCommandPool() {
}
Command pool creation only takes two parameters:
创建命令池只需要2个参数:
QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);
VkCommandPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
poolInfo.flags = ; // Optional
Command buffers are executed by submitting them on one of the device queues, like the graphics and presentation queues we retrieved. Each command pool can only allocate command buffers that are submitted on a single type of queue. We're going to record commands for drawing, which is why we've chosen the graphics queue family.
命令buffer被执行by提交它们到一个设备队列-例如图形和呈现队列that我们检索过的。每个命令池只能分配一种类型queue的命令buffer。
There are two possible flags for command pools:
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT: Hint that command buffers are rerecorded with new commands very often (may change memory allocation behavior)VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT: Allow command buffers to be rerecorded individually, without this flag they all have to be reset together
命令池有2个候选的标志:
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT:命令buffer常常记录新的命令(可能改变内存分配行为)。VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT:允许命令缓存独立记录,如果没有这个标志,它们就必须一起重置。
We will only record the command buffers at the beginning of the program and then execute them many times in the main loop, so we're not going to use either of these flags.
我们只会在程序开始时记录命令,然后在主循环中执行很多次,所以我们不会用这2个标志。
if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
throw std::runtime_error("failed to create command pool!");
}
Finish creating the command pool using the vkCreateCommandPool function. It doesn't have any special parameters. Commands will be used throughout the program to draw things on the screen, so the pool should only be destroyed at the end:
首先用vkCreateCommandPool 创建命令池。
void cleanup() {
vkDestroyCommandPool(device, commandPool, nullptr);
...
}
Command buffer allocation 分配命令buffer
We can now start allocating command buffers and recording drawing commands in them. Because one of the drawing commands involves binding the right VkFramebuffer, we'll actually have to record a command buffer for every image in the swap chain once again. To that end, create a list of VkCommandBuffer objects as a class member. Command buffers will be automatically freed when their command pool is destroyed, so we don't need an explicit cleanup.
我们现在可以分配命令buffer,向其中记录绘制命令了。因为有个绘制命令涉及绑定到正确的VkFramebuffer,我们实际上要记录一个命令buffer for交换链的每个image。为此,创建VkCommandBuffer 对象数组作为类成员。命令buffer会自动释放when它们的命令池被销毁,所以我们不需显示地清理它们。
std::vector<VkCommandBuffer> commandBuffers;
We'll now start working on a createCommandBuffers function that allocates and records the commands for each swap chain image.
我们现在开始编写createCommandBuffers 函数that分配和记录命令for交换链的每个image。
void initVulkan() {
createInstance();
setupDebugCallback();
createSurface();
pickPhysicalDevice();
createLogicalDevice();
createSwapChain();
createImageViews();
createRenderPass();
createGraphicsPipeline();
createFramebuffers();
createCommandPool();
createCommandBuffers();
}
...
void createCommandBuffers() {
commandBuffers.resize(swapChainFramebuffers.size());
}
Command buffers are allocated with the vkAllocateCommandBuffers function, which takes a VkCommandBufferAllocateInfo struct as parameter that specifies the command pool and number of buffers to allocate:
命令buffer用vkAllocateCommandBuffers 函数分配,它接收一个VkCommandBufferAllocateInfo 结构体作为参数that指定命令池和要分配的buffer的数量:
VkCommandBufferAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = commandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = (uint32_t) commandBuffers.size();
if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate command buffers!");
}
The level parameter specifies if the allocated command buffers are primary or secondary command buffers.
VK_COMMAND_BUFFER_LEVEL_PRIMARY: Can be submitted to a queue for execution, but cannot be called from other command buffers.VK_COMMAND_BUFFER_LEVEL_SECONDARY: Cannot be submitted directly, but can be called from primary command buffers.
参数level 指定了分配的命令buffer是一级还是二级命令buffer。
VK_COMMAND_BUFFER_LEVEL_PRIMARY:可被提交到queue去执行,但不能被其他命令buffer调用。VK_COMMAND_BUFFER_LEVEL_SECONDARY:不能被直接提交,但可以被一级命令buffer调用。
We won't make use of the secondary command buffer functionality here, but you can imagine that it's helpful to reuse common operations from primary command buffers.
Starting command buffer recording 开始在命令buffer里记录命令
We begin recording a command buffer by calling vkBeginCommandBuffer with a small VkCommandBufferBeginInfostructure as argument that specifies some details about the usage of this specific command buffer.
我们开始记录命令buffer by调用vkBeginCommandBuffer with一个小的VkCommandBufferBeginInfostructure结构体作为参数that指定这个命令buffer的用法的细节。
for (size_t i = ; i < commandBuffers.size(); i++) {
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
beginInfo.pInheritanceInfo = nullptr; // Optional
if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
throw std::runtime_error("failed to begin recording command buffer!");
}
}
The flags parameter specifies how we're going to use the command buffer. The following values are available:
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT: The command buffer will be rerecorded right after executing it once.VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT: This is a secondary command buffer that will be entirely within a single render pass.VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT: The command buffer can be resubmitted while it is also already pending execution.
参数flags 指定了我们要如何使用这个命令buffer。下述值可选:
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT:命令buffer会在执行一次后立即再次记录。VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT:这是二级命令buffer,其完全处于一个render pass内。VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT:这个命令缓存可以被重新提交,同时它已经准备好执行了。
We have used the last flag because we may already be scheduling the drawing commands for the next frame while the last frame is not finished yet. The pInheritanceInfo parameter is only relevant for secondary command buffers. It specifies which state to inherit from the calling primary command buffers.
我们用了最后一个标志,因为我们可能已经在安排下一帧的绘制命令,同时最新的一帧还没完成。参数pInheritanceInfo 只与二级命令buffer有关。它指定了从一级命令buffer继承哪个状态。
If the command buffer was already recorded once, then a call to vkBeginCommandBuffer will implicitly reset it. It's not possible to append commands to a buffer at a later time.
如果命令buffer已经记录过一次了,那么调用vkBeginCommandBuffer 会隐式地重置它。之后扩展命令到buffer里是无法做到的。
Starting a render pass 启动一个render pass
Drawing starts by beginning the render pass with vkCmdBeginRenderPass. The render pass is configured using some parameters in a VkRenderPassBeginInfo struct.
绘制开始by调用vkCmdBeginRenderPass。Render pass用VkRenderPassBeginInfo 结构体的一些参数来配置。
VkRenderPassBeginInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = renderPass;
renderPassInfo.framebuffer = swapChainFramebuffers[i];
The first parameters are the render pass itself and the attachments to bind. We created a framebuffer for each swap chain image that specifies it as color attachment.
第一个参数是render pass自己和要绑定的附件。我们为交换链的每个image创建了一个帧缓存that指定它为颜色附件。
renderPassInfo.renderArea.offset = {, };
renderPassInfo.renderArea.extent = swapChainExtent;
The next two parameters define the size of the render area. The render area defines where shader loads and stores will take place. The pixels outside this region will have undefined values. It should match the size of the attachments for best performance.
接下来的2个参数定义渲染区域的大小。渲染区域定义了shader加载和保存的位置。位于区域外的像素的值是未定义的。它应当与附件的大小匹配for最佳性能。
VkClearValue clearColor = {0.0f, 0.0f, 0.0f, 1.0f};
renderPassInfo.clearValueCount = ;
renderPassInfo.pClearValues = &clearColor;
The last two parameters define the clear values to use for VK_ATTACHMENT_LOAD_OP_CLEAR, which we used as load operation for the color attachment. I've defined the clear color to simply be black with 100% opacity.
最后2个参数定义清空值for VK_ATTACHMENT_LOAD_OP_CLEAR,which我们用作load操作for颜色附件。我已经定义了清空颜色to简单的黑色with 100%不透明度。
vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
The render pass can now begin. All of the functions that record commands can be recognized by their vkCmd prefix. They all return void, so there will be no error handling until we've finished recording.
现在可以开始render pass了。记录命令的所有函数都带有vkCmd前缀。它们都返回void,所以在我们结束记录前,不会有任何错误处理。
The first parameter for every command is always the command buffer to record the command to. The second parameter specifies the details of the render pass we've just provided. The final parameter controls how the drawing commands within the render pass will be provided. It can have one of two values:
VK_SUBPASS_CONTENTS_INLINE: The render pass commands will be embedded in the primary command buffer itself and no secondary command buffers will be executed.VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS: The render pass commands will be executed from secondary command buffers.
每个命令的第一个参数总是用于记录命令的buffer。第二个参数指定我们提供的render pass的细节。最后一个参数控制render pass的绘制命令如何被提供。它可能有下述2个值之一:
VK_SUBPASS_CONTENTS_INLINE:render pass命令会被嵌入一级命令buffer本身,不会有二级命令被执行。VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS:render pass命令会被二级命令buffer执行。
We will not be using secondary command buffers, so we'll go with the first option.
我们不会使用二级命令buffer,所以我们选第一个。
Basic drawing commands 基础绘制命令
We can now bind the graphics pipeline:
我们现在可以绑定图形管道:
vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
The second parameter specifies if the pipeline object is a graphics or compute pipeline. We've now told Vulkan which operations to execute in the graphics pipeline and which attachment to use in the fragment shader, so all that remains is telling it to draw the triangle:
第二个参数指定管道对象是图形or计算管道。我们现在告诉了Vulkan在图形管道执行哪些操作,在Fragment shader中使用哪些附件,所以剩下的就是告诉它to绘制三角形:
vkCmdDraw(commandBuffers[i], , , , );
The actual vkCmdDraw function is a bit anticlimactic, but it's so simple because of all the information we specified in advance. It has the following parameters, aside from the command buffer:
vertexCount: Even though we don't have a vertex buffer, we technically still have 3 vertices to draw.instanceCount: Used for instanced rendering, use1if you're not doing that.firstVertex: Used as an offset into the vertex buffer, defines the lowest value ofgl_VertexIndex.firstInstance: Used as an offset for instanced rendering, defines the lowest value ofgl_InstanceIndex.
函数vkCmdDraw 有点虎头蛇尾的,但是它如此简单-因为我们提前指定了所有的信息。它的参数,除了命令缓存外,有:
vertexCount:即使没有顶点buffer,我们技术上仍旧有3个顶点要画。instanceCount:用于instanced渲染,如果不用,填入1。firstVertex:顶点buffer的偏移量,它定义了gl_VertexIndex的最小值。firstInstance:用于instanced渲染的偏移量,定义了gl_InstanceIndex的最小值。
Finishing up 马上完工
The render pass can now be ended:
Render pass现在可以结束了:
vkCmdEndRenderPass(commandBuffers[i]);
And we've finished recording the command buffer:
我们已经完成了记录命令buffer的任务:
if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
throw std::runtime_error("failed to record command buffer!");
}
In the next chapter we'll write the code for the main loop, which will acquire an image from the swap chain, execute the right command buffer and return the finished image to the swap chain.
下一章我们将写主循环的代码,which要从交换链请求一个image,执行正确的命令buffer,返回完成的image到交换链。
C++ code / Vertex shader / Fragment shader
[译]Vulkan教程(18)命令buffers的更多相关文章
- [译]Vulkan教程(27)Image
[译]Vulkan教程(27)Image Images Introduction 入门 The geometry has been colored using per-vertex colors so ...
- [译]Vulkan教程(26)描述符池和set
[译]Vulkan教程(26)描述符池和set Descriptor pool and sets 描述符池和set Introduction 入门 The descriptor layout from ...
- [译]Vulkan教程(25)描述符布局和buffer
[译]Vulkan教程(25)描述符布局和buffer Descriptor layout and buffer 描述符布局和buffer Introduction 入门 We're now able ...
- [译]Vulkan教程(24)索引buffer
[译]Vulkan教程(24)索引buffer Index buffer 索引buffer Introduction 入门 The 3D meshes you'll be rendering in a ...
- [译]Vulkan教程(23)暂存buffer
[译]Vulkan教程(23)暂存buffer Staging buffer 暂存buffer Introduction 入门 The vertex buffer we have right now ...
- [译]Vulkan教程(22)创建顶点buffer
[译]Vulkan教程(22)创建顶点buffer Vertex buffer creation 创建顶点buffer Introduction 入门 Buffers in Vulkan are re ...
- [译]Vulkan教程(20)重建交换链
[译]Vulkan教程(20)重建交换链 Swap chain recreation 重建交换链 Introduction 入门 The application we have now success ...
- [译]Vulkan教程(19)渲染和呈现
[译]Vulkan教程(19)渲染和呈现 Rendering and presentation 渲染和呈现 Setup 设置 This is the chapter where everything ...
- [译]Vulkan教程(13)图形管道基础之Shader模块
[译]Vulkan教程(13)图形管道基础之Shader模块 Shader modules Unlike earlier APIs, shader code in Vulkan has to be s ...
随机推荐
- 浅谈Redis面试热点之工程架构篇[1]
前言 前面用两篇文章大致介绍了Redis热点面试中的底层实现相关的问题,感兴趣的可以回顾一下:[决战西二旗]|Redis面试热点之底层实现篇[决战西二旗]|Redis面试热点之底层实现篇(续) 接下来 ...
- 聊聊JS的二进制家族:Blob、ArrayBuffer和Buffer
事实上,前端很少涉及对二进制数据的处理,但即便如此,我们偶尔总能在角落里看见它们的身影. 今天我们就来聊一聊前端的二进制家族:Blob.ArrayBuffer和Buffer 概述 Blob: 前端的一 ...
- BeetleX之TCP服务应用详解
BeetleX是.net core平台下的一个开源TCP 通讯组件,它不仅使用简便还提供了出色性能的支持,可以轻易让你实现上百万级别RPS吞吐的服务应用.组件所提供的基础功能也非常完善,可以让你轻易扩 ...
- 1.1 Spring 概述
1.1 Spring 概述 1.1.1 Spring 的简史 第一阶段:xml配置 Spring 1.x时代使用xml配置Bean 第二阶段:注解配置 Spring2.x Spring 提供了声明B ...
- java8-date和timeAPI
一 我们为什么要学习 java.timeAPI 原先的Date and Calendar 类的api比较复杂,不易于理解,应用起来不是很灵活. Calendar 是个线程不安全的类会导致SimpleD ...
- 【Java必修课】好用的Arrays.asList也有这三个坑
好用的asList 在开发或写测试用例的过程中,经常会用到Arrays.asList()这个方法,可以快速方便地将数组转化成一个List.例如: List<String> list = A ...
- 微信小程序目录结构与配置介绍
一.小程序结构目录 小程序框架提供了自己的视图层描述语言 WXML 和 WXSS,以及 JavaScript,并在视图层与逻辑层间提供了数据传输和事件系统,让开发者能够专注于数据与逻辑. 官网 1.1 ...
- CAD绘图效率低?教你4个CAD绘图技巧,绘图效率提升十倍
CAD绘图一直是一个谜一样的存在,说它简单吧,很多人都无法完全精通,说它难吧,很多人也都自学成才了. 如何学好CAD绘图是个难题,但是老话说的好,只要思想不滑坡,办法总比困难多,掌握以下这些CAD绘图 ...
- 安卓AlertDialog四种对话框的最科学编写用法
首先我们上图: xml的代码如下,用于编写按钮: <?xml version="1.0" encoding="utf-8"?> <Linear ...
- 如何把Mybatis的Mapper.xml配置文件和dao接口放在同一个包下
有的时候我们在Maven项目中写关于Mybatis的项目时,会涉及到很多的实体类,也就会涉及到很多的dao接口,如果此时我们仍然把dao接口和xml写在同一个包下,会让项目接口变得很乱,杂七杂八的,所 ...