DirectX 12 GPU-Based Validation Explained
GPU-Based Validation (GBV) is an advanced debugging feature in DirectX 12 that catches pipeline errors that the standard CPU-side debug layer cannot detect. While the CPU-side debug layer tracks API calls and resource states on the host, it cannot observe dynamic operations that occur strictly on the GPU timeline. GBV bridges this gap by instrumenting shader code and injecting runtime checks directly into GPU workloads, making it possible to identify out-of-bounds descriptor accesses, uninitialized resource usage, and resource state mismatches during execution.
What Is GPU-Based Validation?
In DirectX 12, developers manage memory, synchronization, and resource binding explicitly. Standard validation runs on the CPU during API recording, validating parameters passed to command lists. However, shaders often compute memory offsets, resource indices, and descriptor table locations dynamically at runtime.
Because the CPU cannot evaluate dynamically calculated GPU operations, typical CPU validation cannot catch issues like a shader indexing past the end of a descriptor table. GBV solves this by moving verification code onto the GPU itself, ensuring operations are safe as they execute on the hardware.
How GBV Detects Invalid Shader Descriptors
A shader descriptor is an object that describes a resource (such as a Texture2D, Buffer, or Sampler) to the GPU, organized inside descriptor heaps. Shaders read descriptors using descriptor tables or direct heap indexing. Detecting invalid descriptors at runtime relies on a multi-step process:
1. Shader Bytecode Instrumentation
When GBV is enabled via the DirectX 12 debug layer, the runtime intercepts the compiled shader bytecode (DXBC or DXIL) before it is submitted to the GPU driver. GBV instruments the code by injecting validation instructions immediately preceding any resource access operation (such as texture loads, sample operations, or unordered access view writes).
2. Runtime Bounds Checking
When a shader attempts to dynamically index into a descriptor table or heap, the injected validation code executes first. It compares the requested index against the actual allocation limits of the bound descriptor heap and root signature ranges:
- If the index exceeds the size of the descriptor table or descriptor heap, the access is flagged as out-of-bounds.
- If the index references unallocated space, it is caught before an
illegal memory read can trigger a device removal
(
DXGI_ERROR_DEVICE_REMOVED) or GPU hang.
3. Descriptor State and Type Verification
GBV checks whether the descriptor being accessed is valid and compatible with the operation:
- Initialization: It verifies that the descriptor slot contains an initialized descriptor rather than uninitialized memory.
- Type Matching: It verifies that the descriptor type matches the shader instruction (for example, ensuring a Constant Buffer View is not being read where a Shader Resource View is expected).
- Resource Liveness: It checks if the underlying resource pointed to by the descriptor is still resident and has not been destroyed.
4. Diagnostic Logging and Fault Reporting
When an invalid descriptor access occurs:
- The injected shader logic writes an error record into a dedicated, internal GPU validation buffer.
- The faulty access can either be clamped or redirected to dummy memory to prevent a catastrophic hardware crash.
- Upon command list completion or at synchronization points (fences), the CPU-side debug layer reads the validation buffer.
- The DirectX 12 runtime outputs a detailed error message to the debug output window, identifying the exact draw or dispatch call, the root parameter index, the invalid descriptor index, and the shader instruction that caused the failure.