欢迎转载【作者:张佩】【原文:http://www.yiiyee.cn/Blog/wddm2/】
第二部分专门只讲VIDPN。这是后面内容的基础。WDDM框架用VIDPN这个概念,来描述它所要处理的显示关系。
1. VIDPN
VIDPN的全称是Video Present Network,这个因为词组不太好翻译(直译可以是:视频提交网络,但颇为难听),所以一般都直接讲它的英文。
VIDPN是WDDM引入的概念,用来描述通过显卡设备(Adapter)建立的若干个显示源(Source)和若干个目标接口(Target)之间的关系。系统按照VIDPN所定义的方式,将一个或多个显示源(Source)通过显卡设备,输出到这些目标接口(Target)上。
1.1 基本元素
组成VIDPN网络的,是下面这样一些基本元素:
显示源(Source):这是一系列可提交到显卡设备,并显示出来的图像内容。
源模式(Source Mode):一个源(Source)需要一个模式(Mode)来定义它的属性,包括长、宽、颜色格式和比特深度(Bit Depth)等。
源模式集合(Source Mode Set):一个源(Source)的所有模式(Mode)的集合(Set),称为源模式集合。网络中所有源的模式集合,称为集合集(Sets)。
目标(Target):一系列物理接口,显卡设备通过它们来输出源内容。可以把它们理解成显卡上的多个接口。虽然不是很准确,但如果你愿意,也可以直接把它们理解成接在显卡设备上的若干个显示器设备——这里之所以讲不是很准确,因为从接口到最终显示,中间还存在显示器设备对图像内容的处理。作为显示器设备,另有元素Monitor Source Mode Set来标识它所能够支持的Mode。但KMDOD项目中并没有用到这个元素,它默认由显示设备自己处理Target Mode和Monitor Mode间关系。
目标模式(Target Mode):一个目标(Target)也需要一个模式(Mode)来定义它的属性,它比源更复杂一些,除了长、宽、颜色格式和深度外,还有刷新率、缓冲区长度(Pitch)等。
目标模式集合(Source Mode Set):一个目标(Target)的所有模式(Mode)集合(Set),就是源模式集合。网络中所有目标的模式集合,称为集合集(Sets)。
确定的模式(Pinned Mode):在一条Path中,不管Source还是Target,在另一方Mode确定的情况下,选择出来的能够和另一方相匹配的一个模式,称为确定的模式(Pinned Mode)。Pin是钉子的意思,即表示板上钉钉的Mode。
路径(Path):把一个源(Source)和一个目标(Target)之间的连接,称为一个路径(Path)。VIDPN网络中可能存在若干个Path,但至少有一个Path。
拓扑结构(Topology):由若干个Path组成的结构,称为拓扑结构。
从MSDN文档中还会看到其它的一些元素,如输出设备(也就是显示设备或Monitor)、输出Codec(用作输出信号或格式转换)等,但上面这些却是组成VIDPN最基本和重要的元素。KMDOD项目中也并未用到此外的其它元素。
1.2 拓扑结构
下面介绍几个VIDPN网络拓扑结构的例子,都取自MSDN文档。
1.2.1 示例1
下面列几张VIDPN的拓扑图,解释其中所包含的上述基本元素。
在上面的这个VIDPN的拓扑结构中,它有两个源(source1、source2)和三个目标(DVI、HD15、S-Video)。这是一种老式显卡,可以看到接口类型都比较旧。它提供了三种输出头,但只能支持两个输出源。这样一个源(Source1)输出到DVI口,另外一个源(Source2)以克隆模式(Clone Mode)同时输出到HD15和S-Video两个口上。两个源之间则是扩展模式(External Mode)。
Path1: Source 1-> DVI
Path2: Source2->HD15
Path3: Source2->S-video
三条Path组成了这个VIDPN的拓扑结构。每个Source 和Target都各有它们的mode 和mode set。在最后确定的时候,它们各自的pinned mode将被运用。
1.2.2 示例2
示例1中讲到一个VIDPN,两个Source对应到三个Target。那么Source和Target的数量各由什么决定的呢?从下面的图中可以清楚看到,Source的数量是由显卡内部的Codec决定的。
显卡内部因为只有两个Codec,所以它仅能提供两条Surface处理的管线(PipeLine)。这和显卡本身的处理能力有关系。但是一个被处理过的Surface却可以被轻易地输出到多个Target,这一步仅仅需要少量的硬件连线就可以实现了。所以Target的数量,取决于显卡自己的设计和定位,提供比Source数量更多的Target头也是可以的。
1.3 元素关系
MSDN用下图解释了这么多VIDPN元素之间的对应关系。
这是一张一对多和多对一的结构图。理解它们的关系很重要。总结而言,可把上面的四种关系翻译成下面的文字:
- 一个VIDPN,包含一个拓扑结构;一个拓扑结构包含若干条Path;每个Path中,包含一个Target和一个Source;一个Target只能在唯一的Path中;但一个Source可以存在于若干个Path中(Clone模式,见示例1)。
- 一个VIDPN包含多个Source Mode Set;一个Source Mode Set由多个Source Mode组成。
- 一个VIDPN包含多个Target Mode Set;一个Target Mode Set由多个Target Mode组成。
- 一个VIDPN包含多个Monitor source mode set;一个Monitor source mode set由多个Monitor source mode组成。
1.4 框架接口
前文我们介绍了框架和小端口之间的关系,框架对小端口提供了强有力的支持,胜过再造的力量。这个再造的力量,源自于框架向小端口所提供的各种接口。所谓接口,其实就是框架业已实现的种种功能,而以函数形式提供给小端口驱动使用。形式上,它们被封装在若干个不同的数据结构中。
框架为小端口驱动提供的各种接口,其组织上,基本是根据VIDPN的各主要元素来划分的,下面会看到。我们会对这些接口做简单介绍,并列出接口的详细定义。
1.4.1 框架回调接口
框架回调接口是唯一由WDDM框架直接向小端口驱动提供的接口。其它接口都是直接或间接地,可以通过框架回调接口获取到。之所以把这个接口称为框架回调接口,因为它提供的所有函数名称,都依照DxgkCbXXX这样的格式命名。Cb是Callback的缩写。在WDDM的语境中,我们把这个接口中的所有函数,称为由框架提供的回调函数。
这个接口由DDI函数DxgkDdiStartDevice在调用的时候,作为参数传入。它的定义如下:
typedef struct _DXGKRNL_INTERFACE { ULONG Size; ULONG Version; HANDLE DeviceHandle; DXGKCB_EVAL_ACPI_METHOD DxgkCbEvalAcpiMethod; DXGKCB_GET_DEVICE_INFORMATION DxgkCbGetDeviceInformation; DXGKCB_INDICATE_CHILD_STATUS DxgkCbIndicateChildStatus; DXGKCB_MAP_MEMORY DxgkCbMapMemory; DXGKCB_QUEUE_DPC DxgkCbQueueDpc; DXGKCB_QUERY_SERVICES DxgkCbQueryServices; DXGKCB_READ_DEVICE_SPACE DxgkCbReadDeviceSpace; DXGKCB_SYNCHRONIZE_EXECUTION DxgkCbSynchronizeExecution; DXGKCB_UNMAP_MEMORY DxgkCbUnmapMemory; DXGKCB_WRITE_DEVICE_SPACE DxgkCbWriteDeviceSpace; DXGKCB_IS_DEVICE_PRESENT DxgkCbIsDevicePresent; DXGKCB_GETHANDLEDATA DxgkCbGetHandleData; DXGKCB_GETHANDLEPARENT DxgkCbGetHandleParent; DXGKCB_ENUMHANDLECHILDREN DxgkCbEnumHandleChildren; DXGKCB_NOTIFY_INTERRUPT DxgkCbNotifyInterrupt; DXGKCB_NOTIFY_DPC DxgkCbNotifyDpc; DXGKCB_QUERYVIDPNINTERFACE DxgkCbQueryVidPnInterface; DXGKCB_QUERYMONITORINTERFACE DxgkCbQueryMonitorInterface; DXGKCB_GETCAPTUREADDRESS DxgkCbGetCaptureAddress; DXGKCB_LOG_ETW_EVENT DxgkCbLogEtwEvent; DXGKCB_EXCLUDE_ADAPTER_ACCESS DxgkCbExcludeAdapterAccess; #if DXGKDDI_INTERFACE_VERSION >= DXGKDDI_INTERFACE_VERSION_WIN8) DXGKCB_CREATECONTEXTALLOCATION DxgkCbCreateContextAllocation; DXGKCB_DESTROYCONTEXTALLOCATION DxgkCbDestroyContextAllocation; DXGKCB_SETPOWERCOMPONENTACTIVE DxgkCbSetPowerComponentActive; DXGKCB_SETPOWERCOMPONENTIDLE DxgkCbSetPowerComponentIdle; DXGKCB_ACQUIRE_POST_DISPLAY_OWNERSHIP DxgkCbAcquirePostDisplayOwnership; DXGKCB_POWERRUNTIMECONTROLREQUEST DxgkCbPowerRuntimeControlRequest; DXGKCB_SETPOWERCOMPONENTLATENCY DxgkCbSetPowerComponentLatency; DXGKCB_SETPOWERCOMPONENTRESIDENCY DxgkCbSetPowerComponentResidency; #endif } DXGKRNL_INTERFACE, *PDXGKRNL_INTERFACE;
1.4.2 VIDPN接口
这个接口用来管理VIDPN相关的操作。可通过框架回调接口中的DxgkCbQueryVidPnInterface获取到,示例代码如下:
// 函数:IsSupportedVidPn CONST DXGK_VIDPN_INTERFACE* pVidPnInterface; NTSTATUS Status = m_DxgkInterface.DxgkCbQueryVidPnInterface( pIsSupportedVidPn->hDesiredVidPn, // DXGK_VIDPN_INTERFACE_VERSION_V1, pVidPnInterface);
结构体定义如下:
typedef struct _DXGK_VIDPN_INTERFACE { DXGK_VIDPN_INTERFACE_VERSION Version; DXGKDDI_VIDPN_GETTOPOLOGY pfnGetTopology; DXGKDDI_VIDPN_ACQUIRESOURCEMODESET pfnAcquireSourceModeSet; DXGKDDI_VIDPN_RELEASESOURCEMODESET pfnReleaseSourceModeSet; DXGKDDI_VIDPN_CREATENEWSOURCEMODESET pfnCreateNewSourceModeSet; DXGKDDI_VIDPN_ASSIGNSOURCEMODESET pfnAssignSourceModeSet; DXGKDDI_VIDPN_ASSIGNMULTISAMPLINGMETHODSET pfnAssignMultisamplingMethodSet; DXGKDDI_VIDPN_ACQUIRETARGETMODESET pfnAcquireTargetModeSet; DXGKDDI_VIDPN_RELEASETARGETMODESET pfnReleaseTargetModeSet; DXGKDDI_VIDPN_CREATENEWTARGETMODESET pfnCreateNewTargetModeSet; DXGKDDI_VIDPN_ASSIGNTARGETMODESET pfnAssignTargetModeSet; } DXGK_VIDPN_INTERFACE;
1.4.3 Topology接口
这个接口用来管理VIDPN的拓扑结构。可以通过调用VIDPN接口的pfnGetTopology函数得到。示例代码如下:
// 函数:IsSupportedVidPn D3DKMDT_HVIDPNTOPOLOGY hVidPnTopology; CONST DXGK_VIDPNTOPOLOGY_INTERFACE* pVidPnTopologyInterface; Status = pVidPnInterface->pfnGetTopology( pIsSupportedVidPn->hDesiredVidPn, &hVidPnTopology, &pVidPnTopologyInterface);
结构体定义如下:
typedef struct _DXGK_VIDPNTOPOLOGY_INTERFACE { DXGKDDI_VIDPNTOPOLOGY_GETNUMPATHS pfnGetNumPaths; DXGKDDI_VIDPNTOPOLOGY_GETNUMPATHSFROMSOURCE pfnGetNumPathsFromSource; DXGKDDI_VIDPNTOPOLOGY_ENUMPATHTARGETSFROMSOURCE pfnEnumPathTargetsFromSource; DXGKDDI_VIDPNTOPOLOGY_GETPATHSOURCEFROMTARGET pfnGetPathSourceFromTarget; DXGKDDI_VIDPNTOPOLOGY_ACQUIREPATHINFO pfnAcquirePathInfo; DXGKDDI_VIDPNTOPOLOGY_ACQUIREFIRSTPATHINFO pfnAcquireFirstPathInfo; DXGKDDI_VIDPNTOPOLOGY_ACQUIRENEXTPATHINFO pfnAcquireNextPathInfo; DXGKDDI_VIDPNTOPOLOGY_UPDATEPATHSUPPORTINFO pfnUpdatePathSupportInfo; DXGKDDI_VIDPNTOPOLOGY_RELEASEPATHINFO pfnReleasePathInfo; DXGKDDI_VIDPNTOPOLOGY_CREATENEWPATHINFO pfnCreateNewPathInfo; DXGKDDI_VIDPNTOPOLOGY_ADDPATH pfnAddPath; DXGKDDI_VIDPNTOPOLOGY_REMOVEPATH pfnRemovePath; } DXGK_VIDPNTOPOLOGY_INTERFACE;
1.4.4 Source Mode Set接口
这个接口用来管理VIDPN中的Source Mode Set。可以通过调用VIDPN接口的pfnAcquireSourceModeSet函数得到。示例代码如下:
//函数:CommitVidPn CONST DXGK_VIDPNSOURCEMODESET_INTERFACE* pVidPnSourceModeSetInterface = NULL; Status = pVidPnInterface->pfnAcquireSourceModeSet( pCommitVidPn->hFunctionalVidPn, pCommitVidPn->AffectedVidPnSourceId, &hVidPnSourceModeSet, &pVidPnSourceModeSetInterface);
结构体定义如下:
typedef struct _DXGK_VIDPNSOURCEMODESET_INTERFACE { DXGKDDI_VIDPNSOURCEMODESET_GETNUMMODES pfnGetNumModes; DXGKDDI_VIDPNSOURCEMODESET_ACQUIREFIRSTMODEINFO pfnAcquireFirstModeInfo; DXGKDDI_VIDPNSOURCEMODESET_ACQUIRENEXTMODEINFO pfnAcquireNextModeInfo; DXGKDDI_VIDPNSOURCEMODESET_ACQUIREPINNEDMODEINFO pfnAcquirePinnedModeInfo; DXGKDDI_VIDPNSOURCEMODESET_RELEASEMODEINFO pfnReleaseModeInfo; DXGKDDI_VIDPNSOURCEMODESET_CREATENEWMODEINFO pfnCreateNewModeInfo; DXGKDDI_VIDPNSOURCEMODESET_ADDMODE pfnAddMode; DXGKDDI_VIDPNSOURCEMODESET_PINMODE pfnPinMode; } DXGK_VIDPNSOURCEMODESET_INTERFACE;
1.4.5 Target Mode Set 接口
这个接口用来管理VIDPN中的Target Mode Set。可以通过调用VIDPN接口的pfnAcquireTargetModeSet函数得到。示例代码如下:
// 函数:EnumVidPnCofuncModality CONST DXGK_VIDPNTARGETMODESET_INTERFACE* pVidPnTargetModeSetInterface = NULL; Status = pVidPnInterface->pfnAcquireTargetModeSet( pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnTargetId, &hVidPnTargetModeSet, &pVidPnTargetModeSetInterface)
结构体定义如下:
typedef struct _DXGK_VIDPNTARGETMODESET_INTERFACE { DXGKDDI_VIDPNTARGETMODESET_GETNUMMODES pfnGetNumModes; DXGKDDI_VIDPNTARGETMODESET_ACQUIREFIRSTMODEINFO pfnAcquireFirstModeInfo; DXGKDDI_VIDPNTARGETMODESET_ACQUIRENEXTMODEINFO pfnAcquireNextModeInfo; DXGKDDI_VIDPNTARGETMODESET_ACQUIREPINNEDMODEINFO pfnAcquirePinnedModeInfo; DXGKDDI_VIDPNTARGETMODESET_RELEASEMODEINFO pfnReleaseModeInfo; DXGKDDI_VIDPNTARGETMODESET_CREATENEWMODEINFO pfnCreateNewModeInfo; DXGKDDI_VIDPNTARGETMODESET_ADDMODE pfnAddMode; DXGKDDI_VIDPNTARGETMODESET_PINMODE pfnPinMode; } DXGK_VIDPNTARGETMODESET_INTERFACE;
1.4.6 Monitor接口
除了上面这些由框架提供的常用接口外,还有一些未曾在KMDOD项目中用到的接口:
Monitor接口:DXGK_MONITOR_INTERFACE
这个接口可通过框架回调接口中的DxgkCbQueryMonitorInterface回调函数获取。通过这个接口,可以获取Monitor的Monitor Source Mode Set接口和刷新率范围。它有两个版本,版本2中增加了函数,用来获取Windows操作系统为Monitor增加的必须支持的Mode。
Monitor Source Mode Set接口:DXGK_MONITORSOURCEMODESET_INTERFACE
这个接口可通过Monitor接口中的接口函数pfnAcquireMonitorSourceModeSet获取,它提供了操作显示器Mode相关的函数。
2. 版本历史
V1.0:2013/8/5
23,767 total views, 3 views today
如何得到hDesiredVidPn?
如何得到当前 hDesiredVidPn?
HVIDPN句柄是以输入参数的形式,由Dxgkrnl传给到显卡驱动的。比如下面的DDI:
NTSTATUS APIENTRY DxgkDdiIsSupportedVidPn(
_In_ const HANDLE hAdapter,
_Inout_ DXGKARG_ISSUPPORTEDVIDPN *pIsSupportedVidPnArg
);
pIsSupportedVidPnArg所指向的结构体中就包含了HVIDPN句柄。
问题是hAdapter又如何得到, 好像没有直接方法!
hAdapter是另一个输入参数,你应该能看到它的in修饰符。
在KMDOD的例子中,hAdapter被强制转换成BASIC_DISPLAY_DRIVER对象指针。
这 hAdapter会不会是 MiniportDeviceContext?
>>这 hAdapter会不会是 MiniportDeviceContext?
是的。
博主,请问下,假如我没有3D的要求,仅用小端口驱动是否能够完成显示?也就是说没有加载user-model display driver的情况下是否也能正常使用WDDM?先谢啦
你的问题比较奇怪,你有什么大用场吗?
看你使用的系统,在Vista和Win7系统上,还可以使用GDI画桌面。在没有user mode driver的情况下,桌面显示还是没问题的,但不能跑D3D程序。
博主,我想问,WDDM的miniport需要配合user-modle display driver 一起使用吗?我的User-model 驱动仅仅运行了openAdapter() 随后就跑去执行closeAdapter()了,理应是由D3Druntime 先调用CreateDevice。不过,小端口驱动方面则是运行正常。不知道原因所在?谢啦….
主要看系统。Vista以后有DWM进程管理桌面,DWM使用的是D3D9的接口,所以如果要让DWM跑的话,就一定要有user mode driver。DWM在Win8以前是可以禁用的,如果被禁用,explorer和一般程序可以通过GDI接口直接画桌面。但是Win8以后,DWM无法禁用,只能通过DWM画桌面内容,如果没有user mode driver(简称UMD),或者UMD有问题的话,就看不到桌面了。
你应该是在Vista或Win7上测试的。你自己写了UMD,可以调试一下DWM进程,我想它会执行失败,最后结束进程的。但我没有试过。
我试了下,主要有两种情况。第一种,是我的user-model驱动没有被加载,但是DWM正常运行。第二种,user-model的DLL被dwm加载,不过也是openAdapter() 后 closeAdapter(),但DWM并未出现异常。但是3D的东西均无法运行。
你是怎么判断UMD没有被加载的?应该用windbg调试器attach到DWM进程上去,然后用lm列举所有模块,查看有没有D3D的DLL;在适当的DDI上设置断点,并检查是否被调用。
你可以发邮件给我,告诉我详细的运行环境。
That takes us up to the next level. Great posintg.
写一个PCI-E总线过滤驱动,虚拟一个显卡设备,然后给这个设备安装微软的KMDOD驱动,该虚拟显卡能不能正常工作输出一个屏(当然,不能输出到显示器)。我这个思路可不可行?
虚拟显卡设备,不必通过PCI总线或总线过滤驱动,只要是通过向系统报告一个外部设备,然后为它安装你的驱动即可。通过旧的Add Hardware Wizard就可以为你的虚拟设备添加驱动程序。
如果你想为虚拟设备安装KMDOD驱动,首先你可能要改inf文件(如果不是通过Add hardware wizard添加),其次驱动程序也要改,因为KMDOD只能用在作为POST的显卡上,虚拟显卡是不能工作的(但加载也许能成功)。
如何修改呢?
怎样在“屏幕分辨率”中虚拟出一个显示器呢?
Mirror驱动有什么示范作用? 不知道张老师有没有玩过这个东东?
WDDM不支持Mirror映射;Mirror驱动是XP时代的东西。这是WDDM被诟病的一个缺点,不知道后续版本会怎样。
大神您好!我写了个显卡驱动UMD的空壳,只有OpenAdapter这个函数,在里面输出日志到文件。安装好后却没看到日志。难道入口函数不是OpenAdapter么?
您好!我现在需要开发一个windows xp的双屏显示驱动。现在已经有了完整的单屏的驱动,需要再次基础上加上另一个通道。DDK文档中有一些关于DualView的说明,但是不是很全面。对于屏幕变化的具体调用过程不是很清楚,DDK中有说通过IOCTL_VIDEO_SWITCH_DUALVIEW这个IO控制码来传递。对此,您有什么样的建议呢?
做mirror驱动!
Nie piszesz że instytucji finansowych nie stać na dofinansowanie bo nigdy nie będą mogły go w takiej skali wraz z odsetkami oddać,pozostaje nacjonalizacja-wtedy wzrośnie zadłużenie państwa i koło problemów się zamyka.
搞定没?如何处理IOCTL_VIDEO_SWITCH_DUALVIEW的
你好!您有user-modle display driver 的demo 嗎?感謝指點。
有个地方有笔误,目标模式集合的英语翻译
大神,您好!
我们要在Win10里面开关HDMI信号通道(希望通过一个小插件BUTTON自如切换调用),以便发送一个电平信号给主板BIOS,进而能让我们微型投影仪的光机供电系统单片机的引脚感知到变化,然后打开电源供电或关闭光机。请问,如何才能实现?需要进入什么路径调用哪个函数?
I hate my life but at least this makes it beeabrla.
嗨,您好,虚拟多个显示器,如何通过VidPN管理器设计路径将桌面扩展到多个监视器??谢谢!