DirectX 11 Deferred Contexts vs DirectX 12 Multithreading
This article examines DirectX 11 deferred contexts, exploring how they were designed to bring multithreaded command recording to modern games and the technical reasons they failed to deliver meaningful performance improvements. It contrasts this legacy approach with the ground-up architectural redesign of DirectX 12, detailing why modern low-level APIs succeed where DirectX 11 struggled.
What Are Deferred Contexts in DirectX 11?
In DirectX 11, the runtime divides rendering duties between two types of device contexts: the Immediate Context and Deferred Contexts.
The immediate context is tied directly to the driver and the GPU pipeline. It is responsible for executing commands and presenting frames. Because the immediate context is not thread-safe, all traditional draw calls and state changes had to occur sequentially on a single CPU thread (the main render thread).
To utilize multi-core CPUs, Microsoft introduced deferred contexts. A
deferred context allows background worker threads to record rendering
commands into an intermediate structure called an
ID3D11CommandList. Worker threads generate these command
lists concurrently without directly communicating with the GPU. Once
recorded, these command lists are passed back to the immediate context
on the main thread, which calls ExecuteCommandList to play
them back in sequence for GPU submission.
Why Deferred Contexts Failed to Deliver Multithreading Gains
While conceptually promising, deferred contexts rarely yielded significant frame rate improvements and often performed worse than pure single-threaded rendering. Several architectural bottlenecks caused this failure:
1. The Immediate Context Bottleneck
Command lists recorded on deferred contexts could not be submitted directly to hardware queues. They still had to be serialized through the single immediate context on the primary thread. The CPU overhead required to replay, validate, and stitch these command lists together often equaled or exceeded the time saved by generating them across multiple threads.
2. Heavy Driver State Tracking and Validation
DirectX 11 is a high-level API where the graphics driver actively monitors pipeline state, tracks resource hazards, and handles memory residency on the fly. Because the driver had to guarantee state consistency across independently recorded command lists, it performed extensive state validation during playback. This dynamic dependency tracking introduced substantial CPU overhead that negated multithreading benefits.
3. Driver-Level Lock Contention
Because DirectX 11 drivers were not originally architected for concurrency, many GPU vendors implemented deferred contexts using heavy internal locking. When worker threads attempted to allocate memory or reference shared resources simultaneously, they frequently stalled, waiting for internal driver mutexes.
4. Hardware and Driver Disparity
Implementation quality varied widely across hardware vendors. Some vendors wrote optimized driver paths for deferred contexts, while others essentially emulated the feature in software by queuing commands in system memory and executing them on the main thread. Game developers could not rely on consistent performance across different GPU architectures, leading many to abandon the feature entirely.
How DirectX 12 Solved the Problem
DirectX 12 eliminated the centralized immediate context and redesigned the rendering pipeline to align with modern multi-core CPU architectures:
- Independent Command Allocators and Lists: In
DirectX 12,
ID3D12GraphicsCommandListandID3D12CommandAllocatorare completely decoupled from each other. Worker threads can record draw calls with zero driver-level synchronization or locking between threads. - Pipeline State Objects (PSOs): Rather than allowing individual state changes that the driver must validate at runtime, DirectX 12 bakes shaders, blend states, rasterizer states, and render target formats into immutable PSOs ahead of time. This eliminates CPU-side validation overhead during command recording.
- Direct Queue Submission: Once recorded, command
lists are submitted directly to an
ID3D12CommandQueue. The submission model is extremely lightweight, consisting of an array of pointers passed to the GPU driver without re-parsing or re-validating the individual draw calls. - Explicit Developer Control: Hazard tracking, resource state transitions (barriers), and thread synchronization are placed entirely in the hands of the developer. By removing the driver's guessing games, DirectX 12 allows true parallel command generation that scales linearly with available CPU cores.