May 27th, 2026
0 reactions

Partial Graphics Programs

Overview


Following the GDC announcement of the partial graphics programs feature, we are pleased to announce that it is available today in the AgilitySDK 1.721-preview. Checkout the release blog for more details about the full release.

This preview serves as a first look to explore what might be possible to ship in a future retail Agility SDK.  We encourage developers to try out the feature and provide feedback to https://discord.gg/directx which may influence the final design.

Downloads

Drivers

See Appendix > Feature Support for the full table of each feature’s supported hardware.

IHV Driver Link(s)
AMD AMD Developer Preview Edition 26.10.07.02
Intel Partial Programs support will be available in a future driver.
NVIDIA Partial Programs support will be available in a future driver.

The WARP software device supports partial graphics programs: https://www.nuget.org/packages/Microsoft.Direct3D.WARP/1.65535.20-preview

Partial Graphics Programs


Some game titles have a huge number of unique PSO combinations that they need to be compiled before the game loads to avoid stutter. Precompiling these PSO heavy titles in advance for many different hardware configurations for distribution through advanced shader delivery or for the preloading screen would take a significant amount of time and create duplicate effort.

To address this, we are creating partial graphics programs. Partial graphics programs split the pipeline creation into two steps: create partial pre-rasterization and pixel shader programs containing common state used by different graphics pipelines, then link them together in a generic program with other state. As result, reducing the compilation time and deduplicating common state getting compiled. The full technical details of partial graphics programs can be found in the spec.

Sample walkthrough


Enabling partial graphics programs in an app

Before creating a D3D device, enable the D3D12StateObjectsExperiment experimental feature (this is a temporary step while the feature is in preview) as shown from the sample:

 UUID experimentalFeatures[] = { D3D12StateObjectsExperiment };
 HRESULT hr = D3D12EnableExperimentalFeatures(_countof(experimentalFeatures), experimentalFeatures,
                                             nullptr, nullptr);

Once a D3D device has been created, double check that partial graphics programs are supported from the sample:

D3D12_FEATURE_DATA_PARTIAL_GRAPHICS_PROGRAMS partialGraphicsProgramTier = {};
ThrowIfFailed(m_device->CheckFeatureSupport(
D3D12_FEATURE_PARTIAL_GRAPHICS_PROGRAMS, &partialGraphicsProgramTier, sizeof(partialGraphicsProgramTier)));
if (partialGraphicsProgramTier.PartialGraphicsProgramsTier < D3D12_PARTIAL_GRAPHICS_PROGRAMS_TIER_1_0)
{
     OutputDebugStringA("Partial Graphics Programs Tier 1.0 is required.");
     ThrowIfFailed(E_FAIL);
}

Compiling Shaders

The sample app uses compiled shaders at runtime, though the shaders can be precompiled.

Partial graphics programs is a new way to define generic programs and, like generic programs, any shaders compiled with SM6.0+ can be used.  The sample app uses a VS and PS compiled using SM6.0 as shown below.

// Compile VS + PS once.
ComPtr<ID3DBlob> vertexShader;
ComPtr<ID3DBlob> pixelShader;
ThrowIfFailed(CompileDxilLibraryFromFile(
    GetAssetFullPath(L"shaders.hlsl").c_str(),
    L"VSMain", L"vs_6_0", nullptr, 0, &vertexShader));
ThrowIfFailed(CompileDxilLibraryFromFile(
    GetAssetFullPath(L"shaders.hlsl").c_str(),
    L"PSMain", L"ps_6_0", nullptr, 0, &pixelShader));

Demonstrating partial graphics programs


The sample app demonstrates the usage of partial graphics programs by defining two core partial graphics programs in a collection that will be reused in 3 different generic programs with different blend specifications. The two partial graphics programs used are:

  1. Prerasterization shader partial: This partial includes the vertex shader, primitive topology and input layout.
  1. Pixel shader partial: This partial includes the pixel shader, render target formats and newly added pixel shader partial fields that specify late-linked blend subobject. The late-linked blend subobject flag allows the variation of the blend subobject when creating the full generic programs.
// Create collection state object containing partial graphics programs with common state
// Partial graphics programs in a collection are compiled when
// the collection is created, so the executable state object that
// references it only has to perform a cheap link step. 
CD3DX12_STATE_OBJECT_DESC collectionDesc;
collectionDesc.SetStateObjectType(D3D12_STATE_OBJECT_TYPE_COLLECTION);

// Add subobjects needed in the partials
auto pConfig = collectionDesc.CreateSubobject<CD3DX12_STATE_OBJECT_CONFIG_SUBOBJECT>();
pConfig->SetFlags(D3D12_STATE_OBJECT_FLAG_ALLOW_STATE_OBJECT_ADDITIONS);

auto pRootSig = collectionDesc.CreateSubobject<CD3DX12_GLOBAL_ROOT_SIGNATURE_SUBOBJECT>();
pRootSig->SetRootSignature(m_rootSignature.Get());

auto pVS = collectionDesc.CreateSubobject<CD3DX12_DXIL_LIBRARY_SUBOBJECT>();
CD3DX12_SHADER_BYTECODE bcVS(vertexShader.Get());
pVS->SetDXILLibrary(&bcVS);

auto pPS = collectionDesc.CreateSubobject<CD3DX12_DXIL_LIBRARY_SUBOBJECT>();
CD3DX12_SHADER_BYTECODE bcPS(pixelShader.Get());
pPS->SetDXILLibrary(&bcPS);

auto pIL = collectionDesc.CreateSubobject<CD3DX12_INPUT_LAYOUT_SUBOBJECT>();
for (UINT i = 0; i < _countof(inputElementDescs); ++i)
{
    pIL->AddInputLayoutElementDesc(inputElementDescs[i]);
}

auto pTopology = collectionDesc.CreateSubobject<CD3DX12_PRIMITIVE_TOPOLOGY_SUBOBJECT>();
pTopology->SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE);

auto pRTFormats = collectionDesc.CreateSubobject<CD3DX12_RENDER_TARGET_FORMATS_SUBOBJECT>();
pRTFormats->SetNumRenderTargets(1);
pRTFormats->SetRenderTargetFormat(0, DXGI_FORMAT_R8G8B8A8_UNORM);

// Pixel shader partial program fields: blend will be late linked.
// AlphaToCoverageEnable and DualSourceBlendEnable affect PS
// compilation, so they must be specified here (the late-linked
// blend subobject must agree with these values).
auto pPSFields = collectionDesc.CreateSubobject<CD3DX12_PIXEL_SHADER_PARTIAL_PROGRAM_FIELDS_SUBOBJECT>();
pPSFields->SetAlphaToCoverageEnable(FALSE);
pPSFields->SetDualSourceBlendEnable(FALSE);
pPSFields->SetLateLinkBlendSubobject(TRUE);

// Create prerasterization shader partial
auto pPreRastProgram = collectionDesc.CreateSubobject<CD3DX12_PARTIAL_GRAPHICS_PROGRAM_SUBOBJECT>();
pPreRastProgram->SetProgramName(L"VSPartial");
pPreRastProgram->SetPartialGraphicsProgramType(D3D12_PARTIAL_GRAPHICS_PROGRAM_TYPE_PRERASTERIZATION_SHADER);
pPreRastProgram->AddExport(L"VSMain");
pPreRastProgram->AddSubobject(*pIL);
pPreRastProgram->AddSubobject(*pTopology);

// Create pixel shader partial with late-link blend set to true in PS partial fields subobject
auto pPSProgram = collectionDesc.CreateSubobject<CD3DX12_PARTIAL_GRAPHICS_PROGRAM_SUBOBJECT>();
pPSProgram->SetProgramName(L"PSPartial");
pPSProgram->SetPartialGraphicsProgramType(D3D12_PARTIAL_GRAPHICS_PROGRAM_TYPE_PIXEL_SHADER);
pPSProgram->AddExport(L"PSMain");
pPSProgram->AddSubobject(*pRTFormats);
pPSProgram->AddSubobject(*pPSFields);

ThrowIfFailed(m_device->CreateStateObject(collectionDesc, IID_PPV_ARGS(&m_collection)));

Blend subobject definitions

Define the blend subobjects with different configurations.

// Three blend descs (the only state that varies across the three
// generic programs we link).
auto MakeBlend = [](BOOL enable, D3D12_BLEND src, D3D12_BLEND dst)
{
    D3D12_BLEND_DESC b = {};
    b.RenderTarget[0].BlendEnable           = enable;
    b.RenderTarget[0].SrcBlend              = src;
    b.RenderTarget[0].DestBlend             = dst;
    b.RenderTarget[0].BlendOp               = D3D12_BLEND_OP_ADD;
    b.RenderTarget[0].SrcBlendAlpha         = D3D12_BLEND_ONE;
    b.RenderTarget[0].DestBlendAlpha        = D3D12_BLEND_ZERO;
    b.RenderTarget[0].BlendOpAlpha          = D3D12_BLEND_OP_ADD;
    b.RenderTarget[0].LogicOp               = D3D12_LOGIC_OP_NOOP;
    b.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
    return b;
};
const D3D12_BLEND_DESC blendDescs[NumBlendPermutations] =
{
    MakeBlend(FALSE, D3D12_BLEND_ONE,       D3D12_BLEND_ZERO),         // opaque
    MakeBlend(TRUE,  D3D12_BLEND_ONE,       D3D12_BLEND_ONE),          // additive
    MakeBlend(TRUE,  D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_INV_SRC_ALPHA),// alpha
};

Generic program definitions

Here are the generic program definitions using the partial graphics programs and subobjects described above.

// Set the generic program names
const LPCWSTR programNames[NumBlendPermutations] = { L"OpaqueProgram", L"AdditiveProgram", L"AlphaProgram" };

// Below is a snippet of defining AlphaProgram generic program
// Create a blend subobject used in a generic program definition
auto pBlend = executableDesc.CreateSubobject<CD3DX12_BLEND_SUBOBJECT>();
static_cast<D3D12_BLEND_DESC&>(*pBlend) = blendDescs[2];

// Create a generic program using the partial graphics programs defined in the collection and the blend subobject
auto pProgram = executableDesc.CreateSubobject<CD3DX12_GENERIC_PROGRAM_SUBOBJECT>();
pProgram->SetProgramName(programNames[2]);
pProgram->AddExport(L"VSPartial");
pProgram->AddExport(L"PSPartial");
pProgram->AddSubobject(*pBlend);

Executing the generic programs on command list


The code binds state to the command list and sets the generic program used before each draw call.

// Command list allocators can only be reset when the associated 
// command lists have finished execution on the GPU; apps should use 
// fences to determine GPU execution progress.
ThrowIfFailed(m_commandAllocator->Reset());

// However, when ExecuteCommandList() is called on a particular command 
// list, that command list can then be reset at any time and must be before 
// re-recording.
ThrowIfFailed(m_commandList->Reset(m_commandAllocator.Get(), nullptr));

// Set necessary state.
m_commandList->SetGraphicsRootSignature(m_rootSignature.Get());
m_commandList->RSSetViewports(1, &m_viewport);
m_commandList->RSSetScissorRects(1, &m_scissorRect);

// Indicate that the back buffer will be used as a render target.
m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));

CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart(), m_frameIndex, m_rtvDescriptorSize);
m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);

// Record commands.
const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f };
m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView);

// -------------------------------------------------------------------------
// Draw each triangle with its own blend-permutation program. The three
// program identifiers were produced by linking the same VS + PS partial
// programs with three different blend subobjects in LoadAssets().
// -------------------------------------------------------------------------
for (UINT i = 0; i < NumBlendPermutations; ++i)
{
    D3D12_SET_PROGRAM_DESC sp = {};
    sp.Type = D3D12_PROGRAM_TYPE_GENERIC_PIPELINE;
    sp.GenericPipeline.ProgramIdentifier = m_blendPrograms[i];
    m_commandList->SetProgram(&sp);
    m_commandList->DrawInstanced(3, 1, i * 3, 0);
}

// Indicate that the back buffer will now be used to present.
m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));

ThrowIfFailed(m_commandList->Close());

Sample


Partial graphics program sample can be found at DirectX-Graphics-Samples on GitHub, under the Samples/Desktop/D3D12HelloWorld folder.

Relevant to this preview is the basic sample D3D12HelloPartialGraphicsPrograms that the above code is from:

Samples/Desktop/D3D12HelloWorld/src/HelloPartialGraphicsPrograms

 

Screenshot 2026 05 27 123441 image

This sample is a new version of the basic D3D12HelloTriangle sample that uses generic programs with partial graphics programs to draw 3 triangles. Each triangle is drawn with a different generic program. All three generic programs share the same partial graphics programs (vertex shader, pixel shader, input layout, root signature) and differ only in their blend subobject. The green triangle’s additive blend sums with the red triangle beneath it, producing yellow in the overlap, while the blue triangle’s source-alpha blend makes its overlap with the green appear translucent.

PIX


PIX supports partial graphics programs. See the PIX release blog: https://devblogs.microsoft.com/pix/pix-2605-28-preview/

Appendix


Feature Support

Given drivers described in Overview > Drivers:

AMD Intel NVIDIA
Partial Programs AMD Radeonâ„¢ RX 7000 and 9000 series hardware Xe2 or newer hardware (in a future driver) All RTX hardware (in a future driver)

The WARP software device supports partial graphics programs.

Category

Author

Engineer

0 comments