DirectX 12 Root Signatures and Resource Binding
This article provides an overview of root signatures in DirectX 12, detailing their role as the binding contract between application code and GPU shaders. It explores the core components of a root signature—including descriptor tables, root descriptors, and root constants—and explains how these elements map resources such as constant buffers, textures, and samplers to shader input registers.
What Is a Root Signature?
In DirectX 12, a root signature functions like a function signature in C++. Just as a function signature defines the types, order, and count of parameters a function expects, a root signature defines the types, layout, and memory locations of the resources that pipeline shaders expect to access.
DirectX 12 decouples shader resource binding from pipeline state objects (PSOs). Shaders do not directly reference memory or hardware descriptor slots at a driver level. Instead, the application defines a root signature, validates it against target shaders, and binds data matching that layout at runtime.
Root Signature Components
A root signature consists of a sequence of root parameters. DirectX 12 provides a maximum total size of 64 DWORDs (256 bytes) for all parameters in a single root signature to ensure the configuration fits within fast, on-chip GPU memory. There are three types of root parameters, each trading flexibility for access speed.
1. Root Constants
- Cost: 1 DWORD per 32-bit value.
- Mechanism: Data is stored directly inside the root signature without any pointer indirection.
- Use Case: Small, frequently changing variables such as transform matrices, world coordinates, or material flags. They bypass descriptor heaps entirely, providing the lowest latency memory access for shaders.
2. Root Descriptors (Inline Descriptors)
- Cost: 2 DWORDs per descriptor (equivalent to a 64-bit GPU virtual address).
- Mechanism: Directly contains the GPU virtual address pointing to the resource in memory, bypassing the descriptor heap lookup.
- Use Case: Frequently accessed resources that change on a per-draw or per-pass basis, typically limited to Constant Buffer Views (CBVs) and raw/structured Buffer Shader Resource Views (SRVs) or Unordered Access Views (UAVs). Texture SRVs generally cannot be bound this way on most hardware.
3. Descriptor Tables
- Cost: 1 DWORD per table.
- Mechanism: A descriptor table identifies a contiguous range of descriptors stored inside an external descriptor heap.
- Use Case: Binding large arrays or complex groups of resources, such as multiple textures, samplers, and structured buffers. Because they incur a level of indirection (the GPU reads the table pointer, then accesses the heap, and finally reads the resource data), they are slightly slower than root constants or root descriptors, but they allow massive resource sets to be bound using minimal root signature space.
Static Samplers
Root signatures also support static samplers, which define texture filtering and addressing modes directly inside the root signature. Static samplers cost zero DWORDs against the 64 DWORD limit and do not require space in descriptor heaps, making them the preferred way to bind standard sampling states.
How Root Signatures Define Resource Bindings
The root signature acts as the bridge connecting your high-level
application memory structures to Low-Level High-Level Shading Language
(HLSL) registers (bX, tX, uX,
sX).
- Definition and Compilation: The layout is defined
using either the
D3D12_ROOT_SIGNATURE_DESCC++ structure or directly within HLSL code using the[RootSignature(...)]attribute. It is then serialized into binary format viaD3D12SerializeRootSignatureand created on the device as anID3D12RootSignatureobject. - PSO Association: When creating a Pipeline State Object, the root signature is supplied. The DirectX 12 runtime verifies that the bytecode of the vertex, pixel, or compute shaders uses registers compatible with the root signature’s layout.
- Runtime Binding: Before issuing draw or dispatch
calls through a command list, the root signature is set via
SetGraphicsRootSignatureorSetComputeRootSignature. - Parameter Population: The command list populates
each parameter slot defined in the signature:
SetGraphicsRoot32BitConstantswrites direct values to root constant slots.SetGraphicsRootConstantBufferViewpoints an inline descriptor slot to a GPU memory address.SetGraphicsRootDescriptorTablepoints a descriptor table slot to a handle in an active descriptor heap.
Once bound, the GPU maps the incoming parameters directly to the corresponding HLSL register ranges defined in the signature, enabling shader execution with deterministic access to graphics memory.