이 문서에서는 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에는 다음이 포함됩니다.
-
- KMD는 DxgkDdiStartDevice호출된후 시스템에서 특정 기능이 지원되고 활성화되는지 여부를 확인하기 위해 이 인터페이스를 쿼리하고 사용할 수 있습니다.
- 이 메커니즘은 다음과 같은 기존 DDI를 대체합니다.
-
- 이 메커니즘을 사용하면 OS가 KMD에서 기능 인터페이스를 쿼리할 수 있습니다.
-
- 이 함수는 시스템 제공 displib 라이브러리의 진입점입니다.
- KMD는 Dxgkrnl초기화하지 않고 DriverEntry 루틴에서 DxgkIsFeatureEnabled2 호출하여 시스템에 특정 기능이 활성화되어 있는지 여부를 확인할 수 있습니다.
-
- 이 사용자 모드 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는 다음 코드 조각에서 볼 수 있듯이, ServiceType을 DXGK_SERVICES 값 DxgkServicesFeature로 설정하고 Dxgkrnl의 DxgkCbQueryServices 콜백을 호출합니다. 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;
}