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 은 기능 구성을 결정하는 핸드셰이크 메커니즘을 제공합니다. 레지스트리 키는 어댑터별로 기능별 구성을 재정의할 수 있습니다.
드라이버 기능에는 기능과 관련된 드라이버의 DDI를 제공하는 기능 인터페이스 가 있을 수도 있습니다. 개별 기능 인터페이스를 지원하여 이전에 업데이트된 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는 하위 28비트 DXGK_FEATURE_ID 저장됩니다.
DXGK_FEATURE_CATEGORY DXGK_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을 사용하여 Dxgkrnl의 DxgkCbQueryServices 콜백을 DxgkServicesFeature의 DXGK_SERVICES 값으로 호출합니다. 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는 DxgkRnl이 초기화되기 전에 DxgkIsFeatureEnabled2를 호출하여 기능의 존재에 대한 검사 수 있습니다.
이 호출은 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
속성 | 타입 | 값 | 설명 |
---|---|---|---|
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 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;
}