Partager via


Interroger la prise en charge et l'activation d'une fonctionnalité de WDDM

Cet article explique comment interroger la prise en charge et l'activation des fonctionnalités du modèle de pilote d'affichage Windows (WDDM). Il décrit :

  • Comment les pilotes d'affichage en mode utilisateur et en mode noyau (UMD et KMD) peuvent interroger le système d'exploitation pour déterminer si une fonctionnalité WDDM est prise en charge et activée sur un système.
  • Comment le système d'exploitation peut déterminer si un pilote supporte une fonctionnalité WDDM particulière.

Ce mécanisme d'interrogation des fonctionnalités est introduit à partir de Windows 11, version 24H2 (WDDM 3.2).

Aperçu des fonctionnalités de WDDM

WDDM peut être considéré comme une collection de fonctionnalités, où une fonctionnalité est une collection d'APIs/DDIs de WDDM qui couvrent une certaine fonctionnalité.

Une fonctionnalité est identifiée par son ID, qui se compose d'un ID de catégorie et d'un sous-ID pour la fonctionnalité elle-même au sein de la catégorie.

Chaque fonctionnalité connue du système d'exploitation est associée à des informations d'état permettant de déterminer si la fonctionnalité est prise en charge et/ou activée sur le système. Certaines fonctionnalités peuvent être des fonctionnalités pilotes. Une fonctionnalité de pilote nécessite un certain niveau de support de la part du pilote pour être activée. Dxgkrnl fournit un mécanisme de liaison pour déterminer la configuration des fonctionnalités. Une clé de registre peut remplacer la configuration des fonctionnalités pour chaque fonctionnalité et chaque adaptateur.

Les fonctionnalités du pilote peuvent également avoir une interface de fonctionnalité qui fournit les DDI du pilote liées à la fonctionnalité. En prenant en charge des interfaces de fonctionnalité individuelles, nous n'avons plus besoin de compter sur la mise à jour des interfaces principales entre le système d'exploitation et KMD, qui ne pouvaient auparavant être étendues qu'avec les changements de version de WDDM. Cette approche offre un moyen plus souple de rétroporter des fonctionnalités vers des systèmes d'exploitation antérieurs ou via des versions de Windows Moment, sans qu'il soit nécessaire de définir un support spécial.

Chaque fonctionnalité peut avoir une liste de dépendances qui doivent également être prises en charge en tant que prérequis. Chaque fonctionnalité peut avoir une liste de dépendances qui doivent également être prises en charge en tant que prérequi

Les fonctionnalités sont déclinées en plusieurs versions et peuvent avoir des interfaces ou des configurations différentes pour chaque version prise en charge.

WDDM introduit un ensemble d'API pour interroger l'état d'une fonctionnalité particulière. Ces API sont les suivantes :

Lorsqu'un pilote de miniport d'affichage est chargé, le pilote du port WDDM interroge toutes les fonctionnalités qui dépendent de la prise en charge par le pilote.

Le pilote peut interroger le pilote du port WDDM sur les fonctionnalités prises en charge lorsqu'il est chargé.

Définitions des fonctionnalités de WDDM

Une fonctionnalité est identifiée par son ID, qui est représenté par une valeur DXGK_FEATURE_ID. Une valeur DXGK_FEATURE_ID a le format suivant :

  • L'ID de catégorie de la fonctionnalité est une valeur DXGK_FEATURE_CATEGORY qui identifie la catégorie de la fonctionnalité. Il est stocké dans les 4 bits supérieurs de DXGK_FEATURE_ID.
  • Le sous-ID de la fonctionnalité qui identifie la fonctionnalité réelle au sein de la catégorie de fonctionnalité. Le sous-ID est stocké dans les 28 bits inférieurs de DXGK_FEATURE_ID.

Lorsque DXGK_FEATURE_CATEGORY est DXGK_FEATURE_CATEGORY_DRIVER, le sous-ID de la fonctionnalité est une valeur DXGK_DRIVER_FEATURE qui identifie la fonctionnalité réelle.

Fonctionnalités globales ou adaptatives

Les fonctionnalités individuelles correspondent soit à une fonctionnalité globale, soit à une fonctionnalité spécifique à un adaptateur. La documentation d'une fonctionnalité indique s'il s'agit d'une fonctionnalité globale. Il est important de connaître cette information lorsque vous demandez si une fonctionnalité est activée, car un paramètre hAdapter peut être nécessaire pour demander la configuration de la fonctionnalité spécifique à cet adaptateur ou pour utiliser la base de données globale.

Les fonctionnalités suivantes sont actuellement définies comme des fonctionnalités globales :

  • DXGK_FEATURE_GPUVAIOMMU

Virtualization

Pour le GPU-PV, le système d'exploitation négocie automatiquement la prise en charge et l'activation des fonctionnalités entre l'hôte et l'invité. Le pilote n'a pas besoin d'implémenter un support spécial pour de telles requêtes.

Dépendances

Chaque fonctionnalité peut spécifier une liste de dépendances. Ces dépendances sont liées à la définition de la fonctionnalité elle-même et sont codées en dur au moment de la compilation par le système d'exploitation.

Pour qu'une fonctionnalité particulière soit activée, toutes ses dépendances doivent l'être également.

Les fonctionnalités suivantes ont actuellement des dépendances :

  • DXGK_FEATURE_USER_MODE_SUBMISSION
  • DXGK_FEATURE_HWSCH
  • DXGK_FEATURE_NATIVE_FENCE

La documentation d'une fonctionnalité précise si celle-ci a des dépendances qui doivent également être activées.

Interrogation de la prise en charge des fonctionnalités par KMD

Le système d'exploitation utilise un mécanisme de liaison pour déterminer si le système d'exploitation et le pilote prennent en charge une fonctionnalité. Ce mécanisme permet à la demande initiale de savoir si une fonctionnalité est activée de provenir de n'importe quelle source (système d'exploitation/Dxgkrnl, KMD, UMD, Runtime, etc.), tout en disposant des mécanismes appropriés pour que le système d'exploitation et le pilote négocient la prise en charge de la fonctionnalité de support.

La KMD doit mettre en œuvre l'interface DXGKDDI_FEATURE_INTERFACE pour que le pilote du port puisse interroger la prise en charge des fonctionnalités. Le GUID de l'interface est GUID_WDDM_INTERFACE_FEATURE.

Si le pilote implémente l'interface DXGKDDI_FEATURE_INTERFACE, il n'a pas besoin d'appeler DxgkCbQueryFeatureSupport pour activer une fonctionnalité dans le pilote de port à l'avance. Il peut à la place interroger le support de fonctionnalité à la demande en utilisant l'interface de sa DXGKDDI_FEATURE_INTERFACE.

Interroger l'activation des fonctionnalités

Cette section décrit comment un composant vérifie si une fonctionnalité est activée sur le système. La structure DXGK_ISFEATUREENABLED_RESULT définit les résultats d'une requête de fonctionnalité.

Requête en mode utilisateur

Un client en mode utilisateur appelle D3DKMTIsFeatureEnabled pour demander si une fonctionnalité WDDM particulière est activée.

Requête en mode noyau

Pour obtenir le rappel permettant d'interroger la prise en charge des fonctionnalités, KMD doit interroger l'interface DxgkServicesFeature. Pour obtenir cette interface, KMD appelle la fonction de rappel DxgkCbQueryServices de Dxgkkrnl avec ServiceType défini sur une valeur DXGK_SERVICES de DxgkServicesFeature, comme le montre l'extrait de code suivant. La KMD peut appeler DxgkCbQueryServices une fois qu'elle a obtenu le pointeur de rappel lors d'un appel à son 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);

Vérification de la présence d'une fonctionnalité avant l'initialisation de Dxgkrnl

DxgkIsFeatureEnabled2 est défini dans la bibliothèque du pilote du port d'affichage (displib.h). Par conséquent, KMD peut appeler DxgkIsFeatureEnabled2 pour vérifier la présence d'une fonctionnalité avant l'initialisation de Dxgkrnl.

Comme cet appel est destiné à être utilisé à DriverEntry, seul un sous-ensemble de fonctionnalités globales peut être interrogé par ce biais. Ce sous-ensemble comprend actuellement

  • DXGK_FEATURE_GPUVAIOMMU

Remplacements dans le registre

Vous pouvez remplacer les configurations des fonctionnalités dans le registre pendant le développement et le test des pilotes. Cette possibilité est utile pour forcer une fonctionnalité particulière à être utilisable dans un environnement de développement lorsque la configuration par défaut de la fonctionnalité pourrait indiquer qu'elle n'est pas prise en charge.

Un pilote ne doit définir aucune de ces clés de registre dans son INF lors de l'installation du pilote. Ces clés ne sont destinées qu'à des fins de test et de développement, et non à remplacer globalement une fonctionnalité spécifique.

La configuration des fonctionnalités d'un pilote est stockée dans la clé logicielle PNP de l'adaptateur, sous une clé XXXX\Features\YYYY, où XXXX est l'identifiant de l'appareil attribué par PnP lors de l'installation de l'appareil et YYYY représente l'ID de la fonctionnalité. par exemple HKLM\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000\Features\4.

Les sections suivantes décrivent les remplacements qui peuvent être spécifiés pour une fonctionnalité.

Nom de la clé de registre : Activé

Nom Type Value Description
Enabled DWORD 0 (non pris en charge) ou 1 (pris en charge). La valeur par défaut dépend de la fonctionnalité. Remplace la prise en charge de la fonctionnalité par le système d'exploitation.

Malgré son nom, cette entrée ne remplace que la prise en charge d'une fonctionnalité par le système d'exploitation. Elle ne force pas la fonctionnalité à être toujours activée (c'est-à-dire qu'elle ne garantit pas qu'un appel à IsFeatureEnabled(ID) renvoie0 Enabled=TRUE). Une négociation correcte côté pilote est toujours nécessaire pour les fonctionnalités du pilote.

Nom de la clé de registre : MinVersion

Nom Type Value Description
MinVersion DWORD Dépend de la fonctionnalité Contraint la version minimale prise en charge pour cette fonctionnalité à être plus restrictive que la version minimale par défaut.

Cette valeur ne peut pas être utilisée pour forcer le système d'exploitation à prendre en charge une version inférieure à la version minimale prise en charge par défaut (car cette version minimale par défaut est choisie en raison de la prise en charge de l'implémentation). En revanche, cette clé peut contraindre la version minimale supportée à une valeur supérieure à la valeur par défaut.

Cette configuration est utile pour contourner un bogue qui pourrait être présent dans une version spécifique de l'implémentation de la fonctionnalité.

Si MinVersion est spécifié, MaxVersion doit également l'être.

Nom de la clé de registre : MaxVersion

Nom Type Value Description
MaxVersion DWORD Dépend de la fonctionnalité Contraint la version maximale prise en charge pour cette fonctionnalité à être plus restrictive que la version maximale par défaut.

Cette valeur ne peut pas être utilisée pour forcer le système d'exploitation à prendre en charge une version supérieure à la version maximale prise en charge par défaut (car ce maximum par défaut est choisi en raison de la prise en charge de l'implémentation). En revanche, cette clé peut contraindre la version maximale prise en charge à une valeur inférieure à la valeur par défaut.

Cette configuration est particulièrement utile pour contourner un bogue qui pourrait être présent dans une version spécifique de l'implémentation de la fonctionnalité.

Si MaxVersion est spécifié, MinVersion doit également l'être.

Nom de la clé de registre : AllowExperimental

Nom Type Value Description
AllowExperimental DWORD 0 (le support expérimental n'est pas autorisé) ou 1 (supporté). La valeur par défaut est définie par la branche. Force le système d'exploitation à autoriser le chargement de versions expérimentales de cette fonctionnalité, même si la construction ne l'autorise pas par défaut.

Le système d'exploitation définit généralement le support expérimental. Par défaut, le support expérimental est défini fonctionnalité par fonctionnalité, avec un remplacement global disponible sur les builds de développement (par exemple, les builds de développement internes autorisent toujours le support expérimental pour toutes les fonctionnalités, alors que les builds de pré-version peuvent n'autoriser le support que pour des fonctionnalités spécifiques).

Cette valeur permet d'outrepasser la définition du système d'exploitation pour un ID de fonctionnalité spécifique. Elle peut même être utilisée dans les versions préliminaires pour activer le support expérimental des pilotes sur un système d'exploitation qui supporte la fonctionnalité, tout en gardant la fonctionnalité désactivée dans un environnement de vente au détail.

Extension du débogueur : dxgkdx

L'extension dxgkdx du débogueur de noyau implémente une commande !feature qui peut interroger l'état de diverses fonctionnalités.

Les commandes actuellement prises en charge (avec un exemple de sortie) sont les suivantes :

!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 overriden.

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    

Exemple d’implémentation

Pour faciliter la prise en charge, un exemple d'implémentation dépouillé est présenté ci-dessous. Les pilotes peuvent utiliser ce code comme point de départ pour leur propre implémentation et l'enrichir de fonctionnalités supplémentaires si nécessaire (par exemple, en ajoutant des moyens de remplacer des capacités).


#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 overriden 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;
}

Le code suivant met en œuvre les interfaces de la fonctionnalité FEATURE_SAMPLE.

//
// 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;
}