Prepare your game for Advanced Shader Delivery (ASD)

August 14, 2026 · View on GitHub

Table of contents

Prepare your game for Advanced Shader Delivery (ASD)

Advanced Shader Delivery reduces shader compilation times, power consumption, and disruptive stuttering for D3D12 games on PC. Shaders captured from the title are compiled ahead of time and delivered to the player's device alongside it, supplementing the device's shader cache from first launch.

This guide covers three stages of integration:

  1. Create a State Object Database (SODB) for your game, by programmatic generation or manual capture.
  2. Test the SODB by compiling it into a Precompiled Shader Database (PSDB), registering it locally, and confirming its quality.
  3. Deploy the SODB to a storefront.

As a developer, you will produce an SODB for your title. An SODB is the serialized list of the Pipeline State Objects (PSOs) and State Objects (SOs) your game creates, together with the shader DXIL needed to reconstruct them. After deploying the database to a storefront, it compiles it into a PSDB and delivers it to the players. The PSDB is used by the device’s shader cache to load all the shaders during creation.

ASD lifecycle diagram

Prerequisites

RequirementDetails
Agility SDK1.619.5 or newer
OS BuildWindows 11, version 24H2 (build 26100.1000) or newer
GPU / driverAn ASD-capable GPU and driver. To check, see Confirm ASD support
ToolsAgility SDK (includes d3dconfig.exe and D3D12StateObjectCompiler.exe), and D3D12CacheListener.

Confirm ASD support

SODB compilation requires an ASD-supported GPU. Run the following command to check for support:

D3D12StateObjectCompiler.exe list --adapters

The adapter supports ASD if Compiler<Wow> returns a path to both compilers, and if Feature Supported under ABI Support returns <Supported>:

D3D12 UserMode Driver:
        ...
        Compiler: C:\Windows\System32\DriverStore\FileRepository\...\<IHV_CompilerPlugin>.dll
        CompilerWow: C:\Windows\System32\DriverStore\FileRepository\...\<IHV_CompilerPluginWow>.dll
ABI Support:
        Feature Supported: <Supported>
        ...

Terms and acronyms

TermDefinition
ASDAdvanced Shader Delivery
SOState Object. A state object representing raytracing, work graphs, compute programs or graphics program components like the input assembler, rasterizer, pixel shader, and output merger, etc.
PSOPipeline State Object. A unified state object representing the graphics or compute pipeline.
SODBState object database. The serialized SO/PSO description objects and the shader DXIL needed to reconstruct any create SO/PSO API calls.
ShaderA user-defined program that runs on some stage of the graphics processor. Subset of PSO.
PSDBPrecompiled Shader Database. The output of compiling an SODB with an IHV compiler plugin.
IHVIndependent Hardware Vendor (GPU vendor).
ISVIndependent Software Vendor (game/app developer).

1. SODB creation

Creating an SODB has two parts: set up your application identity, then fill the SODB with your pipelines through programmatic generation or manual capture.

1.1 Set the application identity

Setting application identity is an essential step of SODB creation. Every SODB must carry a matching application identity:

D3D12_APPLICATION_DESC: exe filename, name, version, engine name, engine version.

This information will be used to apply the correct application-specific profile, binding the resulting PSDB to your game. For the full field definitions and identity precedence rules, see the Application Identity spec.

Set the identity before device creation with ID3D12ApplicationIdentity::SetApplicationIdentity. The runtime reads it at device creation, so it must be in place before a capture session or before you build the SODB from code:

#include <initguid.h>
#include <d3d12.h>

CComPtr<ID3D12ApplicationIdentity> spAppIdentity;
D3D12GetInterface(CLSID_D3D12ApplicationIdentity, IID_PPV_ARGS(&spAppIdentity));

D3D12_APPLICATION_DESC appDesc = {};
appDesc.pExeFilename   = L"game.exe";
appDesc.pName          = L"Application Name";
appDesc.Version.Version = 0x0001000000000000; // 1.0.0.0
appDesc.pEngineName    = L"Engine Name";
appDesc.EngineVersion.Version = 0x0001000000000000; // 1.0.0.0

// AppId is a stable GUID that keys the PSDB to your title on the device.
// Create GUID from VS: Tools -> Create GUID -> Select the format
static constexpr GUID AppId = { /* ... */ };
spAppIdentity->SetApplicationIdentity(&appDesc, AppId);

// Create the D3D12 device after setting the application identity.

Alternatively, if you capture without setting the identity first, you can add it to the finished SODB with D3D12StateObjectCompiler.exe:

D3D12StateObjectCompiler.exe set-identity \
    --exe-filename "game.exe" \
    --name "Application Name" \
    --app-version 1.0.0.0 \
    --engine "Engine Name" \
    --engine-version 1.0.0.0 \
    "C:\asd\game.sodb"

Verify it with:

D3D12StateObjectCompiler.exe get-identity "C:\asd\game.sodb"

With the identity set, fill the SODB with your pipelines through one of two capture methods:

1.2 Choose a capture method

Programmatic generation (section 1.3)Manual capture (section 1.4)
How the pipelines are gatheredThe title enumerates its own pipelines and writes them to the database through an API, or translates them from an existing capture mechanism.The D3D12 runtime records every PSO and SO created during a play session.
Engineering effortWeeks to a few months for a custom engine. Substantially less on a middleware game engine that already supports SODB generation.No engine changes. Setup is the d3dconfig steps in section 1.4, and most of the cost is play time across configurations and hardware.
CoverageBounded by the completeness of the title's pipeline list.Bounded by what the play sessions exercise.
RepeatabilityAutomated. Fits into a build.Manual. Every capture round is a fresh set of play sessions.
Best suited toTitles early in development, and titles that already enumerate their pipelines for other reasons.Shipped titles, and titles late in development.

Programmatic generation is the recommended long-term approach. Build the integration now on a new title, or one still early in development, so the engine produces the SODB as part of a build.

Manual capture is the recommended short-term approach. It reaches a usable SODB fastest and needs no engine changes, which suits a title that has already shipped or is close to shipping. Set the identity on the captured SODB with set-identity.

Where a middleware engine, such as Unreal Engine, its path supersedes both methods below. Consult that engine's documentation for its ASD support.

Note

If you’re thinking about even longer-term strategies: check out section 4. Design your next game for pre-compilation.

1.3 Capture method 1: Programmatic SODB generation

Programmatic SODB generation builds the SODB from code.

1.3.1 Build an SODB from a shader list

Build the SODB from a known list of Pipeline State Objects and State Objects with the ID3D12StateObjectDatabase API.

Follow these steps:

  1. Retrieve the factory with D3D12GetInterface(CLSID_D3D12StateObjectFactory, ...).

  2. Create the database with ID3D12StateObjectDatabaseFactory::CreateStateObjectDatabaseFromFile.

  3. Set the application identity with ID3D12StateObjectDatabase::SetApplicationDesc.

  4. For each pipeline in your list, call StorePipelineStateDesc (PSOs) or StoreStateObjectDesc (state objects) with the corresponding descriptor and shader DXIL.

    #include <initguid.h>
    #include <d3d12.h>
    
    // Retrieve the factory.
    CComPtr<ID3D12StateObjectDatabaseFactory> spFactory;
    D3D12GetInterface(CLSID_D3D12StateObjectFactory, IID_PPV_ARGS(&spFactory));
    
    // Create the SODB on disk.
    Microsoft::WRL::ComPtr<ID3D12StateObjectDatabase> spSODB;
    spFactory->CreateStateObjectDatabaseFromFile(
        L"C:\\asd\\game.sodb",
        D3D12_STATE_OBJECT_DATABASE_FLAG_NONE,
        IID_PPV_ARGS(&spSODB));
    
    // Stamp the application identity.
    D3D12_APPLICATION_DESC appDesc = {};
    appDesc.pExeFilename   = L"game.exe";
    appDesc.pName          = L"Application Name";
    appDesc.Version.Version = 0x0001000000000000; // 1.0.0.0
    appDesc.pEngineName    = L"Engine Name";
    appDesc.EngineVersion.Version = 0x0001000000000000; // 1.0.0.0
    spSODB->SetApplicationDesc(&appDesc);
    
    // For each pipeline, store its descriptor + DXIL:
    //   spSODB->StorePipelineStateDesc(key, keySize, version, &streamDesc);  // PSOs
    //   spSODB->StoreStateObjectDesc(key, keySize, version, &stateObjectDesc); // SOs
    

Build a list of every PSO and SO your game uses, iterate over it, and Store* each one. Make sure not to gate it through cap checks when enumerating the set of pipelines. The goal is a device-agnostic SODB that covers every pipeline across all supported hardware and settings.

See StateObjectDatabase for the complete, runnable API example, and the D3D12StateObjectDatabase sample for working sample code that generates an SODB programmatically.

1.4 Capture method 2: Manual capture

Manual capture serializes every PSO and SO the D3D12 runtime creates during a play session. Coverage depends directly on how thoroughly you exercise the game (see Play through the game for full coverage).

1.4.1 Capture via d3dconfig

  1. Add your game’s executable name to the app list.

    d3dconfig.exe apps --add <exefilename>
    
  2. Set the SODB output path.

    d3dconfig.exe device pso-db-path=<filepath>.sodb
    
  3. Enable SODB collection.

    d3dconfig.exe device enable-pso-db=true
    
  4. Play through the game, then exit. The runtime writes the PSOs and SOs created by the D3D12 API to the SODB path from step 2. See Play through the game for full coverage.

  5. Reset d3dconfig settings when collection is complete.

    d3dconfig.exe --reset
    
  6. Checkpoint the SODB

    D3D12StateObjectCompiler.exe checkpoint-db <filepath>.sodb
    

1.4.2 Play through the game for full coverage

Tips to maximize coverage during manual capture:

  • Play a representative portion of your game under a variety of graphics options. The pipelines are created as the game runs, so you need to play through content with each configuration. For example: play through a representative section with each graphics preset, both with and without ray tracing enabled.
  • Restart the game between configuration changes, if needed. Depending on your game's architecture, a restart may be necessary to ensure all content is captured under the new settings. For example, if your game compiles shaders once at startup rather than reacting to setting changes mid-session. If you're unsure whether a setting fully takes effect at runtime, restart to be safe.
  • If the game does not have a dedicated shader loading screen, play through major sections of the game.
  • Capture on a variety of hardware to exercise device-specific code paths. Depending on your engine architecture, you may have device-specific branches. Code that selects different pipelines based on the GPU, driver, or feature support. A single machine only exercises the branches that apply to it, so all the other paths won't appear in an SODB captured there. To get thorough coverage across all devices, capture at least one SODB per IHV so every branch runs, then merge the results into one master SODB.

1.4.3 Merge SODBs

Merge multiple capture runs into one SODB with D3D12StateObjectCompiler.exe:

D3D12StateObjectCompiler.exe merge-sodb \
    C:\asd\run1.sodb \
    C:\asd\run2.sodb \
    ...              \
    C:\asd\runX.sodb

This method merges multiple SODBs into an already existing one, preserving the original application identity.

Verify the application identity data with the following command:

D3D12StateObjectCompiler.exe get-identity "C:\asd\runX.sodb"

2. Testing

Before deploying, validate locally that your SODB produces a PSDB with a good cache hit rate on your target hardware.

2.1 Compile the SODB into a PSDB

Compile your SODB into a PSDB with D3D12StateObjectCompiler.exe. The compiler uses the IHV plugin registered with the currently installed GPU driver, or an explicit --plugin path:

D3D12StateObjectCompiler.exe compile \
    --adapter 0 \
    --name "Application Name" \
    --exe-filename "game.exe" \
    C:\asd\game.sodb \
    C:\asd\game.psdb

Note

Use --adapter <index> to compile with a locally installed adapter’s plugin, or --plugin <path> with an explicit --adapter-family / --abi to target a specific plugin. Run D3D12StateObjectCompiler.exe list --adapters to enumerate adapters and adapter families.

2.2 Register the PSDB

Register the PSDB with d3dconfig:

  1. Add your game's executable to the d3dconfig app list.

    d3dconfig.exe apps --add <exefilename>
    
  2. Select the PSDB.

    d3dconfig.exe device precompiled-db-path=<filepath>.psdb
    
  3. Confirm the registration override is enabled. It defaults to true, so this step is only needed if it was turned off previously:

    d3dconfig.exe device precompiled-db-registration-override=true
    
  4. Restart the game. These settings are read once before device creation, the game must start after they're set. And clear the GPU's shader cache.

  5. Reset d3dconfig settings when you finish testing:

    d3dconfig.exe --reset
    

Note

To fall back to the older path-only behavior (path override with compatibility checks disabled), set precompiled-db-registration-override=false or precompiled-db-path=””.

2.3 Verify the cache hit rate

  1. Download the latest D3D12CacheListener.
  2. Run D3D12CacheListener.exe as Administrator. It listens to Event Tracing for Windows (ETW) events and requires elevation.
  3. Launch your game and play for a few minutes while CacheListener observes.
  4. Read the reported cache hit rate. In principle, replaying any area of the game you captured should give a 100% cache hit rate, since every pipeline that runs was already recorded into the SODB. In practice, a few factors keep real-world numbers below that ceiling:
    • Differences between capture and playback configuration. A different GPU, driver, or settings combination can create pipelines your capture never saw.
    • Test and capture playthroughs rarely match exactly. A different route or set of actions can trigger pipelines you didn't exercise during capture.
    • IHVs may not yet support 100% of all PSOs and SOs.
  • We generally consider ≥ 90% a good result, meaning that ASD is working and the driver is serving most pipelines from the precompiled PSDB. A low or 0% hit rate points to a problem, see Troubleshoot a low hit rate.

2.4 Troubleshoot a low hit rate

SymptomLikely causeWhat to check
0% hit rateIdentity mismatchThe SODB and PSDB application identity must match the running game (see Set the application identity).
0% hit rate, plus HasDefaultPSDB == FalseRegistration mismatchRegistration is keyed by the exe path. Confirm the --exe-path you passed to register exactly matches the path of the running game executable.
Low hit rateIncomplete capture coverageCycle through presets and play more scenarios; merge additional captures.
Low hit rateMissing IHV specific shadersCapture an additional SODB on hardware where the hit rate is low and merge SODBs

3. Deployment

After your SODB produces a PSDB with a healthy hit rate, it’s time to hand the SODB off to a storefront that supports ASD. Regardless of store, the deployment flow is the same:

  1. You submit your SODB alongside your game package.
  2. The store runs a server-side compile of your SODB against each IHV's compiler plugin, producing a per-driver PSDB.
  3. The store delivers the matching PSDB to each player's device according to the driver.

3.1 Xbox Store

Refer to Xbox’s official documentation for the current submission steps:

4. Design your next game for pre-compilation

This section is aimed at strategizing your next game project (or early in-development).

  • Tradeoffs between IHV-specific code paths:
    • Device-specific branches can multiply the pipelines that must be precompiled. Minimizing this divergence has real benefits: a smaller SODB, less to compile and deliver per device. For example, sharing a single root signature across hardware vendors instead of per-vendor variants keeps a pipeline uniform everywhere. That said, convergence isn't free. Some vendor-specific paths exist because they perform better on that hardware, and collapsing them can cost performance. The right balance depends on your title's performance targets and how much per-device tuning matters to you.
  • Partial programs
    • For titles with very large numbers of pipeline state objects, partial graphics programs split pipeline creation into reusable pieces. This reduces duplication in what needs to be precompiled, and how much the store must compile per device.
  • The long-term goal
    • The direction for ASD is for SODB collection to become a built-in part of the development process. With that, it’s good to think about how to optimize it for future workflows.

References

Appendix A. Command quick reference

TaskCommand
Enumerate locally installed adapters and their pluginsD3D12StateObjectCompiler.exe list --adapters
Add a title to the d3dconfig app listd3dconfig.exe apps --add <exe-filename>
Set the capture output pathd3dconfig.exe device pso-db-path=<absolute-path>.sodb
Start captured3dconfig.exe device enable-pso-db=true
Stop captured3dconfig.exe device enable-pso-db=false
Fold a write-ahead log back into an SODBD3D12StateObjectCompiler.exe checkpoint-db <file.sodb>
Restore the previous journaling behaviord3dconfig.exe device pso-db-journal-mode=default
Clear all d3dconfig settings without promptingd3dconfig.exe --reset --confirm
Merge captures (output first)D3D12StateObjectCompiler.exe merge-sodb <in1.sodb> <in2.sodb> … <out.sodb>
Set identity on an SODBD3D12StateObjectCompiler.exe set-identity --exe-filename <game.exe> --name <app name> --app-version <a.b.c.d> --engine <engine_name > --engine-version <a.b.c.d> <file.sodb>
Read identity from an SODBD3D12StateObjectCompiler.exe get-identity <file.sodb>
Re-create an SODB's contents on a deviceD3D12StateObjectCompiler.exe replay --adapter <n> <file.sodb>
Compile against a local adapterD3D12StateObjectCompiler.exe compile --adapter <n> <in.sodb> <out.psdb>
Compile against a specific pluginD3D12StateObjectCompiler.exe compile --plugin <plugin.dll> --adapter-family <family> --abi <n> <in.sodb> <out.psdb>
Limit compiler resource useAdd --single-threaded, or --max-threads <n> / --priority
Point the runtime at a local PSDBd3dconfig.exe device precompiled-db-path=<path_to_file>.psdb
Remove a title's settings, including that overrided3dconfig.exe apps --remove <exe-filename>