Root Descriptors vs Descriptor Tables in DirectX 12

In DirectX 12, binding resources efficiently is critical to eliminating pipeline stalls and maximizing both CPU and GPU throughput. This article examines the performance overhead differences between root descriptor tables and direct root descriptors, focusing on levels of memory indirection, root signature space consumption, CPU update overhead, and GPU cache behavior.

Levels of Indirection and Latency

The primary performance difference between the two approaches lies in how the GPU resolves resource locations:

  • Direct Root Descriptors: Direct root descriptors (also known as root views) store a raw 64-bit GPU virtual address directly inside the root signature. When a shader accesses a resource bound this way, it encounters zero levels of descriptor indirection; the GPU reads the memory address straight from root constant memory and immediately fetches the resource data.
  • Root Descriptor Tables: Descriptor tables store a single 32-bit offset/pointer inside the root signature that references a location in a descriptor heap. When a shader accesses a resource through a table, it requires one level of indirection: the GPU must first fetch the descriptor from the descriptor heap in memory before it can read the actual resource data. This extra memory read can increase memory latency if the relevant descriptor cache misses.

Root Signature Footprint and Hardware Spilling

DirectX 12 root signatures have a hardware limit of 64 DWORDs. How this space is consumed directly impacts performance:

  • Root Descriptors: Each direct root descriptor consumes 2 DWORDs (a 64-bit GPU virtual address). While they eliminate indirection, packing too many direct descriptors into a root signature quickly exhausts the 64-DWORD budget. On many GPU architectures, large root signatures exceed dedicated on-chip register space and spill into driver-managed local memory, introducing memory latency across all root parameters.
  • Descriptor Tables: A descriptor table consumes only 1 DWORD in the root signature, regardless of how many descriptors the table encompasses. This makes tables exceptionally efficient for binding dozens or hundreds of resources while keeping the root signature small enough to reside entirely in high-speed on-chip cache.

CPU Update Overhead

The CPU cost of changing resource bindings differs based on frequency and batching:

  • Root Descriptors: Changing a direct root descriptor requires calling an API method like SetGraphicsRootConstantBufferView. The CPU cost is minimal—essentially writing a 64-bit value to a command buffer. However, updating multiple root descriptors individually per draw call causes root signature state churn and can bloat the command list.
  • Descriptor Tables: Changing a table binding requires updating the table pointer via SetGraphicsRootDescriptorTable. While the bind call itself is lightweight (writing a single DWORD), updating the underlying descriptors requires pre-baking or copying descriptors into the heap (CopyDescriptorsSimple or CPU-side descriptor staging). This makes tables more CPU-intensive to construct dynamically, but very fast to bind when batched across frames or draw calls.

Functional Limitations

Direct root descriptors are limited by the API:

  • Direct root descriptors can only be used for Constant Buffer Views (CBVs), Raw/Structured Shader Resource Views (SRVs), and Unordered Access Views (UAVs).
  • Direct root descriptors do not support textures (2D, 3D, Cube) or Samplers.
  • Any pipeline stage requiring texture sampling or sampler states must rely on descriptor tables.

Optimization Guidelines

  • Use Direct Root Descriptors for: Frequently changing, latency-sensitive buffers, such as per-draw or per-object constant buffers (matrices, world states) where the indirection penalty would hurt shader execution.
  • Use Root Descriptor Tables for: Textures, samplers, large sets of resources, infrequently changed frame-global resources, and bindless-style array architectures where preserving the 64-DWORD root signature limit is essential.