DirectX 12 Work Graphs Input Records Explained

DirectX 12 Work Graphs represent a major shift toward GPU-driven pipelines, allowing the GPU to generate, schedule, and execute work autonomously without CPU intervention. At the core of this architecture are input records, which serve as the communication payload between producer and consumer nodes. This article details what input records are, how they are defined, and the mechanisms through which DirectX 12 automatically manages memory for dynamic sub-tasks to eliminate the overhead of traditional compute dispatches.

What Are Input Records in Work Graphs?

In a DirectX 12 Work Graph, execution is organized into a directed graph of compute nodes. An input record is a user-defined data structure in HLSL that acts as the parameters or execution context passed from one node (the producer) to another (the consumer).

In traditional compute pipelines, passing intermediate data between interdependent dispatches requires allocating static Unordered Access Views (UAVs) in global memory and issuing indirect dispatch calls from the CPU or via ExecuteIndirect. Work Graphs replace this pattern. Instead of writing out full arrays to global VRAM, a producer node calls dedicated HLSL intrinsics to emit a record. This record is routed directly to the target node's input, triggering subsequent execution stages automatically.

The format of an input record is defined purely in shader code as a standard struct. Depending on the consumer node's launch mode, the input record behaves differently:

  • Broadcasting Launch: The input record is shared across an entire threadgroup. All threads in the dispatched group read from the same record data.
  • Thread Launch: Each emitted record maps to a single thread execution, ideal for fine-grained scalar operations.
  • Coalescing Launch: The runtime aggregates multiple records emitted across different producers into an array of records processed together by a single threadgroup, enabling high-efficiency batching.

Dynamic Memory Allocation via the Backing Store

Managing memory for dynamic sub-tasks has historically been difficult on the GPU because workload expansion is non-deterministic. Developers previously had to allocate "worst-case scenario" memory buffers to avoid out-of-memory faults during high-divergence tasks, wasting significant amounts of VRAM.

DirectX 12 Work Graphs solve this through a runtime- and driver-managed backing store (scratch buffer). When creating a Work Graph state object, the application queries the memory requirements via ID3D12StateObjectProperties1::GetWorkGraphMemoryRequirements and provides a single backing buffer. The internal hardware and driver schedule allocations dynamically inside this pool.

When dynamic sub-tasks are created:

  1. Explicit Allocation Requests: A producer node calculates how many child tasks it needs to spawn based on dynamic runtime data (such as occlusion culling or LOD evaluation). It requests memory for new records using intrinsics like AllocateNodeOutputRecords.
  2. Hardware-Managed Pointers: The driver handles the record pointer allocation inside fast on-chip caches or the backing store. If the requested records fit into transient fast memory or hardware-managed ring buffers, they never touch external VRAM.
  3. Producer Population: Once allocated, the shader populates the record data directly through the retrieved record handle.
  4. Immediate Retirement: When the output record is complete, the runtime handles passing it to the target node.

Memory Recycling and Dynamic Lifetime

Memory management for input records relies on short-lived lifecycles. Traditional UAV pipelines keep transient buffers alive for the duration of a frame or pass. In contrast, the backing store of a Work Graph functions as a transient dynamic pool:

  • Consumption and Deallocation: As consumer nodes read their input records and complete their threadgroups, the memory backing those records is marked as free immediately.
  • Buffer Compaction and Reuse: The driver recycles that memory for subsequent node allocations within the same graph execution. This cyclic reuse prevents memory bloat even if a graph spawns millions of sub-tasks across multiple hierarchical levels.
  • Empty Work Avoidance: If a producer node determines that no sub-task is required (for example, geometry is outside the camera frustum), it simply does not call the allocation intrinsic. Zero memory is reserved, and no downstream threads are dispatched.

By integrating memory allocation directly into shader execution, input records transform inter-shader communication from a manual, worst-case memory planning problem into an automated, cache-coherent streaming system.