DirectX-Specs

Advanced Shader Delivery: D3D Shader Cache Registration (D3DSCR) API

D3DSCR API is a new nano-com based API for managing the registration of and enumeration of Shader Object Databases (SODBs) and Pre-Compiled Shader Databases (PSDBs) on the end-user system. This API is intended to be used by game installers such as the Xbox Store, Steam etc. during their game installation and update process. Additionally, this API will also be used by the D3D12 Runtime in the game process to locate and create state objects using PSDBs and SODBs.

This API will be distributed with the D3D12 Agility SDK; however, a compatible D3D12 runtime will be made available in-box for Windows 11 machines.

Install/Update Workflow:

Diagram representing the workflow of how the Shader Cache registration API should be used for the game store Install/Update scenario

This diagram demonstrates the interactions between the Game Store/Installer and the D3DSCR API during the game install and update process. A typical process is as follow:

  1. The game’s files are installed to the end user’s machine, the SODBs are included with that step as they should be considered part of the game’s content.
  2. The installer will instantiate the D3DSCR API and proceed to register the game that was just installed, providing it’s executable path on disk along with the paths of it’s SODBs.
  3. The installer uses D3DSCR to query which graphics adapter families and compiler ABI versions the installed game should target.
  4. With the adapter and compiler information in hand the installer will fetch a PSDB which matches the application and compiler combination from a compilation service.
  5. The PSDB is installed by the installer to a disk location of it’s choice.
  6. The installer will use D3DSCR to register the newly installed PSDB for the game.

Runtime Workflow (Non-Title Cooperative):

Diagram representing the workflow of how the Shader Cache registration API should be used during runtime

This diagram demonstrates the interaction between the D3D12 runtime and D3DSCR API during game execution time for the ‘Non-Title Cooperative’ case. In this case the title was either shipped before Advanced Shader Delivery (ASD) was released or was shipped after and chose not to use the new ASD APIs. The game can still benefit from pre-compiled shaders if an SODB can be collected via playthrough. A typical process in this scenario is as follows:

  1. The game installer launches the game (after first running through the install/update workflow if applicable).
  2. The game runs, loads the D3D12 runtime and proceeds to create Pipeline State Objects (PSOs) or State Objects (SOs).
  3. The D3D12 runtime will interact with the D3DSCR API to enumerated installed SODBs and PSDBs for the current process. The process executable path will be used as a key for D3DSCR.
  4. The D3D12 runtime will check for a matching pre-compiled shader in the PSDB to avoid runtime compilation.

Public API

GUID Definitions:

Enumerations:


Defines the user account scope of shader cache application registration. In practice this will determine the root path in the Windows registry where data will be stored. For example, if the installer is run with Windows Administrator privilege level then `D3D_SHADER_CACHE_APP_REGISTRATION_SCOPE_SYSTEM` should be used.

- **D3D_SHADER_CACHE_TARGET_FLAGS**:
``` C++
typedef enum D3D_SHADER_CACHE_TARGET_FLAGS
{
    D3D_SHADER_CACHE_TARGET_FLAG_NONE = 0,
}D3D_SHADER_CACHE_TARGET_FLAGS;
cpp_quote("DEFINE_ENUM_FLAG_OPERATORS( D3D_SHADER_CACHE_TARGET_FLAGS )")

Flags to modify target lookup behavior. Currently reserved.

Interface Definition: ID3DShaderCacheInstallerClient

// This interface is implemented by the game installer
interface ID3DShaderCacheInstallerClient
{
  HRESULT GetInstallerName(
      [annotation("_Inout_")] SIZE_T* pNameLength,
      [annotation("_Out_writes_opt_(*pNameLength)")] wchar_t* pName);

  D3D_SHADER_CACHE_APP_REGISTRATION_SCOPE GetInstallerScope();

  HRESULT HandleDriverUpdate(
      [annotation("_In_")] ID3DShaderCacheInstaller* pInstaller);
};

Methods

GetInstallerName

GetInstallerScope

HandleDriverUpdate

This is a callback function that will be called by the system when a graphics driver update occurs. See ID3DShaderCacheInstaller::RegisterDriverUpdateListener for more information.

Usage

The user should instantiate their own final implementation of this interface and passing it to the D3DSCR API during ID3DShaderCacheInstaller creation. This object must persist for the lifetime of the ID3DShaderCacheInstaller object it is used with.

Interface Definition: ID3DShaderCacheComponent

interface ID3DShaderCacheComponent : IUnknown
{
  HRESULT GetComponentName(
      [annotation("_Out_")] const wchar_t** pName);

  HRESULT GetStateObjectDatabasePath(
      [annotation("_Out_")] const wchar_t** pPath);

  HRESULT GetPrecompiledCachePath(
      [annotation("_In_")] const wchar_t* pAdapterFamily,
      [annotation("_Inout_")] const wchar_t** pPath);

  UINT GetPrecompiledShaderDatabaseCount();

  HRESULT GetPrecompiledShaderDatabases(
      UINT ArraySize,
      [annotation("_Out_writes_(ArraySize)")] D3D_SHADER_CACHE_PSDB_PROPERTIES* pPSDBs);
};

A shader component represents the pairing of one State Object Database (SODB) to one or more Pre-Compiled Shader Databases (PSDB) i.e. one for each supported GPU adapter installed in the system. A shader component has an identifying name as well as a file system path to the installed SODB file and associated PSDB files.

Methods

GetComponentName

GetStateObjectDatabasePath

GetPrecompiledCachePath

GetPrecompiledShaderDatabaseCount

GetPrecompiledShaderDatabases

Interface Definition: ID3DShaderCacheApplication

interface ID3DShaderCacheApplication : IUnknown
{
  HRESULT GetExePath(
      [annotation("_Out_")] const wchar_t** pExePath);

  HRESULT GetDesc(
      [annotation("_Out_")] D3D_SHADER_CACHE_APPLICATION_DESC* pApplicationDesc);

  HRESULT RegisterComponent(
      [annotation("_In_")] const wchar_t* pName,
      [annotation("_In_")] const wchar_t* pStateObjectDBPath,
      [annotation("_In_")] UINT NumPSDB,
      [annotation("_In_reads_(NumPSDB)")] const D3D_SHADER_CACHE_PSDB_PROPERTIES* pPSDBs,
      REFIID riid,
      [annotation("_COM_Outptr_")] void** ppvComponent);

  HRESULT RemoveComponent(
      [annotation("_In_")] ID3DShaderCacheComponent* pComponent);

  UINT GetComponentCount();

  HRESULT GetComponent(
      [annotation("_In_")] UINT index,
      REFIID riid,
      [annotation("_COM_Outptr_")] void** ppvComponent);

  UINT GetPrecompileTargetCount(D3D_SHADER_CACHE_TARGET_FLAGS flags);

  HRESULT GetPrecompileTargets(
      [annotation("_In_")] UINT ArraySize,
      [annotation("_In_reads_(ArraySize)")] D3D_SHADER_CACHE_COMPILER_PROPERTIES* pArray,
      D3D_SHADER_CACHE_TARGET_FLAGS flags);
};

Represents an application or game installed on a system. Each application can have one or more Components which represent cached shader objects.

Methods

GetExePath

GetDesc

RegisterComponent

RemoveComponent

GetComponentCount

GetComponent

GetPrecompileTargetCount

GetPrecompileTargets

Interface Definition: ID3DShaderCacheInstaller

interface ID3DShaderCacheInstaller : IUnknown
{
  HRESULT RegisterDriverUpdateListener();

  HRESULT UnregisterDriverUpdateListener();

  HRESULT RegisterServiceDriverUpdateTrigger(
      SC_HANDLE hServiceHandle);

  HRESULT UnregisterServiceDriverUpdateTrigger(
      SC_HANDLE hServiceHandle);

  HRESULT RegisterApplication(
      [annotation("_In_")] const wchar_t* pExePath,
      [annotation("_In_")] const D3D_SHADER_CACHE_APPLICATION_DESC* pApplicationDesc,
      REFIID riid,
      [annotation("_COM_Outptr_")] void** ppvApp);

  HRESULT RemoveApplication(
      [annotation("_In_")] ID3DShaderCacheApplication* pApplication);

  UINT GetApplicationCount();

  HRESULT GetApplication(
      [annotation("_In_")] UINT index,
      REFIID riid,
      [annotation("_COM_Outptr_")] void** ppvApp);

  HRESULT ClearAllState();

  UINT GetMaxPrecompileTargetCount();

  HRESULT GetPrecompileTargets(
      [annotation("_In_opt_")] const D3D_SHADER_CACHE_APPLICATION_DESC* pApplicationDesc,
      [annotation("_In_")] UINT ArraySize,
      [annotation("_In_reads_(ArraySize)")] D3D_SHADER_CACHE_COMPILER_PROPERTIES* pArray,
      D3D_SHADER_CACHE_TARGET_FLAGS flags);
};

Manages the registration of applications and their associated SODBs and PSDBs.

Methods

RegisterDriverUpdateListener

This API is used to indicated that the installer is interested in receiving notifications from the system whenever a graphics driver is installed or updated. The callback function HandleDriverUpdate on the ID3DShaderCacheInstallerClient interface will be called during a driver update.

When a new driver is available on the system the installer will need to review applications it has registered to determine if the PSDBs associated with the device have been invalidated.

UnregisterDriverUpdateListener

RegisterServiceDriverUpdateTrigger

This function registers a Windows service trigger that will automatically start a service when a driver is installed or updated. This service trigger should be used to review registered applications to determine if the PSDBs associated with the device have been invalidated.

ChangeServiceConfig2W and related Win32 APIs can be used to modify the returned service’s SERVICE_CONFIG_TRIGGER_INFO if required.

UnregisterServiceDriverUpdateTrigger

RegisterApplication

RemoveApplication

GetApplicationCount

GetApplication

ClearAllState

GetMaxPrecompileTargetCount

GetPrecompileTargets

Interface Definition: ID3DShaderCacheExplorer

interface ID3DShaderCacheExplorer : IUnknown
{
    HRESULT GetApplicationFromExePath(
        [annotation("_In_")] const wchar_t* pFullExePath,
        [in] REFIID riid,
        [out, iid_is(riid), annotation("_COM_Outptr_")] void** ppvApp); // Expected: ID3DShaderCacheApplication
};

Creates a read-only view of the current state of the shader cache registration on the system. Any mutable operations on ID3DShaderCacheApplication or ID3DShaderCacheComponent objects obtained through this interface will fail and no state will be updated.

Methods

GetApplicationFromExePath

Interface Definition: ID3DShaderCacheInstallerFactory

interface ID3DShaderCacheInstallerFactory : IUnknown
{
    HRESULT CreateInstaller(
        [annotation("_In_")] ID3DShaderCacheInstallerClient* pClient,
        [in] REFIID riid,
        [out, iid_is(riid), annotation("_COM_Outptr_")] void** ppvInstaller);

    HRESULT CreateExplorer(
        [in] IUnknown* pUnknown,                                             // Expected: ID3D12Device, IDXCoreAdapter, IDXGIAdapter
        [in] REFIID riid,
        [out, iid_is(riid), annotation("_COM_Outptr_")] void** ppvExplorer); // Expected: ID3DShaderCacheExplorer
};

Factory interface for creating shader cache installers and explorers. ID3DShaderCacheInstallerFactory Objects are created using the D3D12GetInterface function by passing the CLSID_D3DShaderCacheInstallerFactory GUID.

Methods

CreateInstaller

CreateExplorer

Structure Definition: D3D_SHADER_CACHE_PSDB_PROPERTIES

typedef struct D3D_SHADER_CACHE_PSDB_PROPERTIES
{
    const wchar_t* pAdapterFamily;
    const wchar_t* pPsdbPath;
} D3D_SHADER_CACHE_PSDB_PROPERTIES;

Defines properties for precompiled shader database.

Fields

Structure Definition: D3D_VERSION_NUMBER

typedef union D3D_VERSION_NUMBER
{
    UINT64 Version;
    UINT16 VersionParts[4];
} D3D_VERSION_NUMBER;

Defines a version number as either a 64-bit integer or an array of 4 16-bit parts.

Fields

Structure Definition: D3D_SHADER_CACHE_COMPILER_PROPERTIES

typedef struct D3D_SHADER_CACHE_COMPILER_PROPERTIES
{
    wchar_t szAdapterFamily[128];
    UINT64 MinimumABISupportVersion;
    UINT64 MaximumABISupportVersion;
    D3D_VERSION_NUMBER CompilerVersion;
    D3D_VERSION_NUMBER ApplicationProfileVersion;
} D3D_SHADER_CACHE_COMPILER_PROPERTIES;

Defines properties for shader cache compiler.

Fields

Structure Definition: D3D_SHADER_CACHE_APPLICATION_INFO

typedef struct D3D_SHADER_CACHE_APPLICATION_INFO
{
    const wchar_t* pName;
    const wchar_t* pExeFilename;
    D3D_VERSION_NUMBER Version;
    const wchar_t* pEngineName;
    D3D_VERSION_NUMBER EngineVersion;
} D3D_SHADER_CACHE_APPLICATION_INFO;

The application desc is metadata used for drivers, compilers, and the D3D runtime to uniquely identify an application.

Fields

Notes:

Example Usage:

    struct BasicClient : public ID3DShaderCacheInstallerClient
    {
        BasicClient(const wchar_t* pName) : m_name(pName) {}

        HRESULT __stdcall GetInstallerName(SIZE_T* pNameLength,wchar_t* pName) override
        {
            if (pNameLength == nullptr)
            {
                return E_INVALIDARG;
            }

            if (pName == nullptr)
            {
                *pNameLength = m_name.length() + 1;
                return S_OK;
            }

            if (*pNameLength < m_name.length() + 1)
            {
                return E_INVALIDARG;
            }

            wcscpy_s(pName, *pNameLength, m_name.c_str());

            return S_OK;
        }

        D3D_SHADER_CACHE_APP_REGISTRATION_SCOPE __stdcall GetInstallerScope(void) override
        {
            return D3D_SHADER_CACHE_APP_REGISTRATION_SCOPE_USER;
        }
        HRESULT __stdcall HandleDriverUpdate(ID3DShaderCacheInstaller* pInstaller) override
        {
            UNREFERENCED_PARAMETER(pInstaller);
            return E_NOTIMPL;
        }

        const std::wstring m_name;
    };

    void RegisterApplications()
    {
        BasicClient Client(L"AGameStore");

        CComPtr<ID3DShaderCacheInstallerFactory> pInstallerFactory;

        VERIFY_SUCCEEDED(D3D12GetInterface(CLSID_D3DShaderCacheInstallerFactory, IID_PPV_ARGS(&pInstallerFactory)));

        CComPtr<ID3DShaderCacheInstaller> pInstaller;
        VERIFY_SUCCEEDED(pInstallerFactory->CreateInstaller(&Client, IID_PPV_ARGS(&pInstaller)));

        VERIFY_SUCCEEDED(pInstaller->ClearAllState());

        const std::wstring appIntsallPath = LR"(C:\Program Files\AGame\)";
        const std::wstring appName = L"AGame";
        const std::wstring appExe = L"AGame.exe";
        const std::wstring engineName = L"TaefEngine";
        const std::wstring fullExePath = appIntsallPath + appExe;
        const std::wstring sodbName = L"AGame.sodb";
        const std::wstring sodbPath = appIntsallPath + sodbName;
        const std::wstring psdbName = L"AGame_warp.psdb";
        const std::wstring psdPath = appIntsallPath + psdbName;
        const std::wstring componentName = L"PsoLib";

        const UINT64 appVersion = 101101;
        const UINT64 engineVersion = 122122;

        // 'Install' an application
        {
            D3D_SHADER_CACHE_APPLICATION_DESC appDesc = {};
            appDesc.pName = appName.c_str();
            appDesc.pExeFilename = appExe.c_str();
            appDesc.pEngineName = engineName.c_str();
            appDesc.Version = appVersion;
            appDesc.EngineVersion = engineVersion;

            CComPtr<ID3DShaderCacheApplication> pApp;
            VERIFY_SUCCEEDED(pInstaller->RegisterApplication(fullExePath.c_str(), &appDesc, IID_PPV_ARGS(&pApp)));

            UINT numPrecompileTargets = pApp->GetPrecompileTargetCount(D3D_SHADER_CACHE_TARGET_FLAG_NONE);

            std::vector<D3D_SHADER_CACHE_COMPILER_PROPERTIES> compileParams(numPrecompileTargets);
            VERIFY_SUCCEEDED(pApp->GetPrecompileTargets(numPrecompileTargets, compileParams.data(), D3D_SHADER_CACHE_TARGET_FLAG_NONE));

            std::vector<D3D_SHADER_CACHE_PSDB_PROPERTIES> psdbParams;
            for (auto& target : compileParams)
            {
                // < fetch the psdb for this app and device combo from the interwebs>
                // A compatible psdb is:
                //   - Compiled for the same adapter family
                //   - Targets an ABI compatible with the inclusive MinimumABISupportVersion and MaximumABISupportVersion.
                //   - The top 32bits of the ApplicationProfileVersion match.

                psdbParams.push_back({ params.szAdapterFamily, psdPath.c_str() });
            }

            CComPtr<ID3DShaderCacheComponent> component;
            VERIFY_SUCCEEDED(pApp->RegisterComponent(componentName.c_str(),
                sodbPath.c_str(),
                UINT(psdbParams.size()),
                psdbParams.data(),
                IID_PPV_ARGS(&component)));
        }

        // Read back info about the app
        {
            UINT numApps = pInstaller->GetApplicationCount();

            CComPtr<ID3DShaderCacheApplication> pApp;
            VERIFY_SUCCEEDED(pInstaller->GetApplication(0, IID_PPV_ARGS(&pApp)));

            // Verify
            {
                D3D_SHADER_CACHE_APPLICATION_INFO info = {};

                VERIFY_SUCCEEDED(pApp->GetInfo(&info));

                UINT numComponents = pApp->GetComponentCount();
                for (UINT i = 0; i < numComponents; i++)
                {
                    CComPtr<ID3DShaderCacheComponent> component;
                    VERIFY_SUCCEEDED(pApp->GetComponent(i, IID_PPV_ARGS(&component)));

                    // Component name
                    const wchar_t* componentNameReadback = nullptr;
                    VERIFY_SUCCEEDED(component->GetComponentName(&componentNameReadback));

                    // SODB path
                    const wchar_t* sodbPathReadback = nullptr;
                    VERIFY_SUCCEEDED(component->GetStateObjectDatabasePath(&sodbPathReadback));

                    UINT numPrecompileTargets = pApp->GetPrecompileTargetCount(D3D_SHADER_CACHE_TARGET_FLAG_NONE);
                    VERIFY_IS_GREATER_THAN_OR_EQUAL(numPrecompileTargets, 1u);

                    D3D_SHADER_CACHE_COMPILER_PROPERTIES compileParams;

                    VERIFY_SUCCEEDED(pApp->GetPrecompileTargets(1, &compileParams, D3D_SHADER_CACHE_TARGET_FLAG_NONE));

                    const wchar_t* psdPathReadback = nullptr;
                    VERIFY_SUCCEEDED(component->GetPrecompiledCachePath(compileParams.szAdapterFamily, &psdPathReadback));

                    D3D_SHADER_CACHE_PSDB_PROPERTIES psdb = {};
                    VERIFY_SUCCEEDED(component->GetPrecompiledShaderDatabases(1u, &psdb));
                }
            }
        }
    }

Windows Down Level Support:

While a D3D12 Runtime which supports interacting with this new API will be made available in-box for Windows 11 machines support will not be back ported to Windows 10 machines. While this means that already shipped applications running on Windows 10 won’t be able to benefit from platform level SODB/PSDB injection it won’t prevent new applications compiled against the latest D3D12 Agility SDK from using the shader caching APIs directly through D3D12. i.e. the lack of in-box support on Windows 10 should not be a concern for developers when they consider addressable market.