GPU Data Structures for DirectX 12 Indirect Draw
Executing indirect draw commands in DirectX 12 shifts draw call
generation entirely to the GPU, enabling high-performance GPU-driven
rendering pipelines such as occlusion culling and dynamic LOD selection.
To invoke an indirect draw via
ID3D12GraphicsCommandList::ExecuteIndirect, you must
prepare specific GPU-resident memory buffers that encode drawing
parameters and execution counts. This article outlines the required GPU
data structures, their layouts, resource states, and optional components
needed to execute these commands successfully.
1. The Indirect Argument Buffer
The primary data structure required on the GPU is the Argument
Buffer. This is a standard ID3D12Resource buffer placed in
the D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT state before the
ExecuteIndirect call is issued.
The binary layout of this buffer must strictly mirror the argument
operations defined by the CPU-created
ID3D12CommandSignature. The buffer can contain data for a
single draw call or an array of consecutive draw commands.
Depending on your pipeline, the argument buffer contains one or more of the following fundamental structures:
Draw Instanced
Arguments (D3D12_DRAW_ARGUMENTS)
For non-indexed geometry, the buffer must supply a four-component
structure equivalent to the parameters of
DrawInstanced:
struct D3D12_DRAW_ARGUMENTS {
UINT VertexCountPerInstance;
UINT InstanceCount;
UINT StartVertexLocation;
UINT StartInstanceLocation;
};Draw
Indexed Instanced Arguments
(D3D12_DRAW_INDEXED_ARGUMENTS)
For indexed geometry, the structure matches the arguments expected by
DrawIndexedInstanced:
struct D3D12_DRAW_INDEXED_ARGUMENTS {
UINT IndexCountPerInstance;
UINT InstanceCount;
UINT StartIndexLocation;
INT BaseVertexLocation;
UINT StartInstanceLocation;
};Extended Command Layouts
If the command signature defines root arguments or view bindings prior to the draw, the GPU data structure must pack those sequentially preceding the draw arguments. Common inclusions are:
- Root Constants: Direct 32-bit values matching
D3D12_INDIRECT_ARGUMENT_TYPE_32BIT_CONSTANTS. - Root Views: 64-bit GPU virtual addresses
(
D3D12_GPU_VIRTUAL_ADDRESS) for Constant Buffer Views (CBV), Shader Resource Views (SRV), or Unordered Access Views (UAV). - Vertex and Index Buffer Views: Packed
D3D12_VERTEX_BUFFER_VIEWorD3D12_INDEX_BUFFER_VIEWstructures if vertex or index inputs are altered per command.
2. The Count Buffer (Optional)
When the exact number of commands to execute is determined dynamically on the GPU—such as after a compute-based frustum or occlusion culling pass—a Count Buffer is required.
- Data Layout: The count buffer is a standard GPU
buffer containing a single 32-bit unsigned integer (
UINT32/uint). - Value: This integer specifies the actual number of commands to execute from the argument buffer.
- Behavior: When provided to
ExecuteIndirect, the runtime clamps this value to theMaxCommandCountparameter passed to the CPU-side call. - State: Like the argument buffer, the count buffer
must transition to
D3D12_RESOURCE_STATE_INDIRECT_ARGUMENTprior to command execution.
If a count buffer is not used, the argument buffer executes a fixed
number of commands defined directly by the MaxCommandCount
parameter.
3. GPU Buffer Preparation and Synchronization
Because these data structures are typically populated on the GPU via compute shaders, proper synchronization is mandatory:
- UAV Population: The compute shader writes data to the argument buffer (and optional count buffer) using an Unordered Access View (structured or raw/byte-address buffer).
- Resource Transition: Before issuing
ExecuteIndirect, you must insert a resource barrier transitioning both the argument buffer and the count buffer fromD3D12_RESOURCE_STATE_UNORDERED_ACCESStoD3D12_RESOURCE_STATE_INDIRECT_ARGUMENT. - Alignment: Every command sequence inside the
argument buffer must align to a byte boundary divisible by 4, matching
the stride defined in the corresponding
ID3D12CommandSignature.