创建指令缓冲对象VkCommandBuffer
在之前的内容中,已经创建好了顶点、加载图片,现在可以创建VkCommandBuffer了,每一帧的绘制命令需要记录到VkCommandBuffer上。
先在SceneWidget.h中添加一个函数bool CreateCommandBuffers()进行创建操作,并添加成员变量std::vector<VkCommandBuffer> m_commandBuffers,保存每帧的VkCommandBuffer对象。
在SceneWidget.cpp文件中实现bool CreateCommandBuffers(),
先对m_commandBuffers进行空间分配,由于是记录的每帧对应的VkCommandBuffer对象,所以这里,可以使用m_frameBuffers的数量来进行分配:
m_commandBuffers.resize(m_frameBuffers.size()).
VkCommandBuffer的创建是通过vkAllocateCommandBuffers函数来申请空间。调用vkAllocateCommandBuffers并判断返回值:
VkResult result = vkAllocateCommandBuffers();函数其参数如下:
第一个参数:逻辑设备m_device
第二个参数:VkCommandBufferAllocateInfo结构的指针,所以需定义变量VkCommandBufferAllocateInfo allocateInfo = {};传入& allocateInfo
第三个参数:需要申请空间的VkCommandBuffer数组,这里是m_commandBuffers.data()
补齐allocateInfo的属性
固定写法
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocateInfo.pNext = nullptr;
之前创建的commandPool
allocateInfo.commandPool = m_commandPool;
这个demo里没有VK_COMMAND_BUFFER_LEVEL_SECONDARY。
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
要申请的VkCommandBuffer的数目
allocateInfo.commandBufferCount = m_commandBuffers.size();
对m_commandBuffers进行循环,在每次的循环中进行命令的记录。
记录时,调用vkBeginCommandBuffer函数,重置命令缓冲对象,此函数参数:
第一个参数:对应的m_commandBuffers[i],要记录命令的VkCommandBuffer
第二个参数:VkCommandBufferBeginInfo类型的对象指针。所以需要定义一个变量:VkCommandBufferBeginInfo beginInfo = {};
beginInfo属性赋值:
固定写法
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;;
保持默认
beginInfo.pNext = nullptr;
beginInfo.flags = 0;
beginInfo.pInheritanceInfo = nullptr;
下面开始渲染流程,调用vkCmdBeginRenderPass函数,开始一个渲染流程。其参数:
第一个参数:m_commandBuffers[i]
第二个参数:VkRenderPassBeginInfo类型的变量renderPassInfo = {},输入引用。
第三个参数:VkSubpassContents枚举类型,其值为:VK_SUBPASS_CONTENTS_INLINE将命令嵌入到主命令,不执行辅助命令。
renderPassInfo属性赋值:
固定写法
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.pNext = nullptr;
之前创建过的。
renderPassInfo.renderPass = m_renderPass;
对应的帧缓存
renderPassInfo.framebuffer = m_frameBuffers[i];
不偏移
renderPassInfo.renderArea.offset = {0, 0};
当前的窗口大小
renderPassInfo.renderArea.extent = m_capabilities.currentExtent;
需要补一个std::vector<VkClearValue> clearValues的变量,存清除的颜色,类似于openGL中的背景色。
renderPassInfo.clearValueCount = clearValues.size();
给深度赋初始值
renderPassInfo.pClearValues = clearValues.data();
clearValues的值:
设置背景色
VkClearValue clearColor = {0.5f, 0.3f, 0.1f, 0.0f};
重置深度
VkClearValue clearDepth = {1.0f, 0.0f};
绑定之前创建的pipeline调用vkCmdBindPipeline函数,其参数:
第一个参数:m_commandBuffers[i],
第二个参数:作用到图形管线 VK_PIPELINE_BIND_POINT_GRAPHICS,
第三个参数:m_graphicsPipeline,使用之前创建好的。
绑定vertexBuffer调用 vkCmdBindVertexBuffers函数,其参数:
第一个参数:m_commandBuffers[i]
第二个参数:vertexBuffer可能有多个,从第几个开始,这里是0
第三个参数:从开始位置起用几个buffer,这里是1
第四个参数:vertexBuffer的指针。对于这个demo,这里可直接写&m_vertexBuffer。
第五个参数:数据在数组中的偏移,这里是一个VkDeviceSize类型的指针,所以需要定义一个VkDeviceSize offset = 0,并填入&offset。
绑定描述符集vkCmdBindDescriptorSets,函数参数:
第一个参数:m_commandBuffers[i]
第二个参数:绑定到graphics上,VK_PIPELINE_BIND_POINT_GRAPHICS
第三个参数:之前创建的m_pipelineLayout
第四个参数:第一个VkDescriptorSet的索引,0
第五个参数:DescriptorSet的数量
第六个参数:DescriptorSet的数组指针
第七个参数:不需要偏移数组,所以为0
第八个参数:动态偏移数组,不需要。nullptr
调用绘制函数,进行绘制,绘制方式有两种,一种是根据真实的顶点数据,进行绘制,一种是根据索引,在顶点数据中查找数据进行绘制。
在这个demo,没有输入索引数据,所以按顶点数据进行绘制。
调用vkCmdDraw进行顶点的绘制,其参数:
第一个参数:m_commandBuffers[i]
第二个参数:顶点的数量m_vertexs.size()
第三个参数:教程中说明这个参数为要渲染的instance,没有时,写1.这里写1
第四个参数:第一个顶点的索引,这里写0
第五个参数:第一个instance的索引,这里写0
到这里已经渲染完了,所以要结束渲染vkCmdEndRenderPass(m_commandBuffers[i])
然后结束命令缓存的记录vkEndCommandBuffer(m_commandBuffers[i])之前调用的vkCmd开始的函数,都返回void类型的,而这个vkEndCommandBuffer是有返回值,所以还是要判断一下有没有执行成功。
在SceneWidget::Init中添加调用,并在SceneWidget::UnInit中添加清理函数。
vkFreeCommandBuffers(m_device, m_commandPool, m_commandBuffers.size(), m_commandBuffers.data());
最后执行一下程序,看一下结果
没有报错。