다음을 통해 공유


WDDM 기능 지원 및 활성화 조회

이 문서에서는 WDDM(Windows Display Driver Model) 기능의 지원 및 사용을 쿼리하는 방법을 설명합니다. 다음을 설명합니다.

  • 사용자 모드 및 커널 모드 표시 드라이버(UMD 및 KMD)가 OS를 쿼리하여 시스템에서 WDDM 기능이 지원되고 사용하도록 설정되어 있는지 여부를 확인하는 방법입니다.
  • OS에서 드라이버가 특정 WDDM 기능을 지원하는지 여부를 결정하는 방법입니다.

이 기능 쿼리 메커니즘은 Windows 11 버전 24H2(WDDM 3.2)부터 도입되었습니다.

WDDM 기능 개요

WDDM은 기능 컬렉션으로 볼 수 있습니다. 여기서 기능은 특정 기능을 다루는 WDDM API/DDI의 컬렉션입니다.

기능은 범주 내의 기능 자체에 대한 범주 ID하위 ID 구성된 기능 ID의해 식별됩니다.

OS에 알려진 각 기능에는 해당 기능이 시스템에서 지원 및/또는 사용하도록 설정되어 있는지 여부를 확인하기 위해 연결된 상태 정보가 있습니다. 일부 기능은 드라이버 기능일 수 있습니다. 드라이버 기능을 사용하려면 드라이버에서 어느 정도의 지원이 필요합니다. Dxgkrnl 기능 구성을 결정하는 핸드셰이크 메커니즘을 제공합니다. 레지스트리 키는 어댑터별로 기능별 구성을 재정의할 수 있습니다.

드라이버 기능에는 기능과 관련된 드라이버의 DDIs를 제공하는 기능 인터페이스이 있을 수도 있습니다. 개별 기능 인터페이스를 지원하여 이전에 업데이트된 WDDM 버전 관리 변경으로만 확장할 수 있었던 OS와 KMD 간의 기본 인터페이스를 업데이트하는 데 더 이상 의존할 필요가 없습니다. 이 방법은 특별한 지원을 정의할 필요 없이 이전 OS 또는 Windows Moment 릴리스를 통해 기능을 보다 유연하게 백포팅하는 방법을 제공합니다.

각 기능에는 필수 구성 요소로 지원되어야 하는 종속성 목록이 있을 수 있습니다. 이러한 종속성이 필요한 향후 기능은 설명서에 필요한 종속성을 나타냅니다.

기능은 버전이 지정되며 지원되는 각 버전에 대해 서로 다른 인터페이스 또는 구성을 가질 수 있습니다.

WDDM은 특정 기능 상태를 쿼리하는 API 집합을 도입했습니다. API에는 다음이 포함됩니다.

디스플레이 미니포트 드라이버가 로드되면 WDDM 포트 드라이버는 드라이버 지원에 의존하는 모든 기능을 쿼리합니다.

드라이버는 로드될 때 지원되는 기능에 대해 WDDM 포트 드라이버를 쿼리할 수 있습니다.

WDDM 기능 정의

기능은 DXGK_FEATURE_ID 값으로 표시되는 기능 ID의해 식별됩니다. DXGK_FEATURE_ID 값의 형식은 다음과 같습니다.

  • 기능의 범주 ID 기능의 범주를 식별하는 DXGK_FEATURE_CATEGORY 값입니다. 상위 4비트 DXGK_FEATURE_ID에 저장됩니다.
  • 기능 범주 내의 실제 기능을 식별하는 기능의 하위 ID. 하위 ID는 DXGK_FEATURE_ID의 하위 28비트에 저장됩니다.

DXGK_FEATURE_CATEGORYDXGK_FEATURE_CATEGORY_DRIVER경우 기능의 하위 ID는 실제 기능을 식별하는 DXGK_DRIVER_FEATURE 값입니다.

전역 및 어댑터 기능

개별 기능은 전역 기능 또는 어댑터 관련 기능에 해당합니다. 기능 설명서는 이 기능이 글로벌 기능인지 여부를 나타냅니다. 해당 어댑터와 관련된 기능 구성을 쿼리하거나 전역 데이터베이스를 사용하려면 hAdapter 매개 변수가 필요할 수 있으므로 기능을 사용할 수 있는지 여부를 쿼리할 때 이 정보를 알아야 합니다.

다음 기능은 현재 전역 기능으로 정의되어 있습니다.

  • DXGK_FEATURE_GPUVAIOMMU

가상화

GPU-PV의 경우 OS는 호스트와 게스트 간에 기능 지원 및 활성화를 자동으로 협상합니다. 드라이버는 이러한 쿼리에 대한 특별한 지원을 구현할 필요가 없습니다.

종속성

각 기능은 종속성 목록을 지정할 수 있습니다. 이러한 종속성은 기능 자체의 정의에 연결되며 OS에 의해 컴파일 시간에 하드 코딩됩니다.

특정 기능을 사용하도록 설정하려면 모든 종속성도 사용하도록 설정해야 합니다.

현재 다음 기능에는 종속성이 있습니다.

  • DXGK_FEATURE_USER_MODE_SUBMISSION (사용자 모드 제출 기능)
  • DXGK_FEATURE_HWSCH
  • DXGK_FEATURE_NATIVE_FENCE

기능의 설명서는 기능에도 사용하도록 설정해야 하는 종속성이 있는지 여부를 지정합니다.

KMD에서 기능 지원 쿼리

OS는 핸드셰이크 메커니즘을 사용하여 OS와 드라이버가 모두 기능을 지원하는지 여부를 확인합니다. 이 메커니즘을 사용하면 기능이 원본(OS/Dxgkrnl, KMD, UMD, 런타임 등)에서 시작되는지 여부를 초기 쿼리할 수 있으며 OS 및 드라이버가 기능 지원을 협상할 수 있는 적절한 메커니즘을 계속 사용할 수 있습니다.

KMD는 포트 드라이버가 기능 지원을 쿼리하기 위해 DXGKDDI_FEATURE_INTERFACE 인터페이스를 구현해야 합니다. 인터페이스 GUID는 GUID_WDDM_INTERFACE_FEATURE입니다.

드라이버가 DXGKDDI_FEATURE_INTERFACE구현하는 경우 DxgkCbQueryFeatureSupport 호출하여 포트 드라이버에서 기능을 미리 사용하도록 설정할 필요가 없습니다. 대신 DXGKDDI_FEATURE_INTERFACE인터페이스를 사용하여 요청 시 기능 지원을 쿼리할 수 있습니다.

기능 활성화를 위한 쿼리

이 섹션에서는 구성 요소가 시스템에서 기능을 사용할 수 있는지 여부를 확인하는 방법을 설명합니다. DXGK_ISFEATUREENABLED_RESULT 구조는 기능 쿼리의 결과를 정의합니다.

사용자 모드 쿼리

사용자 모드 클라이언트는 D3DKMTIsFeatureEnabled 호출하여 특정 WDDM 기능이 사용되는지 여부를 쿼리합니다.

커널 모드 쿼리

기능 지원을 쿼리하기 위한 콜백을 얻으려면 KMD에서 DxgkServicesFeature 인터페이스를 쿼리해야 합니다. 이 인터페이스를 얻기 위해, KMD는 다음 코드 조각에서 볼 수 있듯이, ServiceTypeDXGK_SERVICESDxgkServicesFeature로 설정하고 DxgkrnlDxgkCbQueryServices 콜백을 호출합니다. KMD는 DxgkDdiStartDevice호출에서 콜백 포인터를 가져오면 DxgkCbQueryServices를 호출할 수 있습니다.

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 기능 ID나타냅니다. 예제는 HKLM\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000\Features\4입니다.

다음 섹션에서는 기능에 대해 지정할 수 있는 재정의에 대해 설명합니다.

레지스트리 키 이름: 활성화됨

이름 유형 가치 설명
활성화됨 DWORD (더블 워드) 0(지원되지 않음) 또는 1(지원됨). 기본값은 기능 종속입니다. 기능에 대한 OS 지원을 재정의합니다.

이름에도 불구하고 이 항목은 OS 쪽 기능 지원만 재정의합니다. 기능을 항상 사용하도록 설정하지는 않습니다.(즉, IsFeatureEnabled(ID)에 대한 호출이 Enabled=TRUE)를 반환하도록 보장하지는 않습니다. 드라이버 기능에는 적절한 운전자 측 협상이 여전히 필요합니다.

레지스트리 키 이름: MinVersion

이름 유형 가치 설명
MinVersion DWORD (더블 워드) 기능 의존적 이 기능에 대해 지원되는 최소 버전을 기본 최소 버전보다 더 제한적으로 제한합니다.

이 값은 OS가 지원되는 기본 최소 버전보다 낮은 버전을 지원하도록 강제하는 데 사용할 수 없습니다(이 기본 최소값은 구현 지원으로 인해 선택됨). 대신 이 키는 지원되는 최소 버전을 기본값보다 높은 값으로 제한할 수 있습니다.

이 구성은 특정 버전의 기능 구현에 있을 수 있는 버그를 해결하는 데 유용합니다.

MinVersion 지정된 경우 MaxVersion 지정해야 합니다.

레지스트리 키 이름: MaxVersion

이름 유형 가치 설명
MaxVersion DWORD (더블 워드) 기능 종속 이 기능에 대해 지원되는 최대 버전을 기본 최대 버전보다 더 제한적으로 제한합니다.

이 값은 OS가 지원되는 기본 최대 버전보다 높은 버전을 지원하도록 강제하는 데 사용할 수 없습니다(이 기본 최대값은 구현 지원으로 인해 선택됨). 대신 이 키는 지원되는 최대 버전을 기본값보다 낮은 값으로 제한할 수 있습니다.

이 구성은 특정 버전의 기능 구현에 있을 수 있는 버그를 해결하는 데 특히 유용합니다.

MaxVersion 지정된 경우 MinVersion 지정해야 합니다.

레지스트리 키 이름: AllowExperimental

이름 유형 가치 설명
실험 기능 허용 DWORD (더블 워드) 0(실험적 지원은 허용되지 않음) 또는 1(지원됨). 기본값은 분기 정의입니다. 빌드에서 기본적으로 허용하지 않는 경우에도 OS에서 이 기능의 실험적 버전을 로드하도록 허용합니다.

OS는 일반적으로 실험적 지원을 정의합니다. 기본적으로 실험적 지원은 기능별로 정의되며, 개발 빌드에서 사용할 수 있는 전역 재정의가 있습니다(예: 내부 개발자 빌드는 항상 모든 기능에 대한 실험적 지원을 허용하지만 시험판 빌드는 특정 기능에 대해서만 지원을 허용할 수 있음).

이 값을 사용하면 특정 기능 ID에 대해 OS 정의를 재정의할 수 있습니다. 릴리스 빌드에서 이 기능을 지원하는 OS에서 실험적 드라이버 지원을 제공하는 데도 사용할 수 있지만 소매 환경에서는 기능을 사용하지 않도록 설정할 수 있습니다.

디버거 확장: 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 overridden.

2: kd> !dxgkdx.feature config

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

!feature state

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

2: kd> !dxgkdx.feature state

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

샘플 구현

지원의 용이성을 위해 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 overridden dynamically at runtime during DRIVER_ADAPTER::InitializeFeatureConfiguration
//
static const DRIVER_FEATURE_DESC g_FeatureDefaults[] =
{
//                                      SupportedOnConfig
//    VersionRange           Supported  |       Experimental  
//    |                      |          |       |
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_HWSCH
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_HWFLIPQUEUE
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_LDA_GPUPV
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_KMD_SIGNAL_CPU_EVENT
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_USER_MODE_SUBMISSION
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_SHARE_BACKING_STORE_WITH_KMD
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved 
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // Reserved
    { VERSION_RANGE(3, 5), { TRUE,      TRUE,   FALSE,   }, }, // DXGK_FEATURE_TEST_FEATURE_SAMPLE
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_PAGE_BASED_MEMORY_MANAGER
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_KERNEL_MODE_TESTING
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_64K_PT_DEMOTION_FIX
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_GPUPV_PRESENT_HWQUEUE
    { VERSION_RANGE(0, 0), { FALSE,     FALSE,  FALSE,   }, }, // DXGK_FEATURE_NATIVE_FENCE
};
static_assert(ARRAYSIZE(g_FeatureDefaults) == DXGK_FEATURE_MAX, "New feature requires a descriptor");

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

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

    return &m_FeatureDescs[FeatureId];
}

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

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

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

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

    DRIVER_ADAPTER* pAdapter = GetAdapterFromHandle(hAdapter);

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

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

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

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

    return STATUS_SUCCESS;
}

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

    DRIVER_ADAPTER* pAdapter = GetAdapterFromHandle(hAdapter);

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

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

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

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

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

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

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

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

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

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

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

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

    return STATUS_SUCCESS;
}

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

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

    PAGED_CODE();

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

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

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

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

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

        return STATUS_SUCCESS;
    }
    else
    {
        return STATUS_INVALID_PARAMETER;
    }
}

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

    DRIVER_ADAPTER* pAdapter = GetAdapterFromHandle(hAdapter);

    pArgs->Result = {};

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

        //
        // Example experimental status
        //

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

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

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

        return Status;
    }
    else
    {
        return STATUS_NOT_SUPPORTED;
    }
}

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

    ...

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

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

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

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

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

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

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

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

    ...
}

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

    DRIVER_FEATURE_RESULT Result = {};

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

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

    return Result;
}

다음 코드는 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;
}