Welcome back! In the previous tutorial we learned about the Pipeline State Object and how to use these to efficiently change the state of our graphics pipeline for rendering.
Today we will take a look at the next feature of DirectX 12, the command list. Command lists are basically a set of drawing and resource management calls generated from one or more threads, and then executed to render a scene; typically in the end of a Render function. This have a performance benefit as we pre-compute rendering tasks that can be executed and even reused at a later time. These command lists can be executed across multiple threads.
Why do I have to care?
As a DX12 graphics programmer, it’s now your job to group rendering calls into work items, and when to submit this work for the GPU – This has to be a part of your engine architecture.
What’s in a command list?
A command list will contain traditional rendering API calls like drawing primitives, changing rendering states, etc. If we take a look at a part of the command list in our example application, we can see that it’s heavily used in our Render() function:
In the code above (I cut the lines on purpose as it’s not needed here), we can see that we are not directly sending the commands to the GPU, but store them in a list named m_commandList. We submit a call the RSSetViewport and ScissorRects to set up our view, clear the render targets, configure our IA shader stage (Input Assembler), and finally draw our 36 vertices as a cube made up by triangles.
At this point, nothing has really happened as we haven’t submitted the list yet.
Above we simply add commands to a command list, and then at a later point, execute it. This is called a direct command list. However, we can also create lists that contains a small group of API calls that a command list can execute whenever, multiple times. These are named Bundles. Bundles are simply a group of API calls ( a “mini-command” list made for reuse).
We won’t go in depth of this topic here, but will come back to this in a later tutorial. Like Bundles, a direct command list can also be execute multiple times, but you are responsible for checking if it has completed before executing it again.
When creating these bundles, the driver will try to pre-process as much as possible for more efficient execution when it’s needed.
Creating a Command List
A command list is created using the CreateCommandList on the Direct3D Device. This function needs to know what type it will be (D3D12_COMMAND_LIST_TYPE contains all possibilities), like if it will be a bundle or a direct list, a command allocator used to manage the memory for the list, and the PSO created in the previous tutorial.
We create a direct command list with our one and only PSO.
Filling the Command List with commands
Once the command list is created, we can start adding commands to it. When created, it will automatically be in a record state, ready for you to provide commands to it by using the ID3D12GraphicsCommandList interface.
You typically open it up for Recording using the Reset function, and when done recording, you end it with the Close function.
Executing the Command List
Once your list is full of commands, you can submit it to the command queue by using the ExecuteCommandLists function. When you initialize D3D, a default command queue is created. It is possible to create your own command queues, but I recommend you to stick to the default one for now, which is sufficient in most cases.
The command queue will automatically execute the commands in the list.