Ondersteuning en inschakeling van WDDM-functies opvragen

In dit artikel wordt beschreven hoe u query's kunt uitvoeren op de ondersteuning en activering van WDDM-functies (Windows Display Driver Model). Hierin wordt het volgende beschreven:

  • Hoe gebruikersmodus- en kernelmodusweergavestuurprogramma's (UMD en KMD) het besturingssysteem kunnen opvragen om te bepalen of een WDDM-functie wordt ondersteund en ingeschakeld op een systeem.
  • Hoe het besturingssysteem kan bepalen of een stuurprogramma een bepaalde WDDM-functie ondersteunt.

Dit mechanisme voor het opvragen van functies wordt geïntroduceerd vanaf Windows 11, versie 24H2 (WDDM 3.2).

Overzicht van WDDM-functies

WDDM kan worden weergegeven als een verzameling functies, waarbij een functie een verzameling WDDM API's/DDIs is die betrekking hebben op bepaalde functionaliteit.

Een functie wordt geïdentificeerd door de bijbehorende functie-id, die bestaat uit een categorie-id en een sub-id voor de functie zelf in de categorie.

Elke functie die bekend is bij het besturingssysteem, heeft statusinformatie gekoppeld om te bepalen of de functie wordt ondersteund en/of ingeschakeld op het systeem. Sommige functies kunnen stuurprogrammafuncties zijn. Voor een stuurprogrammafunctie moet een bepaald ondersteuningsniveau van het stuurprogramma zijn ingeschakeld. Dxgkrnl biedt een handshake-mechanisme om de functieconfiguratie te bepalen. Een registersleutel kan de functieconfiguratie per functie per adapter overschrijven.

Stuurprogrammafuncties kunnen ook een functie-interface hebben die de DDIS's van het stuurprogramma biedt die betrekking hebben op de functie. Door afzonderlijke functie-interfaces te ondersteunen, hoeven we niet langer te vertrouwen op het bijwerken van de hoofdinterfaces tussen het besturingssysteem en KMD, die voorheen alleen konden worden uitgebreid met bijgewerkte WDDM-versiebeheerwijzigingen. Deze aanpak biedt een flexibelere manier om backporteerfuncties naar eerdere besturingssystemen of via Windows Moment-releases te maken zonder speciale ondersteuning te hoeven definiëren.

Elke functie kan een lijst met afhankelijkheden hebben die ook als vereiste moeten worden ondersteund. Toekomstige functies waarvoor dergelijke afhankelijkheden zijn vereist, geven de vereiste afhankelijkheden in hun documentatie aan.

Functies zijn van versies voorzien en kunnen verschillende interfaces of configuraties hebben voor elke ondersteunde versie.

WDDM introduceert een set API's om een query uit te voeren op een bepaalde functiestatus. De API's zijn onder andere:

Wanneer een stuurprogramma voor de miniportweergave wordt geladen, vraagt het WDDM-poortstuurprogramma alle functies op die afhankelijk zijn van stuurprogrammaondersteuning.

Het stuurprogramma kan een query uitvoeren op het WDDM-poortstuurprogramma voor de ondersteunde functies wanneer het wordt geladen.

WDDM-functiedefinities

Een functie wordt geïdentificeerd door de functie-id, die wordt weergegeven als een DXGK_FEATURE_ID waarde. Een DXGK_FEATURE_ID-waarde heeft de volgende indeling:

  • De categorie-id van de functie is een DXGK_FEATURE_CATEGORY waarde waarmee de categorie van de functie wordt geïdentificeerd. Deze wordt opgeslagen in de bovenste 4 bits van DXGK_FEATURE_ID.
  • De sub-id van de functie waarmee de werkelijke functie in de functiecategorie wordt geïdentificeerd. De sub-id wordt opgeslagen in de onderste 28 bits van DXGK_FEATURE_ID.

Wanneer DXGK_FEATURE_CATEGORY is DXGK_FEATURE_CATEGORY_DRIVER, is de sub-id van de functie een DXGK_DRIVER_FEATURE waarde waarmee de werkelijke functie wordt geïdentificeerd.

Algemene versus adapterfuncties

Afzonderlijke functies komen overeen met een globale functie of een adapterspecifieke functie. De documentatie van een functie geeft aan of de functie een globale functie is. Het is belangrijk om deze informatie te weten bij het uitvoeren van query's of een functie is ingeschakeld, omdat mogelijk een hAdapter-parameter is vereist om een query uit te voeren op de functieconfiguratie die specifiek is voor die adapter, of de globale database te gebruiken.

De volgende functies zijn momenteel gedefinieerd als globale functies:

  • DXGK_FEATURE_GPUVAIOMMU

Virtualisatie

Voor GPU-PV onderhandelt het besturingssysteem automatisch over functieondersteuning en inschakeling tussen de host en gast. Het stuurprogramma hoeft geen speciale ondersteuning voor dergelijke query's te implementeren.

Afhankelijkheden

Elke functie kan een lijst met afhankelijkheden opgeven. Deze afhankelijkheden zijn gekoppeld aan de definitie van de functie zelf en worden vastgelegd tijdens het compileren door het besturingssysteem.

Als u een bepaalde functie wilt inschakelen, moeten alle bijbehorende afhankelijkheden ook worden ingeschakeld.

De volgende functies hebben momenteel afhankelijkheden:

  • DXGK_FEATURE_USER_MODE_SUBMISSION
  • DXGK_FEATURE_HWSCH
  • DXGK_FEATURE_NATIVE_FENCE

De documentatie van een functie geeft aan of een functie afhankelijkheden heeft die ook moeten worden ingeschakeld.

Ondersteuning voor queryfuncties van KMD

Het besturingssysteem maakt gebruik van een handshakingmechanisme om te bepalen of zowel het besturingssysteem als het stuurprogramma een functie ondersteunen. Met dit mechanisme kan de eerste query worden uitgevoerd om te bepalen of een functie afkomstig is van een bron (het besturingssysteem/Dxgkrnl, KMD, UMD, Runtime, enzovoort) en nog steeds over de juiste mechanismen beschikt voor het besturingssysteem en stuurprogramma om te onderhandelen over functieondersteuning.

KMD moet de DXGKDDI_FEATURE_INTERFACE-interface implementeren om de functieondersteuning van het poortstuurprogramma op te vragen. De interface-GUID is GUID_WDDM_INTERFACE_FEATURE.

Als het stuurprogramma DXGKDDI_FEATURE_INTERFACE implementeert, hoeft het geen DxgkCbQueryFeatureSupport aan te roepen om vooraf een functie in het poortstuurprogramma in te schakelen. In plaats daarvan kan het de ondersteuning voor functies op aanvraag opvragen met behulp van de interface van DXGKDDI_FEATURE_INTERFACE.

Functie-inschakeling opvragen

In deze sectie wordt beschreven hoe een onderdeel controleert of een functie is ingeschakeld op het systeem. De DXGK_ISFEATUREENABLED_RESULT-structuur definieert de resultaten van een functiequery.

Query in de gebruikersmodus

Een client in de gebruikersmodus roept D3DKMTIsFeatureEnabled aan om een query uit te voeren of een bepaalde WDDM-functie is ingeschakeld.

Kernelmodusquery

Om de callback voor ondersteuning van queryfuncties te verkrijgen, moet KMD de DxgkServicesFeature-interface opvragen . Om deze interface te verkrijgen, roept KMD dxgkrnl'sDxgkCbQueryServices callback aan met ServiceType ingesteld op een DXGK_SERVICES waarde van DxgkServicesFeature, zoals wordt weergegeven in het volgende codefragment. KMD kan DxgkCbQueryServices aanroepen zodra de callback-pointer is verkregen in een aanroep naar DxgkDdiStartDevice.

DXGK_FEATURE_INTERFACE FeatureInterface = {};
FeatureInterface.Size = sizeof(pDevExt->FeatureInterface);
FeatureInterface.Version = DXGK_FEATURE_INTERFACE_VERSION_1;
Status = DxgkInterface.DxgkCbQueryServices(DxgkInterface.DeviceHandle, DxgkServicesFeature, (PINTERFACE)&FeatureInterface);

Controleren op een functie voordat Dxgkrnl wordt geïnitialiseerd

DxgkIsFeatureEnabled2 is gedefinieerd in de bibliotheek van het beeldschermpoortstuurprogramma (displib.h). Als gevolg hiervan kan KMD DxgkIsFeatureEnabled2 aanroepen om te controleren op de aanwezigheid van een functie voordat Dxgkrnl wordt geïnitialiseerd.

Omdat deze aanroep bedoeld is om te worden gebruikt bij DriverEntry, kan er alleen een subset van globale functies worden opgevraagd. Deze subset bevat momenteel:

  • DXGK_FEATURE_GPUVAIOMMU

Registeroverschrijvingen

U kunt functieconfiguraties in het register overschrijven tijdens het ontwikkelen en testen van stuurprogramma's. Deze mogelijkheid is handig om af te dwingen dat een bepaalde functie bruikbaar is in een ontwikkelomgeving wanneer de standaardfunctieconfiguratie erop kan wijzen dat deze niet wordt ondersteund.

Een stuurprogramma mag geen van deze registersleutels in hun INF definiëren tijdens de installatie van het stuurprogramma. Deze sleutels zijn alleen bedoeld voor test- en ontwikkelingsdoeleinden en niet om een specifieke functie breed te overschrijven.

De functieconfiguratie van een stuurprogramma wordt opgeslagen in de PNP-softwaresleutel voor de adapter, onder een XXXX\Features\JJJJ-sleutel , waarbij XXXX de apparaat-id is die door PnP wordt toegewezen wanneer het apparaat is geïnstalleerd en JJJJ de functie-id vertegenwoordigt. Een voorbeeld is HKLM\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000\Features\4.

In de volgende secties worden "overrides" beschreven die kunnen worden gespecificeerd voor een onderdeel.

Registersleutelnaam: ingeschakeld

Naam Typ Waarde Beschrijving
Ingeschakeld DWORD 0 (niet ondersteund) of 1 (ondersteund). De standaardwaarde is functieafhankelijk. Overschrijft de ondersteuning van het besturingssysteem voor de functie.

Ondanks de naam overschrijft deze vermelding alleen de ondersteuning aan de besturingssysteemzijde van een functie. Het dwingt niet af dat de functie altijd is ingeschakeld (dat wil gezegd, het garandeert niet dat een aanroep naar IsFeatureEnabled(ID) Enabled=TRUE retourneert. Er is nog steeds een goede onderhandeling aan de bestuurderszijde vereist voor stuurprogrammafuncties.

Registersleutelnaam: MinVersion

Naam Typ Waarde Beschrijving
MinimaleVersie DWORD Functieafhankelijk Hiermee beperkt u de minimaal ondersteunde versie voor deze functie tot meer beperkend dan de standaard minimumversie.

Deze waarde kan niet worden gebruikt om het besturingssysteem te dwingen om een versie te ondersteunen die lager is dan de standaardversie die minimaal wordt ondersteund (omdat dit standaard minimum wordt gekozen vanwege ondersteuning voor implementatie). In plaats daarvan kan deze sleutel de minimaal ondersteunde versie beperken tot een waarde die hoger is dan de standaardwaarde.

Deze configuratie is handig om een fout te omzeilen die mogelijk aanwezig is in een specifieke versie van de functie-implementatie.

Als MinVersion is opgegeven, moet MaxVersion ook worden opgegeven.

Registersleutelnaam: MaxVersion

Naam Typ Waarde Beschrijving
MaxVersie DWORD Functieafhankelijk Hiermee beperkt u de maximaal ondersteunde versie voor deze functie tot meer beperkend dan de standaard maximumversie.

Deze waarde kan niet worden gebruikt om het besturingssysteem te dwingen een versie te ondersteunen die hoger is dan de standaard maximaal ondersteunde versie (omdat dit standaard maximum wordt gekozen vanwege ondersteuning voor implementatie). In plaats daarvan kan deze sleutel de maximaal ondersteunde versie beperken tot een waarde lager dan de standaardwaarde.

Deze configuratie is met name handig om een fout te omzeilen die mogelijk aanwezig is in een specifieke versie van de functie-implementatie.

Als MaxVersion is opgegeven, moet MinVersion ook worden opgegeven.

Registersleutelnaam: AllowExperimental

Naam Typ Waarde Beschrijving
AllowExperimental DWORD 0 (experimentele ondersteuning is niet toegestaan) of 1 (ondersteund). De standaardinstelling is gedefinieerd door een afdeling. Dwingt het besturingssysteem toe dat experimentele versies van deze functie worden geladen, zelfs als de build dit niet standaard toestaat.

Het besturingssysteem definieert doorgaans experimentele ondersteuning. Standaard wordt experimentele ondersteuning gedefinieerd op basis van functies, met een globale overschrijving die beschikbaar is voor ontwikkelingsversies (zo staan interne ontwikkelaarsversies bijvoorbeeld altijd experimentele ondersteuning voor alle functies toe, terwijl pre-release versies alleen ondersteuning voor specifieke functies toestaan).

Met deze waarde kan de definitie van het besturingssysteem worden overschreven voor een specifieke functie-id. Het kan zelfs worden gebruikt op release-builds om experimentele stuurprogrammaondersteuning te bieden op een besturingssysteem dat de functie ondersteunt, maar de functie uitgeschakeld houden in een retailomgeving.

Extensie voor foutopsporingsprogramma: dxgkdx

De extensie dxgkdx kernelfoutopsporingsprogramma implementeert een !feature opdracht waarmee de status van verschillende functies kan worden opgevraagd.

De momenteel ondersteunde opdrachten (met voorbeelduitvoer) zijn:

!feature list

Lists features with descriptor information

2: kd> !dxgkdx.feature list

  Id  FeatureName                                       Supported  Version  VirtMode     Global  Driver
   0  HWSCH                                             Yes        1-1      Negotiate    -       X
   1  HWFLIPQUEUE                                       Yes        1-1      Negotiate    -       X
   2  LDA_GPUPV                                         Yes        1-1      Negotiate    -       X
   3  KMD_SIGNAL_CPU_EVENT                              Yes        1-1      Negotiate    -       X
   4  USER_MODE_SUBMISSION                              Yes        1-1      Negotiate    -       X
   5  SHARE_BACKING_STORE_WITH_KMD                      Yes        1-1      HostOnly     -       X
  32  PAGE_BASED_MEMORY_MANAGER                         No         1-1      Negotiate    -       X
  33  KERNEL_MODE_TESTING                               Yes        1-1      Negotiate    -       X
  34  64K_PT_DEMOTION_FIX                               Yes        1-1      DeferToHost  -       -
  35  GPUPV_PRESENT_HWQUEUE                             Yes        1-1      DeferToHost  -       -
  36  GPUVAIOMMU                                        Yes        1-1      None         X       -
  37  NATIVE_FENCE                                      Yes        1-1      Negotiate    -       X

!feature config

Lists the current configuration information for each feature. In most cases, this will be unspecified/default values if not overridden.

2: kd> !dxgkdx.feature config

  Id  FeatureName                                       Enabled Version  AllowExperimental
   0  HWSCH                                             --       --       -
   1  HWFLIPQUEUE                                       --       --       -
   2  LDA_GPUPV                                         --       --       -
   3  KMD_SIGNAL_CPU_EVENT                              --       --       -
   4  USER_MODE_SUBMISSION                              --       --       -
   5  SHARE_BACKING_STORE_WITH_KMD                      --       --       -
  32  PAGE_BASED_MEMORY_MANAGER                         --       --       -
  33  KERNEL_MODE_TESTING                               --       --       -
  34  64K_PT_DEMOTION_FIX                               --       --       -
  35  GPUPV_PRESENT_HWQUEUE                             --       --       -
  36  GPUVAIOMMU                                        --       --       -
  37  NATIVE_FENCE                                      --       --       -

!feature state

Lists the current state of each feature. Features that have bnot been queried will have an unknown state

2: kd> !dxgkdx.feature state

  Id  FeatureName                                       Enabled  Version  Driver  Config
   0  HWSCH                                             No       0        No      No    
   1  HWFLIPQUEUE                                       No       0        No      No    
   2  LDA_GPUPV                                         No       0        No      No    
   3  KMD_SIGNAL_CPU_EVENT                              Yes      1        Yes     Yes   
   4  USER_MODE_SUBMISSION                              No       0        No      No    
   5  SHARE_BACKING_STORE_WITH_KMD                      Unknown  --       --      --    
  32  PAGE_BASED_MEMORY_MANAGER                         No       0        No      No    
  33  KERNEL_MODE_TESTING                               No       0        No      No    
  34  64K_PT_DEMOTION_FIX                               Unknown  --       --      --    
  35  GPUPV_PRESENT_HWQUEUE                             Unknown  --       --      --    
  36  GPUVAIOMMU                                        Unknown  --       --      --    
  37  NATIVE_FENCE                                      No       0        No      No    

Voorbeeld van implementatie

Voor eenvoudige ondersteuning volgt een voorbeeld van een barebones-implementatie. Bestuurders kunnen deze code gebruiken als uitgangspunt voor hun eigen implementatie en indien nodig uitbreiden met extra functies (bijvoorbeeld manieren koppelen om capaciteiten te overschrijven).


#include "precomp.h"

#pragma code_seg("PAGE")

#define VERSION_RANGE(Min, Max) Min, Max
#define DEFINE_FEATURE_INTERFACE(Name, Version, InterfaceStruct) InterfaceStruct Name##_Interface_##Version =
#define DEFINE_FEATURE_INTERFACE_TABLE(Name) const DRIVER_FEATURE_INTERFACE_TABLE_ENTRY Name##_InterfaceTable[] =
#define FEATURE_INTERFACE_ENTRY(Name, Version) { &Name##_Interface_##Version, sizeof(Name##_Interface_##Version) }
#define NO_FEATURE_INTERFACE { nullptr, 0 }
#define FEATURE_INTERFACE(Name, Version) { &Name##_Interface_##Version, sizeof(Name##_Interface_##Version) }
#define FEATURE_INTERFACE_TABLE(Name) { Name##_InterfaceTable, ARRAYSIZE(Name##_InterfaceTable) }
#define NO_FEATURE_INTERFACE_TABLE { nullptr, 0 }

struct DRIVER_FEATURE_INTERFACE_TABLE_ENTRY
{
    const void* Interface;
    SIZE_T InterfaceSize;
};

struct DRIVER_FEATURE_INTERFACE_TABLE
{
    const DRIVER_FEATURE_INTERFACE_TABLE_ENTRY* Entries;
    SIZE_T Count;
};

//
// Interfaces
//

DEFINE_FEATURE_INTERFACE(FEATURE_SAMPLE, 4, DXGKDDIINT_FEATURE_SAMPLE_4)
{
    DdiFeatureSample_AddValue,
};

DEFINE_FEATURE_INTERFACE(FEATURE_SAMPLE, 5, DXGKDDIINT_FEATURE_SAMPLE_5)
{
    DdiFeatureSample_AddValue,
    DdiFeatureSample_SubtractValue,
};

DEFINE_FEATURE_INTERFACE_TABLE(FEATURE_SAMPLE)
{
    NO_FEATURE_INTERFACE,                 // Version 3
    FEATURE_INTERFACE(FEATURE_SAMPLE, 4), // Version 4
    FEATURE_INTERFACE(FEATURE_SAMPLE, 5), // Version 5
};

static const DRIVER_FEATURE_INTERFACE_TABLE g_FeatureInterfaceTables[] =
{
    NO_FEATURE_INTERFACE_TABLE,              // DXGK_FEATURE_HWSCH
    NO_FEATURE_INTERFACE_TABLE,              // DXGK_FEATURE_HWFLIPQUEUE
    NO_FEATURE_INTERFACE_TABLE,              // DXGK_FEATURE_LDA_GPUPV
    NO_FEATURE_INTERFACE_TABLE,              // DXGK_FEATURE_KMD_SIGNAL_CPU_EVENT
    NO_FEATURE_INTERFACE_TABLE,              // DXGK_FEATURE_USER_MODE_SUBMISSION
    NO_FEATURE_INTERFACE_TABLE,              // DXGK_FEATURE_SHARE_BACKING_STORE_WITH_KMD
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved 
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    NO_FEATURE_INTERFACE_TABLE,              // Reserved
    FEATURE_INTERFACE_TABLE(FEATURE_SAMPLE), // DXGK_FEATURE_SAMPLE
    NO_FEATURE_INTERFACE_TABLE,              // DXGK_FEATURE_PAGE_BASED_MEMORY_MANAGER
    NO_FEATURE_INTERFACE_TABLE,              // DXGK_FEATURE_KERNEL_MODE_TESTING
    NO_FEATURE_INTERFACE_TABLE,              // DXGK_FEATURE_64K_PT_DEMOTION_FIX
    NO_FEATURE_INTERFACE_TABLE,              // DXGK_FEATURE_GPUPV_PRESENT_HWQUEUE
    NO_FEATURE_INTERFACE_TABLE,              // DXGK_FEATURE_NATIVE_FENCE
};
static_assert(ARRAYSIZE(g_FeatureInterfaceTables) == DXGK_FEATURE_MAX, "New feature must define an interface table");

#define VERSION_RANGE(Min, Max) Min, Max

//
// TODO: This table may be defined independently for each supported hardware or architecture,
// or may be completely overridden dynamically at runtime during DRIVER_ADAPTER::InitializeFeatureConfiguration
//
static const DRIVER_FEATURE_DESC g_FeatureDefaults[] =
{
//                                      SupportedOnConfig
//    VersionRange           Supported  |       Experimental  
//    |                      |          |       |
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_HWSCH
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_HWFLIPQUEUE
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_LDA_GPUPV
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_KMD_SIGNAL_CPU_EVENT
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_USER_MODE_SUBMISSION
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_SHARE_BACKING_STORE_WITH_KMD
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved 
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(3, 5), { TRUE,      TRUE,   FALSE,   }, }, // DXGK_FEATURE_TEST_FEATURE_SAMPLE
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_PAGE_BASED_MEMORY_MANAGER
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_KERNEL_MODE_TESTING
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_64K_PT_DEMOTION_FIX
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_GPUPV_PRESENT_HWQUEUE
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_NATIVE_FENCE
};
static_assert(ARRAYSIZE(g_FeatureDefaults) == DXGK_FEATURE_MAX, "New feature requires a descriptor");

const DRIVER_FEATURE_DESC*
DRIVER_ADAPTER::GetFeatureDesc(
    DXGK_FEATURE_ID FeatureId
    ) const
{
    PAGED_CODE();

    if(FeatureId >= DXGK_FEATURE_MAX)
    {
        return nullptr;
    }

    return &m_FeatureDescs[FeatureId];
}

void
DRIVER_ADAPTER::InitializeFeatureConfiguration(
    )
{
    //
    // Choose correct default table to use here, or override manually below
    //
    static_assert(sizeof(DRIVER_ADAPTER::m_FeatureDescs) == sizeof(g_FeatureDefaults));
    memcpy(m_FeatureDescs, g_FeatureDefaults, sizeof(g_FeatureDefaults));
    
    //
    // Example overrides
    //

    //
    // While this is a sample feature and not tied to any architectural support, this is
    // an example of how a feature can be marked as supported by the driver in the table
    // above, and then dynamically enabled on this configuration here.
    //
    // The same can be done for hardware features, such as hardware scheduling
    //
    if(IsSampleFeatureSupportedOnThisGPU())
    {
        m_FeatureDescs[DXGK_FEATURE_TEST_FEATURE_SAMPLE].SupportedOnConfig = TRUE;
    }
}

NTSTATUS
DdiQueryFeatureSupport(
    IN_CONST_HANDLE                    hAdapter,
    INOUT_PDXGKARG_QUERYFEATURESUPPORT pArgs
    )
{
    PAGED_CODE();

    //
    // Start by assuming the feature is unsupported
    //
    pArgs->SupportedByDriver = FALSE;
    pArgs->SupportedOnCurrentConfig = FALSE;
    pArgs->MinSupportedVersion = 0;
    pArgs->MaxSupportedVersion = 0;

    DRIVER_ADAPTER* pAdapter = GetAdapterFromHandle(hAdapter);

    const DRIVER_FEATURE_DESC* pFeatureDesc = pAdapter->GetFeatureDesc(pArgs->FeatureId);

    if(pFeatureDesc == nullptr)
    {
        //
        // Unknown feature
        //
        return STATUS_INVALID_PARAMETER;
    }

    if(pFeatureDesc->Supported)
    {
        if(pFeatureDesc->Experimental == FALSE ||
           pArgs->AllowExperimental)
        {
            pArgs->SupportedByDriver = TRUE;
            pArgs->SupportedOnCurrentConfig = pFeatureDesc->SupportedOnConfig;

            pArgs->MinSupportedVersion = pFeatureDesc->MinSupportedVersion;
            pArgs->MaxSupportedVersion = pFeatureDesc->MaxSupportedVersion;
        }
    }

    return STATUS_SUCCESS;
}

NTSTATUS
DdiQueryFeatureInterface(
    IN_CONST_HANDLE                      hAdapter,
    INOUT_PDXGKARG_QUERYFEATUREINTERFACE pArgs
    )
{
    PAGED_CODE();

    DRIVER_ADAPTER* pAdapter = GetAdapterFromHandle(hAdapter);

    UINT16 InterfaceSize = pArgs->InterfaceSize;
    pArgs->InterfaceSize = 0;

    const DRIVER_FEATURE_DESC* pFeatureDesc = pAdapter->GetFeatureDesc(pArgs->FeatureId);

    if(pFeatureDesc == nullptr)
    {
        //
        // Unknown feature
        //
        return STATUS_INVALID_PARAMETER;
    }

    if(pFeatureDesc->Supported == FALSE)
    {
        //
        // Cannot query a feature interface for an unsupported feature.
        //
        return STATUS_UNSUCCESSFUL;
    }

    if(pArgs->Version < pFeatureDesc->MinSupportedVersion ||
       pArgs->Version > pFeatureDesc->MaxSupportedVersion)
    {
        //
        // Invalid feature version.
        //
        return STATUS_UNSUCCESSFUL;
    }

    const DRIVER_FEATURE_INTERFACE_TABLE* pInterfaceTable = &g_FeatureInterfaceTables[pArgs->FeatureId];
    if(pInterfaceTable->Entries == nullptr)
    {
        //
        // This feature does not have any interfaces. It's unclear why the driver is asking for it,
        // but the size should be zero and we will not return any data for it.
        //
        return STATUS_SUCCESS;
    }

    if((SIZE_T)(pArgs->Version - pFeatureDesc->MinSupportedVersion) >= pInterfaceTable->Count)
    {
        //
        // The interface table should have an entry for every supported version. This is
        // a bug in the OS, and the feature interface table must be updated for this feature!
        //
        NT_ASSERT(FALSE);

        //
        // Invalid feature version.
        //
        return STATUS_UNSUCCESSFUL;
    }

    UINT32 InterfaceTableIndex = pArgs->Version - pFeatureDesc->MinSupportedVersion;
    const DRIVER_FEATURE_INTERFACE_TABLE_ENTRY* pInterfaceEntry = &pInterfaceTable->Entries[InterfaceTableIndex];
    if(pInterfaceEntry->Interface == nullptr)
    {
        //
        // This feature does not have any interfaces. It's unclear why the OS is asking for one.
        //
        return STATUS_INVALID_PARAMETER;
    }

    if(InterfaceSize < pInterfaceEntry->InterfaceSize)
    {
        //
        // The driver-provided buffer is too small to store the interface for this feature and version
        //
        return STATUS_BUFFER_TOO_SMALL;
    }

    //
    // We have an interface!
    //
    RtlCopyMemory(pArgs->Interface, pInterfaceEntry->Interface, pInterfaceEntry->InterfaceSize);
    if(InterfaceSize != pInterfaceEntry->InterfaceSize)
    {
        //
        // Zero out remainder of interface in case the provided buffer was larger than
        // the actual interface. This may be done in cases where multiple interface versions
        // are supported simultaneously (e.g. in a unioned structure). Only the requested
        // interface should be valid.
        //
        RtlZeroMemory((BYTE*)pArgs->Interface + pInterfaceEntry->InterfaceSize, InterfaceSize - pInterfaceEntry->InterfaceSize);
    }

    //
    // Write back the interface size
    //
    pArgs->InterfaceSize = (UINT16)pInterfaceEntry->InterfaceSize;

    return STATUS_SUCCESS;
}

static void DdiReferenceFeatureInterfaceNop(PVOID pMiniportDeviceContext)
{
    PAGED_CODE();
}

//
// DRIVER_INITIALIZATION_DATA::DxgkDdiQueryInterface
//
NTSTATUS
DdiQueryInterface(
    IN_CONST_PVOID          pMiniportDeviceContext,
    IN_PQUERY_INTERFACE     pQueryInterface
    )
{
    DDI_FUNCTION();

    PAGED_CODE();

    if(pQueryInterface->Version == DXGK_FEATURE_INTERFACE_VERSION_1)
    {
        PDXGKDDI_FEATURE_INTERFACE Interface = (PDXGKDDI_FEATURE_INTERFACE)pQueryInterface->Interface;

        Interface->Version = DXGK_FEATURE_INTERFACE_VERSION_1;
        Interface->Context = pMiniportDeviceContext;
        Interface->Size = sizeof(DXGKDDI_FEATURE_INTERFACE);

        //
        // Returned interface shouldn't be larger than size provided for Interface
        //
        if (Interface->Size > pQueryInterface->Size)
        {
            return STATUS_BUFFER_TOO_SMALL;
        }

        Interface->InterfaceReference = DdiReferenceFeatureInterfaceNop;
        Interface->InterfaceDereference = DdiReferenceFeatureInterfaceNop;

        Interface->QueryFeatureSupport = DdiQueryFeatureSupport;
        Interface->QueryFeatureInterface = DdiQueryFeatureInterface;

        return STATUS_SUCCESS;
    }
    else
    {
        return STATUS_INVALID_PARAMETER;
    }
}

//
// These two functions act as hooks for when the OS doesn't support the feature functionality.
// If DxgkInterface.DxgkCbQueryServices(DxgkServicesFeature) returns a failure, it may mean
// we're running on an older OS, and we can fake the interface implementation using these
// functions instead.
//
// See DdiStartDevice sample code for how this is used
//
NTSTATUS
LegacyIsFeatureEnabled(
    IN_CONST_PVOID                     hDevice,
    INOUT_PDXGKARGCB_ISFEATUREENABLED2 pArgs
    )
{
    PAGED_CODE();

    DRIVER_ADAPTER* pAdapter = GetAdapterFromHandle(hAdapter);

    pArgs->Result = {};

    if (pAdapter->m_WddmVersion >= DXGKDDI_WDDMv2_9 &&
        pAdapter->m_DxgkInterface.Version >= DXGKDDI_INTERFACE_VERSION_WDDM2_9)
    {
        //
        // QueryFeatureSupport should be available.
        //
        DXGKARGCB_QUERYFEATURESUPPORT Args = {};
        Args.DeviceHandle = pAdapter->m_DxgkInterface.DeviceHandle;
        Args.FeatureId = pArgs->FeatureId;

        //
        // Example experimental status
        //

        /*
        switch(pArgs->FeatureId)
        {
            case DXGK_FEATURE_HWFLIPQUEUE:
            {
                Args.DriverSupportState = DXGK_FEATURE_SUPPORT_EXPERIMENTAL;
                break;
            }

            default:
            {
                Args.DriverSupportState = DXGK_FEATURE_SUPPORT_STABLE;
                break;
            }
        }
        */

        NTSTATUS Status = pAdapter->m_DxgkInterface.DxgkCbQueryFeatureSupport(&Args);
        if(NT_SUCCESS(Status))
        {
            if(Args.Enabled)
            {
                pArgs->Result.Enabled = Args.Enabled;
                pArgs->Result.Version = 1;
                pArgs->Result.SupportedByDriver = TRUE;
                pArgs->Result.SupportedOnCurrentConfig = TRUE;
            }
        }

        return Status;
    }
    else
    {
        return STATUS_NOT_SUPPORTED;
    }
}

//
// Sample code for DdiStartDevice
//
NTSTATUS
DdiStartDevice(
    IN_CONST_PVOID          pMiniportDeviceContext,
    IN_PDXGK_START_INFO     pDxgkStartInfo,
    IN_PDXGKRNL_INTERFACE   pDxgkInterface,
    OUT_PULONG              pNumberOfVideoPresentSources,
    OUT_PULONG              pNumberOfChildren
    )
{
    DRIVER_ADAPTER* pAdapter = GetAdapterFromHandle(hAdapter);

    ...

    //
    // Check fi the OS supports the feature interface.
    //
    pAdapter->m_FeatureInterface.Size = sizeof(pAdapter->m_FeatureInterface);
    pAdapter->m_FeatureInterface.Version = DXGK_FEATURE_INTERFACE_VERSION_1;

    Status = pAdapter->m_DxgkInterface.DxgkCbQueryServices(pAdapter->m_DxgkInterface.DeviceHandle, DxgkServicesFeature, (PINTERFACE)&pAdapter->m_FeatureInterface);

    if(!NT_SUCCESS(Status))
    {
        //
        // OS interface unavailable. This forwards calls to the Legacy functions defined above
        // when not available, which hard codes support for the handful of existing features
        // at the time (optionally going through DxgkCbQueryFeatureSupport).
        //
        // Doing this is optional, but may keep the driver code cleaner.
        //

        pAdapter->m_FeatureInterface.Context = pAdapter;
        pAdapter->m_FeatureInterface.InterfaceReference = nullptr;
        pAdapter->m_FeatureInterface.InterfaceDereference = nullptr;

        //
        // Use legacy function above.
        //
        pAdapter->m_FeatureInterface.IsFeatureEnabled = LegacyIsFeatureEnabled;

        //
        // QueryFeatureInterface is only used by the OS to query an interface for a feature,
        // but the OS doesn't support this. Any attempt to call this function implies
        // the driver is calling it themselves, which makes no sense.
        //
        pAdapter->m_FeatureInterface.QueryFeatureInterface = nullptr;

        Status = STATUS_SUCCESS;
    }
    
    Status = pAdapter->InitializeFeatureConfiguration();

    if(!NT_SUCCESS(Status))
    {
        goto cleanup;
    }

    ...
}

DRIVER_FEATURE_RESULT
DRIVER_ADAPTER::IsFeatureEnabled(
    DXGK_FEATURE_ID FeatureId
    )
{
    PAGED_CODE();

    DRIVER_FEATURE_RESULT Result = {};

    DXGKARGCB_ISFEATUREENABLED2 Args = {};
    Args.FeatureId = FeatureId;

    //
    // Will either call the OS, or the LegacyIsFeatureEnabled function above
    // depending on whether this is supported on the OS. 
    //
    if(NT_SUCCESS(FeatureInterface.IsFeatureEnabled(DxgkInterface.DeviceHandle, &Args)))
    {
        Result.Enabled = Args.Result.Enabled;
        Result.Version = Args.Result.Version;
    }

    return Result;
}

Met de volgende code worden de interfaces voor de FEATURE_SAMPLE-functie geïmplementeerd.

//
// This file implements the interfaces for the FEATURE_SAMPLE feature
//

#include "precomp.h"

//
// The OS supports 3 versions of the feature: 3, 4, and 5.
//
// - v3 has no interface
// - v4 has an interface that defines an "Add" function
// - v5 has an interface that defines both "Add" and "Subtract" functions
//
NTSTATUS
APIENTRY CALLBACK
DdiFeatureSample_AddValue(
    IN_CONST_HANDLE hAdapter,
    INOUT_PDXGKARG_FEATURE_SAMPLE_ADDVALUE pArgs
    )
{
    PAGED_CODE();

    DRIVER_ADAPTER* pAdapter = GetAdapterFromHandle(hAdapter);

    if(pAdapter->m_FeatureState.SampleFeatureVersion < 4)
    {
        //
        // Unexpected. This function should only be called for v4 and above of this feature
        //
        return STATUS_INVALID_PARAMETER;
    }

    DXGKARGCB_FEATURE_SAMPLE_GETVALUE GetValueArgs = {};

    NTSTATUS Status = pAdapter->m_FeatureState.SampleFeatureInterface.GetValue(pAdapter->m_DxgkInterface.DeviceHandle, &GetValueArgs);

    if(!NT_SUCCESS(Status))
    {
        return Status;
    }

    pArgs->OutputValue = pArgs->InputValue + GetValueArgs.Value;

    return STATUS_SUCCESS;
}

NTSTATUS
APIENTRY CALLBACK
DdiFeatureSample_SubtractValue(
    IN_CONST_HANDLE hAdapter,
    INOUT_PDXGKARG_FEATURE_SAMPLE_SUBTRACTVALUE pArgs
    )
{
    PAGED_CODE();

    DRIVER_ADAPTER* pAdapter = GetAdapterFromHandle(hAdapter);

    if(pAdapter->m_FeatureState.SampleFeatureVersion < 5)
    {
        //
        // Unexpected. This function should only be called for v5 and above of this feature
        //
        return STATUS_INVALID_PARAMETER;
    }

    DXGKARGCB_FEATURE_SAMPLE_GETVALUE GetValueArgs = {};

    NTSTATUS Status = pAdapter->m_FeatureState.SampleFeatureInterface.GetValue(pAdapter->m_DxgkInterface.DeviceHandle, &GetValueArgs);
    
    if(!NT_SUCCESS(Status))
    {
        return Status;
    }

    pArgs->OutputValue = pArgs->InputValue - GetValueArgs.Value;

    return STATUS_SUCCESS;
}