Freigeben über


Abfragen von WDDM-Featureunterstützung und -aktivierung

In diesem Artikel wird beschrieben, wie Sie die Unterstützung und Aktivierung von Windows Display Driver Model (WDDM)-Features abfragen. Folgendes wird erläutert:

  • Wie Benutzermodus- und Kernelmodusanzeigetreiber (UMD und KMD) das Betriebssystem abfragen können, um festzustellen, ob ein WDDM-Feature unterstützt und auf einem System aktiviert wird.
  • Wie das Betriebssystem bestimmen kann, ob ein Treiber ein bestimmtes WDDM-Feature unterstützt.

Dieser Featureabfragemechanismus wird ab Windows 11, Version 24H2 (WDDM 3.2) eingeführt.

Übersicht über die WDDM-Funktionen

WDDM kann als eine Sammlung von Features gesehen werden, wobei ein Feature eine Sammlung von WDDM-APIs/-DDIs ist, die bestimmte Funktionen abdecken.

Ein Feature wird durch seine Feature-ID identifiziert, die aus einer Kategorie-ID und einer Unter-ID für das Feature selbst innerhalb der Kategorie besteht.

Jedes Feature, das dem Betriebssystem bekannt ist, hat Statusinformationen zugeordnet, um festzustellen, ob das Feature unterstützt und/oder auf dem System aktiviert ist. Einige Features können Treiberfeatures sein. Ein Treiberfeature erfordert eine gewisse Unterstützung des Treibers, um aktiviert zu werden. Dxgkrnl stellt einen Handshake-Mechanismus bereit, um die Featurekonfiguration zu bestimmen. Ein Registrierungsschlüssel kann die Funktionskonfiguration für jede einzelne Funktion und jeden einzelnen Adapter außer Kraft setzen.

Treiberfeatures können auch über eine Featureschnittstelle verfügen, die die DDIs des Treibers im Zusammenhang mit dem Feature bereitstellt. Durch die Unterstützung einzelner Featureschnittstellen müssen wir nicht mehr die Hauptschnittstellen zwischen Betriebssystem und KMD aktualisieren, die zuvor nur mit aktualisierten WDDM-Versionsverwaltungsänderungen erweitert werden konnten. Dieser Ansatz bietet eine flexiblere Möglichkeit der Rückportierung von Funktionen auf frühere Betriebssysteme oder über Windows Moment-Versionen, ohne dass eine spezielle Unterstützung definiert werden muss.

Jede Funktion kann eine Liste von Abhängigkeiten haben, die als Voraussetzung ebenfalls unterstützt werden müssen. Für zukünftige Funktionen, die solche Abhängigkeiten benötigen, werden diese Abhängigkeiten in der entsprechenden Dokumentation angegeben.

Die Funktionen sind versioniert und können für jede unterstützte Version unterschiedliche Schnittstellen oder Konfigurationen haben.

WDDM führt eine Reihe von APIs ein, um einen bestimmten Featurezustand abzufragen. Diese APIs sind jetzt verfügbar:

Wenn ein Miniporttreiber geladen wird, fragt der WDDM-Porttreiber alle Features ab, die von der Treiberunterstützung abhängig sind.

Der Treiber kann den WDDM-Porttreiber nach den unterstützten Features abfragen, wenn er geladen wird.

WDDM-Featuredefinitionen

Ein Feature wird durch seine Feature-ID identifiziert, die als ein DXGK_FEATURE_ID-Wert dargestellt wird. Ein DXGK_FEATURE_ID-Wert weist das folgende Format auf:

  • Die Kategorie-ID des Features ist ein DXGK_FEATURE_CATEGORY-Wert, der die Kategorie des Features identifiziert. Er wird in den oberen 4 Bits von DXGK_FEATURE_ID gespeichert.
  • Die Unter-ID des Features, die das tatsächliche Feature in der Featurekategorie identifiziert. Die Unter-ID wird in den unteren 28 Bits von DXGK_FEATURE_ID gespeichert.

Wenn DXGK_FEATURE_CATEGORY gleich DXGK_FEATURE_CATEGORY_DRIVER ist, ist die Unter-ID des Features ein DXGK_DRIVER_FEATURE-Wert, der das tatsächliche Feature identifiziert.

Globale Features im Vergleich zu Adapterfeatures

Einzelne Features entsprechen entweder einem globalen Feature oder einem adapterspezifischen Feature. Die Dokumentation eines Features gibt an, ob es sich bei dem Feature um ein globales Feature handelt. Es ist wichtig, diese Informationen beim Abfragen, ob ein Feature aktiviert ist, zu kennen, da möglicherweise ein hAdapter-Parameter erforderlich ist, um die für diesen Adapter spezifische Featurekonfiguration abzufragen oder die globale Datenbank zu verwenden.

Die folgenden Features sind derzeit als globale Features definiert:

  • DXGK_FEATURE_GPUVAIOMMU

Virtualisierung

Bei GPU-PV verhandelt das Betriebssystem automatisch die Featureunterstützung und -aktivierung zwischen Host und Gast. Der Treiber muss keine spezielle Unterstützung für solche Abfragen implementieren.

Abhängigkeiten

Jedes Feature kann eine Liste von Abhängigkeiten angeben. Diese Abhängigkeiten sind an die Definition des Features selbst gebunden und werden zur Kompilierungszeit durch das Betriebssystem hartcodiert.

Damit ein bestimmtes Feature aktiviert werden kann, müssen auch alle Abhängigkeiten aktiviert werden.

Die folgenden Features weisen derzeit Abhängigkeiten auf:

  • DXGK_FEATURE_USER_MODE_SUBMISSION
  • DXGK_FEATURE_HWSCH
  • DXGK_FEATURE_NATIVE_FENCE

In der Dokumentation eines Features wird angegeben, ob ein Feature Abhängigkeiten aufweist, die ebenfalls aktiviert werden müssen.

Abfragefunktionsunterstützung von KMD

Das Betriebssystem verwendet einen Handshake-Mechanismus, um zu bestimmen, ob sowohl das Betriebssystem als auch der Treiber ein Feature unterstützen. Dieser Mechanismus ermöglicht die anfängliche Abfrage, ob ein Feature aus einer beliebigen Quelle (Betriebssystem/Dxgkrnl, KMD, UMD, Runtime usw.) stammt und dennoch über die entsprechenden Mechanismen für das Betriebssystem und den Treiber verfügt, um die Featureunterstützung auszuhandeln.

KMD muss die DXGKDDI_FEATURE_INTERFACE-Schnittstelle implementieren, damit der Porttreiber die Featureunterstützung abfragen kann. Die Schnittstellen-GUID ist GUID_WDDM_INTERFACE_FEATURE.

Wenn der Treiber DXGKDDI_FEATURE_INTERFACE implementiert, muss er DxgkCbQueryFeatureSupportnicht aufrufen, um ein Feature im Porttreiber vorab zu aktivieren. Er kann stattdessen die Unterstützung von Features bei Bedarf mithilfe der DXGKDDI_FEATURE_INTERFACE-Schnittstelle abfragen.

Aktivieren der Abfragefunktion

In diesem Abschnitt wird beschrieben, wie eine Komponente überprüft, ob ein Feature im System aktiviert ist. Die DXGK_ISFEATUREENABLED_RESULT-Struktur definiert die Ergebnisse einer Featureabfrage.

Benutzermodusabfrage

Ein Benutzermodusclient ruft D3DKMTIsFeatureEnabled auf, um abzufragen, ob ein bestimmtes WDDM-Feature aktiviert ist.

Kernelmodusabfrage

Um den Rückruf für die Abfragefunktionsunterstützung abzurufen, muss KMD die DxgkServicesFeature-Schnittstelle abfragen. Um diese Schnittstelle abzurufen, ruft KMD Dxgkrnl's DxgkCbQueryServices Rückruf auf, wobei ServiceType auf einen DXGK_SERVICES-Wert von DxgkServicesFeature gesetzt ist, wie im folgenden Codeausschnitt gezeigt. KMD kann DxgkCbQueryServices aufrufen, sobald der Rückrufzeiger in einem Aufruf seines DxgkDdiStartDevice abgerufen wird.

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

Überprüfen auf ein Feature, bevor Dxgkrnl initialisiert wird

DxgkIsFeatureEnabled2 wird in der Bibliothek des Anzeigeporttreibers (displib.h) definiert. Daher kann KMD DxgkIsFeatureEnabled2 aufrufen, um das Vorhandensein eines Features zu überprüfen, bevor Dxgkrnl initialisiert wird.

Da dieser Aufruf bei DriverEntry verwendet werden soll, kann nur eine Teilmenge der globalen Features abgefragt werden. Diese Teilmenge umfasst derzeit Folgendes:

  • DXGK_FEATURE_GPUVAIOMMU

Registrierungsüberschreibungen

Sie können Featurekonfigurationen in der Registrierung während der Treiberentwicklung und -tests außer Kraft setzen. Diese Funktion ist nützlich, um zu erzwingen, dass ein bestimmtes Feature in einer Entwicklungsumgebung verwendet werden kann, wenn die Standardfeaturekonfiguration darauf hindeutet, dass es nicht unterstützt wird.

Ein Treiber darf während der Treiberinstallation keinen dieser Registrierungsschlüssel in ihrer INF-definieren. Diese Schlüssel sind nur für Test- und Entwicklungszwecke vorgesehen und nicht für die allgemeine Außerkraftsetzung eines bestimmten Features.

Die Featurekonfiguration eines Treibers wird im PNP-Softwareschlüssel für den Adapter unter einem XXXX\Features\YYYY-Schlüssel gespeichert, wobei XXXX der Gerätebezeichner ist, der von PnP bei der Geräteinstallation zugewiesen wird, und YYYY die Feature-ID darstellt. z. B. HKLM\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000\Features\4.

In den folgenden Abschnitten werden Außerkraftsetzungen beschrieben, die für ein Feature angegeben werden können.

Name des Registrierungsschlüssels: Enabled

Name type Wert Beschreibung
Aktiviert DWORD 0 (nicht unterstützt) oder 1 (unterstützt). Der Standardwert ist featureabhängig. Überschreibt die Betriebssystemunterstützung für das Feature.

Trotz des Namens setzt dieser Eintrag nur die betriebssystemseitige Unterstützung eines Features außer Kraft. Es wird nicht erzwungen, dass das Feature immer aktiviert wird (d. h. es garantiert nicht, dass ein Aufruf von IsFeatureEnabled(ID) Enabled=TRUE) zurückgibt. Für Treiberfeatures ist weiterhin eine ordnungsgemäße treiberseitige Aushandlung erforderlich.

Registrierungsschlüsselname: MinVersion

Name type Wert Beschreibung
MinVersion DWORD Feature-abhängig Schränkt die minimale unterstützte Version für diese Funktion ein, so dass sie restriktiver ist als die standardmäßige Mindestversion.

Dieser Wert kann nicht verwendet werden, um das Betriebssystem zu zwingen, eine niedrigere Version als die standardmäßig unterstützte Mindestversion zu unterstützen (da dieses Standardminimum aufgrund der Implementierungsunterstützung gewählt wird). Stattdessen kann dieser Schlüssel die unterstützte Mindestversion auf einen Wert einschränken, der höher als der Standardwert ist.

Diese Konfiguration ist nützlich, um einen Fehler zu umgehen, der möglicherweise in einer bestimmten Version der Featureimplementierung vorhanden ist.

Wenn MinVersion angegeben ist, muss auch MaxVersion angegeben werden.

Name des Registrierungsschlüssels: MaxVersion

Name type Wert Beschreibung
MaxVersion DWORD Feature-abhängig Schränkt die maximale unterstützte Version für diese Funktion ein, so dass sie restriktiver ist als die standardmäßige Maximalversion.

Dieser Wert kann nicht verwendet werden, um das Betriebssystem zu zwingen, eine höhere Version als die standardmäßig unterstützte Maximalversion zu unterstützen (da dieses Standardmaximum aufgrund der Implementierungsunterstützung gewählt wird). Stattdessen kann dieser Schlüssel die unterstützte Maximalversion auf einen Wert einschränken, der niedriger als der Standardwert ist.

Diese Konfiguration ist teilweise nützlich, um einen Fehler zu umgehen, der möglicherweise in einer bestimmten Version der Featureimplementierung vorhanden ist.

Wenn MaxVersion angegeben ist, muss auch MinVersion angegeben werden.

Name des Registrierungsschlüssels: AllowExperimental

Name type Wert Beschreibung
AllowExperimental DWORD 0 (experimentelle Unterstützung ist nicht zulässig) oder 1 (unterstützt). Der Standardwert ist durch eine Zweigstelle definiert. Erzwingt das Betriebssystem, experimentelle Versionen dieses Features zu laden, auch wenn der Build es standardmäßig nicht zulässt.

Das Betriebssystem definiert in der Regel experimentelle Unterstützung. Standardmäßig wird die experimentelle Unterstützung für jedes einzelne Feature definiert, wobei die Entwicklungs-Builds global überschrieben werden können (interne Entwickler-Builds erlauben beispielsweise immer experimentelle Unterstützung für alle Features, während Vorabversions-Builds möglicherweise nur die Unterstützung für bestimmte Features erlauben).

Mit diesem Wert kann die Betriebssystemdefinition für eine bestimmte Feature-ID außer Kraft gesetzt werden. Sie kann sogar bei Releasebuilds verwendet werden, um experimentelle Treiberunterstützung auf einem Betriebssystem zu aktivieren, das die Funktion unterstützt, aber die Funktion in einer Verkaufsumgebung deaktiviert zu lassen.

Debuggererweiterung: dxgkdx

Die dxgkdx-Kerneldebuggererweiterung implementiert einen !feature-Befehl, der den Status verschiedener Features abfragen kann.

Die derzeit unterstützten Befehle (mit Beispielausgabe) sind:

!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    

Beispielimplementierung

Für eine einfache Unterstützung folgt eine Beispielimplementierung für Barebones. Treiber können diesen Code als Ausgangspunkt für ihre eigene Implementierung verwenden und bei Bedarf mit zusätzlichen Features erweitern (z. B. die Verbindung von Möglichkeiten zum Außerkraftsetzen von Funktionen).


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

Der folgende Code implementiert die Schnittstellen für das Feature 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;
}