Day 9 (Dec 29, 2020) ~~~ Filling Command Buffers with Drawing Commands - kwilson33/learning-vulkan GitHub Wiki

Progress Today

Here are some major takeaways

1. Commands in Vulkan are not executed directly using function calls.

  • You have to record all of the operations you want to do in command buffer objects.
  • This allows the setting up of the drawing commands to be done in advance and in multiple threads.

2. Command buffers themselves are stored in a command pool.

3. You need to record a command buffer for every image in the swap chain.

  • This is because, one of the drawing commands (what will be stored in the command buffer in this part of tutorial) involves binding the right VkFramebuffer. And, each VkFramebuffer references an image in the swap chain.

4. To start 'recording' a command buffer you use a small VkCommandBufferBeginInfo struct.

  • You fill in a little information about the usage of this command buffer.

5. Then, you need to start a render pass to actually start drawing! This will help fill in the command buffers with, well, commands!

  • We defined the main render pass earlier, but there are still some things each render pass instance needs to know before it can start like:
    • The render area (where shader loads and stores will happen)
    • The framebuffer it will use (we created framebuffers for each swap chain image)
    • What value to use when clearing the screen.

6. Once the render pass is started, you bind the graphics pipeline to each command buffer

vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
  • Then, you finally tell the command buffer which draw command to do.
vkCmdDraw(commandBuffers[i], 3, 1, 0, 0);
  • The params are
    • The command buffer
    • The vertex count
    • The instance count (use 1 if not doing instanced rendering)
    • The first vertex offset for the vertex buffer.
    • The first instance offset.
  • It took awhile to get to that point! But all the important information is stored before, so the draw command is pretty simple itself.

7. Finally, you end the render pass instance and each command buffer (this is going on inside of a loop over all of the command buffers).

  • Now, each command buffer has the correct commands and each render pass instance knows which command buffer it's in charge of.

See ya!

So, the fact that I had to fill in more render pass info in this part of the tutorial confused me, but I did some more looking up to clear this up. This quote from the API spec helped

A render pass represents a collection of attachments, subpasses, and dependencies between the subpasses, and describes how the attachments are used over the course of  the subpass. The use of a render pass in a command buffer is a render pass instance.

It's that last sentence that helped me understand you need to instantiate the render pass multiple times for each command buffer. You do most of the setting up of the render pass before this step, like defining a class, and then when you're doing command buffer stuff, you instantiate the render pass and fill in a little more information.

Also, this post on reddit and the answers also helped me understand why the render pass instance is started (vkCmdBeginRenderPass) inside of a command buffer (vkBeginCommandBuffer).