Home » Blogs » GPU Work Graphs » Getting Started

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!

Copied!

[Shader("node")]
[NodeLaunch("broadcasting")]
[NodeDispatchGrid(1, 1, 1)]
[NumThreads(1, 1, 1)]
void BroadcastNode()
{
    ...
}

Configuring DirectX 12

Configure the application with a Work Graph-compatible version of the Agility SDK.

Copied!

extern "C" { __declspec(dllexport) extern const UINT D3D12SDKVersion = 613; } // or later

Validating Work Graphs are supported

Ensure the current DirectX 12 stack supports Work Graphs before you continue.

Copied!

D3D12_FEATURE_DATA_D3D12_OPTIONS21 Options = {};
pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS21, &Options, sizeof(Options));

return (Options.WorkGraphsTier != D3D12_WORK_GRAPHS_TIER_NOT_SUPPORTED);

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.

Copied!

pCompiler->Compile(pSource, nullptr, nullptr, L"lib_6_8", nullptr, 0, nullptr, 0, nullptr, &pOperationResult)

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 invoke IncludeAllAvailableNodes(). This simplifies some setup, and we’ll come back to that later.

Copied!

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.

Copied!

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.

Copied!

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.

Copied!

D3D12_DISPATCH_GRAPH_DESC DispatchGraphDesc = { };
DispatchGraphDesc.Mode = D3D12_DISPATCH_MODE_NODE_CPU_INPUT;
DispatchGraphDesc.NodeCPUInput = { };
DispatchGraphDesc.NodeCPUInput.EntrypointIndex = 0;
DispatchGraphDesc.NodeCPUInput.NumRecords = 1;

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.

Copied!

pCommandList->SetProgram(&SetProgramDesc);
pCommandList->DispatchGraph(&DispatchGraphDesc);

Picture of Tim McQuaig
Tim McQuaig

Tim McQuaig is an engineer in the Core Technology Group at AMD. His work engages Software Vendors and Engine Developers to realize the future of GPU applications.

GPU Work Graphs mesh nodes in Microsoft DirectX® 12

Mesh nodes are a new type of leaf node in work graphs that, unlike all other nodes, does not invoke a compute shader, but dispatches a mesh-shader graphics pipeline instead. This blog series covers how to get started with mesh nodes as well as best practices.

Looking for a good place to get started with exploring GPUOpen?

AMD GPUOpen documentation

Explore our huge collection of detailed tutorials, sample code, presentations, and documentation to find answers to your graphics development questions.

AMD GPUOpen Effects - AMD FidelityFX technologies

Create wonder. No black boxes. Meet the AMD FidelityFX SDK!

AMD GPUOpen Performance Guides

The home of great performance and optimization advice for AMD RDNA™ 2 GPUs, AMD Ryzen™ CPUs, and so much more.

AMD GPUOpen Samples

Browse all our useful samples. Perfect for when you’re needing to get started, want to integrate one of our libraries, and much more.

AMD GPUOpen developer SDKs

Discover what our SDK technologies can offer you. Query hardware or software, manage memory, create rendering applications or machine learning, and much more!

AMD GPUOpen Developer Tools

Analyze, Optimize, Profile, Benchmark. We provide you with the developer tools you need to make sure your game is the best it can be!

Getting started: AMD GPUOpen software

New or fairly new to AMD’s tools, libraries, and effects? This is the best place to get started on GPUOpen!

AMD GPUOpen Getting Started Development and Performance

Looking for tips on getting started with developing and/or optimizing your game, whether on AMD hardware or generally? We’ve got you covered!