DirectX 11 vs DirectX 12 Command List Recording

DirectX 11 and DirectX 12 represent fundamentally different architectural philosophies regarding how CPU work is translated into GPU commands. While DirectX 11 uses an abstracted model relying on immediate and deferred contexts managed heavily by the driver, DirectX 12 removes driver intervention to provide low-level, direct control over command generation, allocation, and submission. This article explains the technical differences between how both APIs record command lists, focusing on threading models, memory allocation, pipeline state handling, and command execution.

Architectural Model: Contexts vs. Lists and Allocators

In DirectX 11, command recording revolves around the ID3D11DeviceContext interface. The API provides two context types:

  • Immediate Context: Interacts directly with the GPU driver to record and immediately dispatch draw calls and state changes on the main rendering thread.
  • Deferred Contexts: Intended for secondary threads to record commands into an ID3D11CommandList token stream, which must subsequently be executed on the immediate context.

In contrast, DirectX 12 decouples command recording from command execution entirely. The API introduces three distinct objects:

  • Command Allocators (ID3D12CommandAllocator): Backing storage in system or video memory where recorded commands are written.
  • Command Lists (ID3D12GraphicsCommandList): The recording interface through which commands (draws, dispatches, state updates) are encoded into an associated allocator.
  • Command Queues (ID3D12CommandQueue): The GPU timeline interface to which completed command lists are submitted for hardware execution.

Multithreading and CPU Scalability

DirectX 11 struggles with true multithreaded command generation. Because deferred contexts rely on the driver to manage underlying allocations and state inheritance, driver-level locking is frequently required. In many drivers, deferred command lists are replayed or patched sequentially on the immediate context during submission, which creates CPU bottlenecks and diminishes the performance benefits of multithreaded generation.

DirectX 12 enables lock-free, concurrent command recording. Separate threads can record into separate command lists simultaneously, provided each active command list uses its own command allocator per thread. Because DirectX 12 does not support an immediate context, all recording happens offline relative to the GPU, allowing draw call generation to scale linearly across CPU cores without driver contention.

Pipeline States and Validation Overhead

DirectX 11 records commands using a dynamic state machine model. Programmers alter states independently (e.g., calling RSSetState, OMSetBlendState, or binding shaders individually). Consequently, when a command list is recorded or executed, the graphics driver must validate whether the combination of individual states is valid for the underlying hardware, introducing unpredictable CPU overhead during recording.

DirectX 12 requires states to be pre-baked into immutable Pipeline State Objects (PSOs). A PSO compiles the vertex, pixel, and compute shaders, rasterizer state, blend state, depth-stencil state, and input layout into hardware-ready instructions ahead of time. During command list recording in DirectX 12, changing the pipeline state requires only a single pointer change via SetPipelineState, drastically reducing validation logic and CPU overhead during the recording phase.

Resource Tracking and Hazards

In DirectX 11, the runtime and driver automatically track resource dependencies, hazard detection, and subresource state transitions (such as transitioning a texture from a render target to a shader resource view). This synchronization logic runs invisibly during command recording and execution.

In DirectX 12, resource tracking is entirely explicit. Developers must insert manual resource barriers (ResourceBarrier) into the command list to transition subresources between usage states (e.g., D3D12_RESOURCE_STATE_RENDER_TARGET to D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE). While this places the burden of synchronization and cache flushing on the application, it eliminates the heavy CPU cost of driver-side tracking during recording.

Command Submission and Reuse

DirectX 11 command lists are recorded by calling FinishCommandList on a deferred context, then consumed via ExecuteCommandList on the immediate context, after which the list is typically discarded or preserved as a static set of instructions.

DirectX 12 separates memory recycling from command structures. A closed command list can be immediately submitted to an ID3D12CommandQueue. Once the GPU finishes processing the submitted commands, the application resets the ID3D12CommandAllocator to reclaim memory and calls Reset on the ID3D12GraphicsCommandList to begin recording a new frame, avoiding repeated memory allocations and maximizing throughput.