Draw Bundles

Draw bundles are objects similar to Direct3D 11 Command Lists. Draw bundles encapsulate a collection of state setting and draw calls, are recorded using a Deferred Context, and are a way to significantly reduce CPU overhead for object rendering.

Draw bundles overview

Draw bundles are a means of significantly reducing CPU overhead for object rendering in comparison to either using Direct3D Immediate Context rendering or Direct3D 11 Command Lists.

Draw bundles provide no significant GPU performance benefit or detriment relative to Immediate Context rendering, they are intended to be used in the same way that the majority of Xbox 360 titles used Command Buffers. The Xbox One hardware allows a cleaner API model than was the case for the Xbox 360.

Draw bundles encapsulate a collection of state setting and draw calls, are recorded using a Deferred Context, and are typically executed from an Immediate Context. They are objects that are similar to Direct3D 11 Command Lists, but not identical, and differ in the following respects:

Recording a draw bundle

Draw bundles are created by recording state and draw calls using special Deferred Contexts. Such a Deferred Context must be created by passing the D3D11_CREATE_DEFERRED_CONTEXT_DRAW_BUNDLES flag to ID3D11Device::CreateDeferredContext. The resulting Deferred Context may be used only for draw bundle recording.

ID3D11DeviceContext::FinishCommandList is used with such a Deferred Context to create the resulting Command List which represents the draw bundle. FALSE should always be specified for the RestoreDeferredContextState parameter when recording a draw bundle.

ID3D11DeviceContext::ExecuteCommandList is used to execute the Command List which represents the draw bundle. FALSE should always be specified for the RestoreContextState parameter when executing a draw bundle. ExecuteCommandList can continue to be used for executing true Command Lists in addition to draw bundles.

The following table lists methods that must be recorded into a draw bundle.

Direct3D API Method Implementation Notes
SetShader methods: The shader for each stage must always be explicitly set while recording a draw bundle. The current implementation of draw bundles burns all of the state setting associated with shaders into the bundle.
OMSetRenderTargetsOMSetRenderTargets must be called before the first draw call for a draw bundle, and should only be called once per draw bundle. Only the pixel formats resulting from this call are recorded into the bundle. That is, state such as the location and dimensions of the render targets and fast-Clear/compression/Hi-Stencil/Hi-Z configurations are not recorded and so are inherited at execution time. However, the invoker of the draw bundle guarantees that the render targets set at execution time are the same number and have the same formats as those at record time.
IASetIndexBufferThis is needed only if recording indexed draws. The index buffer is explicitly bound into the bundle and cannot be inherited.
IASetInputLayoutThe input layout is explicitly bound into the bundle and cannot be inherited.
IASetPrimitiveTopologyThe topplogy is explicitly bound into the bundle and cannot be inherited.

The following methods can be used to record the drawing into a draw bundle. Draw Indirect methods are now supported. Multiple draw calls may be recorded into a single draw bundle, subject to the encapsulated state size constraint.

Direct3D API Method
Dispatch
Draw
DrawAuto
DrawIndexed
DrawIndexedInstanced
DrawInstanced
DrawIndexedInstancedIndirect
DrawInstancedIndirect

The following methods allow draw bundles to have a much cleaner API model for inheritance and state encapsulation than was the case for Xbox 360 Command Buffers. When any of the following state is set while recording a draw bundle, that state will be encapsulated into the draw bundle. When a shader references any state represented by the following methods that are not encapsulated into the bundle, the inherited state that is set at execution time will be used instead.

Direct3D API Method Implementation Notes
SetConstantBuffers methods: Refer to the Encapsulation Limitations section below.
SetConstantBuffers1 methods: Refer to the Encapsulation Limitations section below.
SetSamplers methods:  
SetShaderResources methods: Refer to the Encapsulation Limitations section below.
IASetVertexBuffersRefer to the Encapsulation Limitations section below.

This encapsulation is done at a slot granularity. For example, if PSSetShaderResources(1, 1, &pResource) is called during recording but the pixel shader references both slot 0 and slot 1, the inherited state for slot 0 will be used and the encapsulated state for slot 1 will be used.

After a slot has been overridden and encapsulated into the draw bundle for a Draw call, that slot cannot subsequently revert to inherited state within the same draw bundle. That is, if slot #1 is encapsulated before Draw #1, then Draw #2 cannot refer to slot #1 and expect it to be inherited. However, it is fully permissible to reassign slot #1 to a different encapsulated state before invoking Draw #2.

A slot is overridden even if set to NULL; that is, PSSetShaderResource(1, 1, nullptr) will still count as an encapsulated resource.

Invokers of draw bundles do not have to clean up inherited state after execution - that is handled at no cost by the hardware.

Encapsulation Limitations

A draw bundle cannot contain any Shared Resource Views on depth-stencil or color render targets, nor can it contain any Dynamic Resources. Note though that Dynamic Resources and Shared Resource Views can be inherited.

Invalid components of a draw bundle

The following state will always be inherited at execution time and as such these methods cannot be recorded into the draw bundle.

Direct3D API Method
OMSetBlendState
OMSetDepthStencilState
RSSetState
RSSetScissorRects
RSSetViewports
SetPredication

In addition, a draw bundle cannot contain any of the following methods:

 
Direct3D API Method
Begin
ClearDepthStencilView
ClearRenderTargetView
ClearState
ClearView
ClearUnorderedAccessViewFloat
ClearUnorderedAccessViewUint
CopyResource
CopyStructureCount
CopySubresourceRegion
CopySubresourceRegion1
CSSetUnorderedAccessViews
DiscardView
DiscardView1
DispatchIndirect
DispatchIndirect
End
ExecuteCommandList
FinishCommandList
Flush
GenerateMips
Map
OMSetRenderTargetsAndUnorderedAccessViews
PIXBeginEvent
PIXEndEvent
PIXSetMarker
ResolveSubresource
SetResourceMinLOD
SOSetTargets
Unmap
UpdateSubresource
UpdateSubresource1

Draw bundle restrictions

Draw bundles are designed for a small collection of draw calls (recommended numbers are between 3 and 5 calls). There is a fixed hardware-imposed limit on how much aggregate state can be encapsulated within one bundle. The limit is that an aggregate of about 6K of accumulated slot overrides are available for encapsulation. The size limits are given in the following table.

Resource Size in bytes
Shader Resource64
Constant Buffer32
Vertex Buffer32
Sampler32

This 6K limit applies to the accumulated set of overridden resource slots for the duration of the Command List. In other words, updating Shader Resource slot 0 to a different encapsulated resource before every Draw call in a Command List with 10 Draws will only contribute 64 bytes of memory to this total. In contrast, a Command List with only one Draw that sets Shader Resource slots 0 through 9 will contribute 640 bytes of memory to this total.

The invoker of a draw bundle must ensure that the current render targets match the formats that were recorded.

PIX treats draw bundles as Command Lists with the D3D11_CREATE_DEFERRED_CONTEXT_LONG_LIFE_COMMAND_LISTS flag always implied, as draw bundles are intended to be long-life.

The recording of draw bundles has less CPU setup overhead than Command Lists because bundles do not have to reset as much state, but bundles do have higher per-draw overhead.

The first call of draw bundle will always roll the context, and the first call following a draw bundle will also roll the context.

All shaders that use stream-out are cannot be in draw bundles.

Remapping slots for multiple draws

It is useful to record more than one draw into a draw bundle as a means of further reducing the CPU and GPU cost of draw bundle invocation. However, a complication occurs when successive draw calls want different inherited state at the same slot. For this reason, the following slot remapping methods are available:

These methods allow a slot to be remapped to a different inherited slot between draw calls. This remapping is completely free from a CPU and GPU perspective because it is simply exposing control over the slot and register renaming that draw bundles do internally as part of the creation process. For example, if you have 4 draws in your bundle, and they all use the same shader that always reads from slot 0, and you want to inherit different resources at slot 0 for every draw, then before calling ExecuteCommandList you could load slots 0, 10, 11, and 15. Your draw bundle would be recorded as follows:

Draw();     // Draw's shader reference to slot 0 comes directly from inherited slot 0
RemapConstantBufferInheritance(D3D11_STAGE_VS, 0, D3D11_STAGE_VS, 10);
Draw();     // Draw's shader reference to slot 0 now comes from inherited slot 10
RemapConstantBufferInheritance(D3D11_STAGE_VS, 0, D3D11_STAGE_VS, 11);
Draw();     // Draw's shader reference to slot 0 now comes from inherited slot 11
RemapConstantBufferInheritance(D3D11_STAGE_VS, 0, D3D11_STAGE_VS, 15);
Draw();     // Draw's shader reference to slot 0 now comes from inherited slot 15  

The parameters InheritStage and InheritSlot are always relative to the slots inherited at execution time. Consequently, it’s not possible to chain remaps by remapping a slot to another slot that is itself already remapped. The following methods can allow an overridden slot to be un-overridden:

VSSetConstantBuffer(0, 1, &pConstantBuffer); // Overrides slot 0 with encapsulated state
Draw();
RemapConstantBufferInheritance(D3D11_STAGE_VS, 0, D3D11_STAGE_VS, 0); // Reset slot 0 back to the original inherited state
Draw();  

It’s permissible for these methods to remap a resource across stages. For example, a pixel shader can reference a shader constant that is bound into the vertex shader stage. This can also be used to allow more resources to be inherited by a draw bundle, and allowing additional stages to store the inherited state. For example, the constant buffer for a pixel shader can be stored in the hull and domain shader stages. Note that during runtime, setting shader resources on one stage and then remapping onto other stages won’t be faster than setting the corresponding resources separately for each stage.