Getting Started
First-Time Setup
The first thing you’ll need is a version of the Microsoft DirectX® shader compiler and Agility SDK runtime which supports GPU Work Graphs. These can be acquired directly from Microsoft, here. You will also need a graphics card which supports GPU Work Graphs (an AMD Radeon™ RX 7000 Series Graphics Cards) and an AMD driver which supports GPU Work Graphs, which can be acquired here.
Hello Work Graphs
Note: This example is intended as an introduction to Work Graphs for people already familiar with DirectX 12. If you are not already comfortable with DirectX 12, you may wish to learn more before continuing!
A minimal C++ sample is available which implements the basic “Hello World” paradigm for Work Graphs. You can find it here. This program also goes through the process of setting up DirectX and outputting the results, so the fundamental components of exclusively setting up the work graph itself are outlined in the following sections.
Declaring a Work Graph node
Create an HLSL source which contains at least one Work Graph Node. Note that there are many new HLSL intrinsics and attributes that can be involved with this declaration. Many of these options are mutually exclusive, and this example does not attempt to show them all!
Configuring DirectX 12
Configure the application with a Work Graph-compatible version of the Agility SDK.
Validating Work Graphs are supported
Ensure the current DirectX 12 stack supports Work Graphs before you continue.
Compiling a Work Graphs Library
Compile your work graph source like you would a traditional shader. Use nullptr
as the entry point and lib_6_8
as the target profile.
Creating the State Object
Create a state object populated with a CD3DX12_DXIL_LIBRARY_SUBOBJECT
containing the compiled shader source, and a CD3DX12_WORK_GRAPH_SUBOBJECT
containing the target node and a name.
Note
Even if you only have a single Work Graph Subobject, you still want to specify a name to lookup the corresponding Program Identifier with later.
When populating the
CD3DX12_WORK_GRAPH_SUBOBJECT
, we invokeIncludeAllAvailableNodes()
. This simplifies some setup, and we’ll come back to that later.
CD3DX12_STATE_OBJECT_DESC Desc(D3D12_STATE_OBJECT_TYPE_EXECUTABLE);
...
CD3DX12_DXIL_LIBRARY_SUBOBJECT* LibraryDesc = Desc.CreateSubobject<CD3DX12_DXIL_LIBRARY_SUBOBJECT>();
CD3DX12_SHADER_BYTECODE gwgLibraryCode(pGwgLibrary);
LibraryDesc->SetDXILLibrary(&gwgLibraryCode);
CD3DX12_WORK_GRAPH_SUBOBJECT* WorkGraphDesc = Desc.CreateSubobject<CD3DX12_WORK_GRAPH_SUBOBJECT>();
WorkGraphDesc->IncludeAllAvailableNodes();
WorkGraphDesc->SetProgramName(kProgramName);
pDevice->CreateStateObject(Desc, IID_PPV_ARGS(&pStateObject));
Preparing D3D12 Descriptions
Allocate Backing Memory for the Work Graph program to use.
UINT WGIndex = pWorkGraphProperties->GetWorkGraphIndex(kProgramName);
D3D12_WORK_GRAPH_MEMORY_REQUIREMENTS MemoryRequirements = {};
pWorkGraphProperties->GetWorkGraphMemoryRequirements(WGIndex, &MemoryRequirements);
if (MemoryRequirements.MaxSizeInBytes > 0)
{
pBackingMemoryResource = AllocateBuffer(pDevice, MemoryRequirements.MaxSizeInBytes, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_HEAP_TYPE_DEFAULT);
}
Create a D3D12_SET_PROGRAM_DESC
encapsulating the state object.
Note: The flag
D3D12_SET_WORK_GRAPH_FLAG_INITIALIZE
needs to be set at least once per unique Program, but using it comes with performance costs. Don’t specify it during every Dispatch on Work Graphs that will run more than once!
Note: We recommend setting the value to
MaxSizeInBytes
to ensure the optimal performance.
D3D12_SET_PROGRAM_DESC Desc = {};
Desc.Type = D3D12_PROGRAM_TYPE_WORK_GRAPH;
Desc.WorkGraph.ProgramIdentifier = pStateObjectProperties->GetProgramIdentifier(kProgramName);
Desc.WorkGraph.Flags = D3D12_SET_WORK_GRAPH_FLAG_INITIALIZE;
if (pBackingMemoryResource)
{
Desc.WorkGraph.BackingMemory = { pBackingMemoryResource->GetGPUVirtualAddress(), MemoryRequirements.MaxSizeInBytes };
}
Finally, create a D3D12_DISPATCH_GRAPH_DESC
encapsulating inputs for the dispatch.
Note
Even if the graph takes no Input Records, you must specify
NumRecords
to be greater than 0 or no dispatches will occur. If you’re not sure what this means, we’ll come back to it later!In this example, our compiled Work Graph source only contains a single node and that node is included as an Entrypoint to the graph (because we called
IncludeAllAvailableNodes()
while creating the State Object). This means we can safely assume there is exactly one possible Entrypoint, and that Entrypoint must be at index 0.
Invoking DispatchGraph
Place commands on an active command list to set the Program and dispatch the Work Graph. If inserting onto an existing command list, you will need to query the ID3D12GraphicsCommandListExperimental
interface to access these commands.