Vireo  0.0
Vireo 3D Rendering Hardware Interface
Command Lists

Command lists are objects used to record commands which can be subsequently submitted to a submission queue for execution.

Recorded commands include commands to bind pipelines and descriptor sets to the command list, commands to modify the GPU state, commands to draw (for graphics rendering), commands to dispatch (for compute), commands to copy buffers and images, and other commands.

Each command list manages state independently of other command lists. Unless otherwise specified, and without explicit synchronization, the various commands submitted to a submission queue via command lists may execute in arbitrary order relative to each other, and/or concurrently.

Command allocators

Command lists are created from command allocators. A CommandAllocator is used to allocate datas needed by command lists and to reset them before use.

For portability (between graphics API) reasons you can only reset command lists from command allocator, which mean that you reset all command lists created by the corresponding allocator.

And since command allocators can't be shared between threads you typically need one command allocator and a least one command list per worker or frame thread. But don't worry about that, command allocators are cheap and command list allocations ara fast and doing it from multiple allocators in parallel scales well.

If you use threads, record commands in parallel, but submit in one place; use fences/semaphores to coordinate when it’s safe to reset.

Creating a command list

First create a CommandAllocator then create a CommandList. Command lists are created ready-to-use, you don't need to reset them explicitly if you need a one-time command list (e.g. for memory transfers).

// Create a command list
const auto uploadCommandAllocator = vireo->createCommandAllocator(vireo::CommandType::TRANSFER);
const auto uploadCommandList = uploadCommandAllocator->createCommandList();

Using a command list

There is two typical use of command list :

  • One-time command list : for buffer, image transfer or one-time execution of a compute shader
  • Per frame command list : for rendering

For both cases you start a command recording session with vireo::CommandList::begin and end the recording session with vireo::CommandList::end. Then you need to submit the work to the GPU using Submission queues.

One-time command list

For one-time command list you create them, use them, submit them, wait for the end of the execution then destroy them :

// Example of a using a one-time command list
// In a local scope for objects destruction
{
// Create a command list
const auto uploadCommandAllocator = vireo->createCommandAllocator(vireo::CommandType::TRANSFER);
const auto uploadCommandList = uploadCommandAllocator->createCommandList();
uploadCommandList->begin();
...
// Record command here
...
uploadCommandList->end();
// Execute the commands
const auto transferQueue = vireo->createSubmitQueue(vireo::CommandType::TRANSFER);
transferQueue->submit({uploadCommandList});
// Wait for the commands to executed by the GPU
transferQueue->waitIdle();
} // commands allocator, list and queue are destroyed

Other command list

When rendering you generally need a least one command allocator and one command list per frame if you do not want to wait on the CPU side at the end of each frame. Having one command list per frame and waiting for the previous frame to be presented at the start of each frame allow you to do more work on the CPU side (e.g. processing the physic) between frames and you can also have multiple frames in flight.

For this type of use you have to :

  • Allocate one command allocator and at least one command list per frame during the initialization phase
  • Reset the command allocator at the start of the frame
// Typical command list use inside a frame rendering function
// Get the current frame data
const auto& frame = framesData[swapChain->getCurrentFrameIndex()];
...
// Reset the frame command allocator and all the command lists for this frame
frame.commandAllocator->reset();
// Begin a command recording session
const auto& cmdList = frame.commandList;
cmdList->begin();
...
// Record drawing commands
...
cmdList->end();
// Submit all command lists before presenting
graphicQueue->submit(frame.inFlightFence, swapChain, {cmdList});
...