ID3D12GraphicsCommandList
objects are submitted to command queues, which package a slew of graphics- and compute commands in one go.
The memory required for a command list is allocated by an ID3D12CommandAllocator
. This object represents a linear allocator, where every allocation required by the ID3D12GraphicsCommandList
will be serviced. Every time a command list a command list is reset, it will allocate a block of memory, and if that block is filled up it will request more from the allocator. Resetting the command list will not clean up the command list's memory block. Memory usage from the command allocator will keep piling up until the ID3D12CommandAllocator::Reset
function is called. Upon calling this function, executing ID3D12GraphicsCommandList
s with memory previously allocated by that command allocator is not adviced.
The ID3D12CommandAllocator
is not thread-safe, so you should have one or more ID3D12CommandAllocator
objects per thread involved with populating command lists.
In addition, you might need multiple ID3D12CommandAllocator
objects per thread when dealing with dynamic command lists. This is because the Reset
function can not be called while a command buffer is in flight, and your target would be to keep a command buffer in flight at all times. Therefore, in this example, we will have a single command allocator per frame buffer, although a more efficient scheme can be devised fairly easily.
NOTE: The following two examples are invoked per frame buffer.
result = renderer->device->lpVtbl->CreateCommandAllocator(
renderer->device,
D3D12_COMMAND_LIST_TYPE_DIRECT,
&IID_ID3D12CommandAllocator,
(void**)&renderer->frame[i].commandAllocator
);
if ( !SUCCEEDED ( result ) )
RETURN_ERROR(-1, "CreateCommandAllocator failed (0x%08X)", result );
Here we see the D3D12_COMMAND_LIST_TYPE_DIRECT
we have seen when creating the command queue. Similarly, this means the command allocator is only suited to allocating memory for command lists of this specific type.
Now for the command lists:
ID3D12GraphicsCommandList* commandList = NULL;
result = renderer->device->lpVtbl->CreateCommandList (
renderer->device,
0,
D3D12_COMMAND_LIST_TYPE_DIRECT,
renderer->frame[i].commandAllocator,
NULL,
&IID_ID3D12GraphicsCommandList,
(void**)&commandList
);
if ( !SUCCEEDED(result) )
RETURN_ERROR(-1, "CreateCommandList failed (0x%08X)", result );
renderer->frame[i].commandList = commandList;
result = renderer->frame[i].commandList->lpVtbl->Close ( renderer->frame[i].commandList );
if ( !SUCCEEDED(result) )
RETURN_ERROR(-1, "Close failed (0x%08X)", result );
As you might notice, we are calling Close
on the ID3D12GraphicsCommandList
just after we have created the command list. This is because every command list created by Direct3D 12 is created in an open state. This is also why there is a pipeline state object argument in the call to CreateCommandList
.