Command buffers
As discussed in the render thread manual, the framework uses a multi-threaded architecture where rendering operations are executed on the render thread. All rendering operations in the framework are performed through render::GpuCommandBuffer objects. These command buffers are the primary interface for interacting with the GPU - whether you're drawing geometry, setting pipeline states, or dispatching compute shaders.
Rendering can be a very CPU heavy operation even though GPU does all the rendering - but CPU is still the one submitting all those commands. Command buffers help address this by allowing you to record rendering commands on different threads, distributing the CPU workload more efficiently. Each command buffer can be populated with commands on any thread and submitted from any thread.
Creation
Command buffers are created via a render::GpuCommandBufferPool. The pool is created by calling GpuDevice::CreateGpuCommandBufferPool with render::GpuCommandBufferPoolCreateInformation as the parameter. The pool information specifies:
GpuQueueUsage- This determines which commands may be executed on command buffers created from this pool. This can be:GQT_GRAPHICS- Command buffers that support all type of operations (default).GQT_COMPUTE- Command buffers that only support compute operations.
- Thread ID - The thread on which the pool and its command buffers are allowed to be used.
If you know a command buffer will only execute compute operations it is beneficial to create the pool using GQT_COMPUTE.
GpuDevice& gpuDevice = GetApplication().GetPrimaryGpuDevice();
GpuCommandBufferPoolCreateInformation poolCreateInformation =
GpuCommandBufferPoolCreateInformation::CreateForThisThread(GQT_GRAPHICS);
SPtr<GpuCommandBufferPool> commandBufferPool = gpuDevice.CreateGpuCommandBufferPool(poolCreateInformation);
Once you have a pool, you can create command buffers by calling render::GpuCommandBufferPool::Create or render::GpuCommandBufferPool::FindOrCreate. The latter will attempt to reuse an existing command buffer from the pool if one is available.
SPtr<GpuCommandBuffer> commandBuffer = commandBufferPool->Create(
GpuCommandBufferCreateInformation::Create("MyCommandBuffer"));
Command buffers are automatically put into a recording state when created or retrieved from the pool.
Recording commands
Once created, you can record rendering commands by calling methods on the command buffer. These include:
render::GpuCommandBuffer::BeginRenderPass- Begins a render pass for rendering to a render targetrender::GpuCommandBuffer::EndRenderPass- Ends the current render passrender::GpuCommandBuffer::SetGpuGraphicsPipelineState- Sets the graphics pipeline staterender::GpuCommandBuffer::SetGpuComputePipelineState- Sets the compute pipeline staterender::GpuCommandBuffer::SetGpuParameterSet- Binds a GPU parameter setrender::GpuCommandBuffer::SetVertexBuffers- Binds vertex buffersrender::GpuCommandBuffer::SetIndexBuffer- Binds an index bufferrender::GpuCommandBuffer::SetVertexDescription- Sets the vertex declarationrender::GpuCommandBuffer::SetDrawOperation- Sets the draw operation typerender::GpuCommandBuffer::Draw- Draws geometry using vertex buffersrender::GpuCommandBuffer::DrawIndexed- Draws indexed geometryrender::GpuCommandBuffer::DispatchCompute- Executes a compute shaderrender::GpuCommandBuffer::ClearRenderTarget- Clears the render targetrender::GpuCommandBuffer::SetViewport- Sets the viewport arearender::GpuCommandBuffer::IssueBarriers- Issues memory and execution barriersrender::GpuCommandBuffer::TransitionTextureLayout- Transitions a texture layout
// Begin render pass
commandBuffer->BeginRenderPass(renderTarget, 0, RT_NONE);
// Clear the render target
commandBuffer->ClearRenderTarget(FBT_COLOR | FBT_DEPTH, Color::kBlue, 1.0f, 0, 0xFF);
// Bind pipeline state and resources
commandBuffer->SetGpuGraphicsPipelineState(pipelineState);
commandBuffer->SetVertexBuffers(0, &vertexBuffer, 1);
commandBuffer->SetIndexBuffer(indexBuffer);
commandBuffer->SetVertexDescription(vertexDescription);
commandBuffer->SetDrawOperation(DOT_TRIANGLE_LIST);
commandBuffer->SetGpuParameterSet(parameterSet);
// Draw
commandBuffer->DrawIndexed(0, indexCount, 0, vertexCount, 1, 0);
// End render pass
commandBuffer->EndRenderPass();
Finishing recording
Before a command buffer can be submitted, you must call render::GpuCommandBuffer::End to finish recording and prepare the command buffer for submission.
commandBuffer->End();
Submitting
Commands queued on a command buffer will only get executed after the command buffer is submitted. Submission is done by calling GpuDevice::SubmitCommandBuffer.
GpuDevice& gpuDevice = GetApplication().GetPrimaryGpuDevice();
gpuDevice.SubmitCommandBuffer(commandBuffer);
You must externally synchronize access to GpuCommandBuffer when passing it between threads, as it is not thread safe.
Command buffer state
Command buffers have several states that can be queried via render::GpuCommandBuffer::GetState:
render::CommandBufferState::Ready- Buffer is ready to begin recording commandsrender::CommandBufferState::Recording- Buffer is currently recording commandsrender::CommandBufferState::Executing- Buffer has been submitted and is executing on the GPUrender::CommandBufferState::Done- Buffer has finished executing
CommandBufferState state = commandBuffer->GetState();
if (state == CommandBufferState::Done)
{
B3D_LOG(Info, LogRenderer, "Command buffer has finished executing");
}