DirectX 12 Heterogeneous Multi-Adapter Support
DirectX 12 provides low-level explicit multi-adapter capabilities that allow applications to harness the compute and graphics power of fundamentally different GPUs simultaneously. This article explains how heterogeneous multi-adapter configurations function under the API, covering the unlinked adapter architecture, memory sharing via cross-adapter heaps, synchronization with shared fences, and practical task-distribution techniques.
Implicit vs. Explicit Multi-GPU
Traditional multi-GPU solutions such as NVIDIA SLI and AMD CrossFire relied on implicit hardware configurations. The graphics driver handled frame synchronization—usually via Alternate Frame Rendering (AFR)—and required paired GPUs to be nearly identical.
DirectX 12 eliminates implicit driver reliance by introducing explicit multi-adapter support. Under this paradigm, the game engine directly manages hardware detection, memory allocation, workload scheduling, and inter-GPU synchronization.
Unlinked Multi-Adapter Architecture
DirectX 12 categorizes multi-adapter setups into two distinct models:
- Linked Display Adapters (LDA): Manages GPUs
connected via hardware links or identical architectures using a single
logical
ID3D12Device. - Unlinked Adapters: Exposes completely separate
ID3D12Deviceinstances for every physical GPU present in the system, even if they feature different architectures, power envelopes, or vendors (such as pairing an Intel integrated GPU with an NVIDIA or AMD discrete GPU).
In an unlinked configuration, each GPU operates as an isolated ecosystem. Each adapter maintains its own distinct command queues, memory pools, command allocators, and pipeline states.
Cross-Adapter Resource Sharing
Because unlinked GPUs do not share physical or virtual memory spaces, DirectX 12 utilizes shared resources to facilitate communication across the PCIe bus:
- Cross-Adapter Heaps: Developers create shared heaps
using the
D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTERflag. These allocations must reside in system memory (RAM) rather than local GPU VRAM to ensure bidirectional access. - Resource Limitations: Textures allocated in cross-adapter heaps are restricted to standard linear, row-major formats. Swizzled (tiled) layouts specific to one hardware architecture are unsupported because the secondary GPU cannot natively parse proprietary layouts from another vendor.
- Data Marshaling: A typical workflow involves the
rendering GPU copying data from its local VRAM to a shared cross-adapter
heap via
CopyTextureRegion, followed by the secondary GPU reading the data out of the shared heap into its own local memory.
Inter-GPU Synchronization
DirectX 12 implements cross-adapter synchronization through shared
fences (ID3D12Fence created with
D3D12_FENCE_FLAG_SHARED):
- The producing GPU signals the fence when it finishes writing to a cross-adapter resource.
- The consuming GPU inserts a wait command into its own command queue tied to that shared fence handle.
- Work on the consuming GPU remains paused until the producing GPU signals completion, preventing race conditions without requiring CPU-side synchronization.
Workload Splitting Strategies
Because PCIe bandwidth and varying GPU execution speeds create latency bottlenecks, heterogeneous setups are unsuited for strict Alternate Frame Rendering (AFR). Instead, developers use asymmetric task distribution:
- Post-Processing Offloading: The high-performance discrete GPU renders the primary scene geometry, lighting, and materials, then streams the render target to an integrated GPU to execute post-processing passes (temporal anti-aliasing, color grading, bloom, and UI composition).
- Asynchronous Compute: The secondary GPU calculates compute-bound tasks that are not strictly latency-sensitive for the current frame, such as physics simulations, particle updates, pathfinding, or ambient occlusion maps.
- Shadow Map Generation: The auxiliary GPU can generate static or dynamic shadow depth maps asynchronously, allowing the primary GPU to focus exclusively on view-dependent shading.