Поделиться через


Поддержка и включение функций WDDM

В этой статье описывается, как запрашивать поддержку и включение функций модели драйвера windows (WDDM). В нем описано:

  • Как драйверы отображения в пользовательском режиме и режиме ядра (UMD и KMD) могут запрашивать ОС, чтобы определить, поддерживается ли функция WDDM и включена ли она в системе.
  • Как ОС может определить, поддерживает ли драйвер определенную функцию WDDM.

Этот механизм запроса функций представлен начиная с Windows 11 версии 24H2 (WDDM 3.2).

Общие сведения о функциях WDDM

WDDM можно просматривать как коллекцию функций, где функция представляет собой коллекцию API-интерфейсов или DDIS WDDM, охватывающих определенные функциональные возможности.

Компонент определяется его идентификатором функции, который состоит из идентификаторакатегории и под id для самой функции в категории.

Каждая функция, известная операционной системе, содержит связанные сведения о состоянии, чтобы определить, поддерживается ли эта функция и /или включена в системе. Некоторые функции могут быть функциями драйвера. Для функции драйвера требуется включить некоторый уровень поддержки от драйвера. Dxgkrnl предоставляет механизм подтверждения для определения конфигурации компонентов. Раздел реестра может переопределить конфигурацию компонентов на основе каждого компонента.

Функции драйвера также могут иметь интерфейс функций, предоставляющий DDIS драйвера, связанные с этой функцией. Поддерживая отдельные интерфейсы функций, нам больше не нужно полагаться на обновление основных интерфейсов между ОС и KMD, которые ранее могли быть развернуты только с обновленными изменениями версий WDDM. Этот подход обеспечивает более гибкие средства поддержки функций для предыдущих операционных систем или через выпуски Windows Moment без специальной поддержки.

Каждая функция может иметь список зависимостей, которые также должны поддерживаться в качестве необходимых компонентов. Будущие функции, требующие таких зависимостей, будут указывать их необходимые зависимости в их документации.

Компоненты имеют версии и могут иметь разные интерфейсы или конфигурации для каждой поддерживаемой версии.

WDDM представляет набор API для запроса определенного состояния функции. Эти API позволяют решать следующие задачи:

  • DXGK_FEATURE_INTERFACE

    • KMD может запрашивать и использовать этот интерфейс, чтобы определить, поддерживается ли определенная функция и включена в системе после вызова DxgkDdiStartDevice.
    • Этот механизм заменяет следующие существующие DDIS:
  • DXGKDDI_FEATURE_INTERFACE

    • Этот механизм позволяет ОС запрашивать интерфейс компонента из KMD.
  • DxgkIsFeatureEnabled2

    • Эта функция является точкой входа в системной библиотеке дисплибов .
    • KMD может вызывать DxgkIsFeatureEnabled2 из своей подпрограммы DriverEntry без инициализации Dxgkrnl , чтобы определить, включена ли в системе определенная функция.
  • D3DKMTIsFeatureEnabled

    • Этот API пользовательского режима позволяет модулю пользовательского режима определить, включена ли определенная функция.

При загрузке драйвера мини-порта WDDM запрашивает все функции, зависящие от поддержки драйверов.

Драйвер может запросить драйвер порта WDDM для поддерживаемых функций при загрузке.

Определения функций WDDM

Компонент определяется его идентификатором функции, который представлен как DXGK_FEATURE_ID значение. Значение DXGK_FEATURE_ID имеет следующий формат:

  • Идентификатор категории компонента — это значение DXGK_FEATURE_CATEGORY, определяющее категорию функции. Он хранится в верхних 4 битах DXGK_FEATURE_ID.
  • Вложенный идентификатор компонента, определяющий фактическую функцию в категории компонентов. Вложенный идентификатор хранится в нижнем 28 битах DXGK_FEATURE_ID.

Если DXGK_FEATURE_CATEGORY DXGK_FEATURE_CATEGORY_DRIVER, идентификатор вложенного компонента представляет собой значение DXGK_DRIVER_FEATURE, определяющее фактическую функцию.

Функции глобального адаптера и адаптера

Отдельные функции соответствуют глобальной или определенной функции адаптера. Документация по компоненту указывает, является ли функция глобальной. Важно знать эти сведения при запросе на включение функции, так как параметр hAdapter может потребоваться для запроса конфигурации компонентов, относящуюся к этому адаптеру, или использовать глобальную базу данных.

Следующие функции в настоящее время определяются как глобальные функции:

  • DXGK_FEATURE_GPUVAIOMMU

Виртуализация

Для GPU-PV ОС автоматически согласовывает поддержку функций и включение между узлом и гостевым. Драйверу не нужно реализовать специальную поддержку таких запросов.

Зависимости

Каждая функция может указать список зависимостей. Эти зависимости привязаны к определению самой функции и жестко закодируются во время компиляции ОС.

Чтобы включить определенную функцию, необходимо также включить все его зависимости.

Следующие функции в настоящее время имеют зависимости:

  • DXGK_FEATURE_USER_MODE_SUBMISSION
  • DXGK_FEATURE_HWSCH
  • DXGK_FEATURE_NATIVE_FENCE

Документация компонента указывает, имеет ли компонент какие-либо зависимости, которые также должны быть включены.

Поддержка функций запроса из KMD

ОС использует механизм подтверждения, чтобы определить, поддерживает ли ОС и драйвер функцию. Этот механизм позволяет первоначально запрашивать, включена ли функция из любого источника (OS/Dxgkrnl, KMD, UMD, Runtime и т. д.), и по-прежнему имеет соответствующие механизмы для операционной системы и драйвера для согласования поддержки функций.

KMD необходимо реализовать интерфейс DXGKDDI_FEATURE_INTERFACE , чтобы драйвер портов запрашивал поддержку функций. GUID интерфейса GUID_WDDM_INTERFACE_FEATURE.

Если драйвер реализует DXGKDDI_FEATURE_INTERFACE, он не должен вызывать DxgkCbQueryFeatureSupport, чтобы включить функцию в драйвере портов заранее. Он может вместо этого запрашивать поддержку функций по запросу с помощью интерфейса DXGKDDI_FEATURE_INTERFACE.

Включение функций запроса

В этом разделе описывается, как компонент проверка, включен ли компонент в системе. Структура DXGK_ISFEATUREENABLED_RESULT определяет результаты запроса функции.

Запрос в режиме пользователя

Клиент в пользовательском режиме вызывает D3DKMTIsFeatureEnabled , чтобы запрашивать, включена ли определенная функция WDDM.

Запрос в режиме ядра

Чтобы получить обратный вызов для поддержки функций запроса, KMD необходимо запросить интерфейс DxgkServicesFeature . Чтобы получить этот интерфейс, KMD вызывает обратный вызов DxgkCbQueryServices в DXgkCbQueryServices с параметром ServiceType, заданным DXGK_SERVICES значение dxgkServicesFeature, как показано в следующем фрагменте кода. KMD может вызывать DxgkCbQueryServices после получения указателя обратного вызова в вызове 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);

Проверка наличия функции перед инициализацией Dxgkrnl

DxgkIsFeatureEnabled2 определяется в библиотеке драйвера портов отображения (displib.h). В результате KMD может вызвать DxgkIsFeatureEnabled2, чтобы проверка для присутствия функции до инициализации Dxgkrnl.

Так как этот вызов предназначен для использования в DriverEntry, через него можно запросить только подмножество глобальных функций. В настоящее время этот подмножество включает:

  • DXGK_FEATURE_GPUVAIOMMU

Переопределения реестра

Конфигурации компонентов можно переопределить в реестре во время разработки и тестирования драйверов. Эта возможность полезна для принудительного использования определенной функции в среде разработки, когда конфигурация компонентов по умолчанию может указывать, что она не поддерживается.

Драйвер не должен определять какие-либо из этих разделов реестра в INF во время установки драйвера. Эти ключи предназначены только для тестирования и разработки, а не для широкого переопределения определенной функции.

Конфигурация компонентов драйвера хранится в ключе программного обеспечения PNP для адаптера под ключом XXXX\Features\YYYY, где XXXX — это идентификатор устройства, назначенный PnP при установке устройства, и YYYY представляет идентификатор функции. Например, HKLM\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000\Features\4.

В следующих разделах описываются переопределения, которые можно указать для функции.

Имя раздела реестра: включено

Имя. Тип значение Описание
Включено DWORD 0 (не поддерживается) или 1 (поддерживается). Значение по умолчанию зависит от компонентов. Переопределяет поддержку ОС для функции.

Несмотря на имя, эта запись переопределяет поддержку компонента на стороне ОС. Она не принудительно включает функцию (т. е. не гарантирует, что вызов IsFeatureEnabled(ID) возвращает Значение Enabled=TRUE. Для функций драйверов по-прежнему требуется надлежащее согласование на стороне драйвера.

Имя раздела реестра: MinVersion

Имя. Тип значение Описание
MinVersion DWORD Зависимость от компонентов Ограничивает минимальную поддерживаемую версию этой функции более строгой, чем минимальная версия по умолчанию.

Это значение нельзя использовать для принудительной поддержки ОПЕРАЦИОННОй системы, чем минимальная поддерживаемая версия по умолчанию (так как это минимальное значение по умолчанию выбрано из-за поддержки реализации). Вместо этого этот ключ может ограничить минимальную поддерживаемую версию значением выше значения по умолчанию.

Эта конфигурация полезна для обхода ошибки, которая может присутствовать в определенной версии реализации функции.

Если задан minVersion, необходимо также указать MaxVersion.

Имя раздела реестра: MaxVersion

Имя. Тип значение Описание
MaxVersion DWORD Зависимость от компонентов Ограничивает максимальную поддерживаемую версию этой функции более строгой, чем максимальная версия по умолчанию.

Это значение нельзя использовать для принудительной поддержки ОПЕРАЦИОННОй системы, превышающей максимальную поддерживаемую версию по умолчанию (так как это максимальное значение по умолчанию выбирается из-за поддержки реализации). Вместо этого этот ключ может ограничить максимальную поддерживаемую версию значением ниже значения по умолчанию.

Эта конфигурация особенно полезна для работы с ошибкой, которая может присутствовать в определенной версии реализации функции.

Если задано значение MaxVersion, необходимо также указать MinVersion.

Имя раздела реестра: AllowExperimental

Имя. Тип значение Описание
AllowExperimental DWORD 0 (экспериментальная поддержка не разрешена) или 1 (поддерживается). Значение по умолчанию определяется ветвью. Принудительно заставляет ОС разрешить загрузку экспериментальных версий этой функции, даже если сборка не разрешает ее по умолчанию.

Ос обычно определяет экспериментальную поддержку. По умолчанию экспериментальная поддержка определяется на основе функций с глобальным переопределением, доступным для сборок разработки (например, внутренние сборки разработчиков всегда разрешают экспериментальную поддержку для всех функций, в то время как предварительные сборки могут разрешать только поддержку конкретных функций).

Это значение позволяет переопределить определение ОС для определенного идентификатора компонента. Его можно использовать даже в сборках выпусков для создания поддержки экспериментального драйвера в ОС, поддерживающей эту функцию, но сохраняйте функцию отключенной в розничной среде.

Расширение отладчика: dxgkdx

Расширение отладчика ядра dxgkdx реализует !feature команду, которая может запрашивать состояние различных функций.

В настоящее время поддерживаются команды (с примером выходных данных):

!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    

Простой пример реализации

Для удобства поддержки следует использовать пример реализации barebones. Драйверы могут использовать этот код в качестве отправной точки для собственной реализации, а также расширять дополнительные функции при необходимости (например, перехватывание способов переопределения возможностей).


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

Следующий код реализует интерфейсы для функции 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;
}