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 ID3D12Device instances 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:

  1. Cross-Adapter Heaps: Developers create shared heaps using the D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER flag. These allocations must reside in system memory (RAM) rather than local GPU VRAM to ensure bidirectional access.
  2. 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.
  3. 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.