Executing Command Lists and Draw Bundles

Efficient execution of command lists and draw bundles includes wrapping ExecuteCommandList calls within BeginCommandListExecution and EndCommandListExecution calls; saving and restoring state; profiling command lists with INSTRUMENTED but checking RETAIL performance; and tuning your XMemAlloc allocator.

Wrapping ExecuteCommandList calls in BeginCommandListExecution and EndCommandListExecution

To enable optimization of executing command lists, you can wrap ExecuteCommandList calls within calls to ID3D11DeviceContext::BeginCommandListExecution and ID3D11DeviceContext::EndCommandListExecution. The method ID3D11DeviceContext::ExecuteCommandList performs a number of overhead functions that can detract from performance if the method is called repeatedly. To improve the performance of a series of calls to ExecuteCommandList, wrap the calls between the methods ID3D11DeviceContext::BeginCommandListExecution and ID3D11DeviceContext::EndCommandListExecution. The Begin and End command list execution methods perform the necessary overhead once, and reduce the overhead of the ExecuteCommandList calls.

If this process is used, then the RestoreContextState boolean parameter of ExecuteCommandList must be set to false on each call.

Saving and restoring state

The Flags parameter of BeginCommandListExecution should currently be set to zero, which simply indicates that the default functionality should be followed for the saving and restoring of state.

Alternatively, the Constant Engine RAM save/restore APIs (ID3D11DeviceContextX::StoreConstantRam and ID3D11DeviceContextX::LoadConstantRam) may be used to save and restore resource binding state. For example, resource views set on an immediate context may be saved before the execution of a command list and restored after execution. Without these calls, after execution of a command list, the given state is reset to defaults.

D3D11_BUFFER_DESC bufferDesc = {0};

bufferDesc.ByteWidth = D3D11X_CERAM_OFFSET_LIMIT;
bufferDesc.Usage = D3D11_USAGE_DEFAULT;
bufferDesc.BindFlags = 0;
bufferDesc.CPUAccessFlags = 0;
bufferDesc.MiscFlags = 0;
bufferDesc.StructureByteStride = 0;

hr = pDevice->CreateBuffer(&bufferDesc, nullptr, &pConstantRamBuffer);
if (FAILED(hr))
{
  return hr;
}

...

// Save constant RAM

pContext->StoreConstantRam(0,
                           pConstantRamBuffer,
                           0,
                           0,
                           D3D11X_CERAM_OFFSET_LIMIT);

// Bind resources and perform rendering, modifying constant ram

...

// Restore constant RAM

pContext->LoadConstantRam(0,
                          pConstantRamBuffer,
                          0,
                          0,
                          D3D11X_CERAM_OFFSET_LIMIT);  

Draw Bundles

Draw Bundles are objects that are similar to Direct3D 11 Command Lists. They encapsulate a collection of state setting and draw calls, are recorded using a Deferred Context, and are a means of significantly reducing CPU overhead for object rendering. For more information, see Draw Bundles.

Profiling command lists with INSTRUMENTED but checking RETAIL performance

If you use Command Lists and you profile with the INSTRUMENTED version of the Monolithic Direct3D libraries, double-check performance occasionally with the RETAIL version of the libraries. PIX currently adds measurable record-time overhead to Command Lists with INSTRUMENTED builds.

Tuning your XMemAlloc allocator

You can eliminate considerable CPU overhead by tuning your XMemAlloc allocator (or the default system XMemAllocDefault allocator) to never call the kernel to allocate (or free) pages when your game is in its steady state. This is particularly important for any titles that use Command Lists as those do graphics memory allocations every time a Command List is recorded. If you’re using the default system XMemAllocDefault allocator, you can query statistics by calling XMemGetAllocationStatistics. Make sure that the dwAllocationCount and dwFreeCount fields of the XMEM_HEAP_STATISTICS Structure structure do not increment during game play. If they do increment, you can call XMemSetAllocationHysteresis to tune the size hysteresis so that this doesn’t happen.

You can override the default XMemAlloc by hooking up your own allocator through XMemSetAllocationHooks.

See also

DirectX