查询 WDDM 功能支持和启用
本文介绍如何查询 Windows 显示驱动程序模型 (WDDM) 功能的支持和启用。 其中介绍了:
- 用户模式和内核模式显示驱动程序(UMD 和 KMD)如何查询 OS,以确定 WDDM 功能在系统上是否受支持和已启用。
- OS 如何确定驱动程序是否支持特定的 WDDM 功能。
从 Windows 11 版本 24H2 (WDDM 3.2) 开始引入此功能查询机制。
WDDM 功能概述
可以将 WDDM 视为功能集合,其中一项功能是涵盖某些功能的 WDDM API/DDI 的集合。
功能由功能 ID 标识,它由类别 ID 和类别中功能本身的子 ID 组成。
OS 已知的每个功能都有关联的状态信息,用于确定该功能在系统上是否受支持和/或已启用。 某些功能可以是驱动程序功能。 驱动程序功能需要启用驱动程序的某个级别的支持。 Dxgkrnl 提供握手机制来确定功能配置。 注册表项可以基于每个功能、每个适配器覆盖功能配置。
驱动程序功能还可以具有一个功能接口,该接口提供与该功能相关的驱动程序 DDI。 通过支持单个功能接口,我们不再需要依赖更新 OS 和 KMD 之间的主接口,后者以前只能使用更新的 WDDM 版本控制更改进行扩展。 此方法提供了一种更灵活的方式,用于向以前的 OS 或通过 Windows Moment 版本向后移植功能,而无需定义特殊支持。
每个功能都可以包含一个依赖项列表,作为先决条件,这些依赖项也必须受支持。 需要此类依赖项的未来功能将在其文档中指示其所需的依赖项。
功能会进行版本控制,并且每个受支持的版本均可以具有不同的接口或配置。
WDDM 引入了一组 API 来用于查询特定功能状态。 API 包括:
-
- 在调用 DxgkDdiStartDevice 后,KMD 可以查询并使用此接口来确定特定功能在系统中是否受支持和已启用。
- 此机制取代了以下现有 DDI:
-
- 此机制允许 OS 从 KMD 查询功能接口。
-
- 此函数是系统提供的 displib 库中的入口点。
- KMD 可以从其 DriverEntry 例程调用 DxgkIsFeatureEnabled2,而无需初始化 Dxgkrnl 来确定系统是否启用了特定功能。
-
- 此用户模式 API 允许用户模式模块确定是否启用了特定功能。
加载显示微型端口驱动程序时,WDDM 端口驱动程序会查询所有依赖于驱动程序支持的功能。
驱动程序可以在加载时查询 WDDM 端口驱动程序,以便获取受支持的功能。
WDDM 功能定义
功能由其功能 ID 标识,该 ID 表示为 DXGK_FEATURE_ID 值。 DXGK_FEATURE_ID 值采用以下格式:
- 功能的类别 ID 是标识功能类别的 DXGK_FEATURE_CATEGORY 值。 它存储在 DXGK_FEATURE_ID 的上 4 位。
- 用于标识功能类别中实际功能的功能子 ID。 子 ID 存储在 DXGK_FEATURE_ID 的底部 28 位中。
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、Runtime 等),并且仍具有适当的机制,使 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 需调用 Dxgkrnl 的 DxgkCbQueryServices 回调,其中将 ServiceType 设置为 DxgkServicesFeature 的 DXGK_SERVICES 值,如以下代码片段所示。 在调用 DxgkDdiStartDevice 时获得回调指针后,KMD 可以调用 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
。
以下部分介绍可为功能指定的覆盖。
注册表项名称:Enabled
名称 | 类型 | 值 | 说明 |
---|---|---|---|
已启用 | DWORD | 0(不受支持)或 1(受支持)。 默认值与功能相关。 | 覆盖功能的 OS 支持。 |
尽管名称如此,但此条目仅会覆盖功能的 OS 端支持。 它不会强制始终启用该功能(也就是说,不保证 IsFeatureEnabled(ID) 调用会返回 Enabled=TRUE)。 驱动程序功能仍需要适当的驱动程序端协商。
注册表项名称:MinVersion
名称 | 类型 | 值 | 说明 |
---|---|---|---|
MinVersion | DWORD | 与功能相关 | 将此功能支持的最低版本限制为比默认最低版本更严格。 |
此值不能用于强制 OS 支持低于默认最低支持版本的版本(因为此默认最低版本是由于实施支持而选择的)。 相反,此项可以将支持的最低版本限制为高于默认值的值。
此配置可用于解决特定版本的功能实施中可能存在的 Bug。
如果指定了 MinVersion,则还必须指定 MaxVersion。
注册表项名称:MaxVersion
名称 | 类型 | 值 | 说明 |
---|---|---|---|
MaxVersion | DWORD | 与功能相关 | 将此功能支持的最高版本限制为比默认最高版本更严格。 |
此值不能用于强制 OS 支持高于默认最高支持版本的版本(因为此默认最高版本是由于实施支持而选择的)。 相反,此项可以将支持的最高版本限制为低于默认值的值。
此配置尤其适用于解决特定版本的功能实施中可能存在的 Bug。
如果指定了 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
示例实现
为便于支持,请遵循裸机示例实施。 驱动程序可以将此代码用作自身实施的起点,并根据需要扩展其他功能(例如,挂接覆盖功能的方法)。
#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;
}