Windows顯示驅動(WDDM)編程初步(1)
轉載自【作者:張佩】【原文:http://www.yiiyee.cn/Blog/wddm1/】
WDDM Frame
Windows顯示驅動從Vista開始,使用新的WDDM編程框架,稱為Windows Display Driver Model。也有一種最初的名稱是LDDM,L代表Longhorn,但後來微軟在所有產品線上都不再使用Longhorn代號,故而改成現在的名稱。雖然在有些地方還能看到LDDM的說法,但應理解成舊文檔的遺存,不應該做概念上的區分。
WDDM框架是一種典型的小端口(miniport)驅動框架。 NT系統中的所有小端口框架,都是基於WDM框架來實現的,但小端口框架對外提供了更高級的接口,以簡化編程的難度,並提高穩定性。如下圖所示,中間的WDDM是系統提供的編程框架,我們基於這個框架,編寫裡面的小端口驅動,也就是顯示驅動。
顯示驅動類型
現在的顯卡設備,可以按照功能將它分成顯示和計算兩類。大部分的顯卡是用來連接顯示器顯示圖片和動畫用的,也有些顯卡主要確實用來做科學計算用的。顯卡處理器(GPU)對浮點運算有較強的能力,而主機處理器(CPU)處理浮點運算的能力較弱。而在科學計算領域,浮點運算是非常重要的內容,所以工業界就想到利用GPU進行科學運算。
應該說,所有的顯卡都既能夠支持顯示,又能夠支持運算。只是看它偏向哪個方面,為哪個功能做優化罷了。對於偏重計算的顯卡,就不必配置多個顯示接口,圖像處理的模塊就不用很高級;相反,對於圖形功能偏重的顯卡,它就必須要大數據帶寬,大顯存,支持多種類型的接口,能夠實現鋸齒優化等等。
針對我們的驅動來講,如果一個顯示驅動,既支持顯卡的顯示功能,又支持運算功能,稱為全功能驅動(Complete function);如果只支持顯示,不支持運算,就是Display Only驅動;如果只支持運算,不支持顯示功能,就是Render Only驅動。
微軟在Win8的系統上,為所有不同類型的顯卡,編寫了Display Only和Render Only驅動。在未安裝廠商驅動或者廠商驅動被破壞、禁用的情況下,系統會默認選擇使用Display Only驅動來顯示桌面內容。但一般系統不會選擇安裝Render Only驅動,那樣就什麼都看不到了。 Render Only驅動的具體應用場景,我到目前還沒有看到。可能在Render Only的數據服務器顯卡上會被運用。
我的這份顯示驅動初步教材,就是基於微軟公開的Display Only驅動項目KMDOD來寫的。不會涉及數據Render部分。其實可以很方便地把一個Display only的驅動拓展到Complete驅動,在講完所有內容後,會有一小部分內容做介紹。
KMDOD項目可以從MSDN代碼網站上下載到,地址:Windows-driver-samples KMDOD
初始化
如果不更改編譯配置,WDDM驅動的默認啟動函數是DriverEntry。這是驅動對像初始化的地方,一般對於小端口驅動而言,它需要調用框架的初始化函數。 WDDM框架的初始化函數是DxgkInitializeDisplayOnlyDriver。從內核編程好幫手WDK中,可以找到它的聲明:
NTSTATUS DxgkInitializeDisplayOnlyDriver( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath, _In_ PKMDDOD_INITIALIZATION_DATA KmdDodInitializationData );
初始化函數會完成驅動對象的初始化,所以前面兩個參數是入口函數的輸入參數。在全文最後的實驗章節中,會介紹如何查看驅動對象,就能夠比較清晰地看到WDDM框架對顯示驅動對象所進行的初始化作業了。此外,初始化函數還要完成顯示驅動相關的初始化。顯示驅動傳入一個函數結構體參數,類型是KMDDOD_INITIALIZATION_DATA,結構體裡麵包含的是顯示驅動向框架提供的一系列回調函數(Callback Function)。框架會在合適的時候調用這些回調函數,完成對應功能。
結構體定義如下:
InitializationData
KMDOD項目沒有實現結構體中列舉的所有回調函數,所以它不能支持WDDM提供的全部和Display相關的功能。比如D3D用戶程序通過DC句柄和顯示驅動進行交互的escape回調函數,這裡就沒有實現。對於沒有實現的回調函數,在結構體中的對應函數指針應被初始化為NULL。
第一個參數Version用來標識你所編寫的顯示驅動使用哪個版本的WDDM。 WDDM一共有四個版本:1.0(Vista & Vista SP1);1.1(Win7);1.2(Win8);1.3(Win Blue)。 KMDOD這個項目中使用的是Win8版本:DXGKDDI_INTERFACE_VERSION_WIN8。 已更新為DXGKDDI_INTERFACE_VERSION_WDDM2_1
為了完成結構體初始化,我們要首先實現這些函數。在具體列舉實現代碼之前,把這些回調函數做一個簡單的分類和介紹是有必要的。
Pnp和power函數
所有現代的物理設備都必須處理Pnp和Power事件。 Pnp事件對應了設備插拔、開始、移除、停止等,以及作為總線設備需要提供的子設備(顯示器)枚舉等;Power事件對應上電、掉電操作,以及查詢設備是否運行進行電源操作。
另外把卸載回調函數也歸入其中。卸載函數在驅動被停止,沒有任何外部模塊引用的時候,系統會嘗試將驅動卸載,這時候卸載回調被調用。
InitialData.DxgkDdiAddDevice = BddDdiAddDevice; InitialData.DxgkDdiStartDevice = BddDdiStartDevice; InitialData.DxgkDdiStopDevice = BddDdiStopDevice; InitialData.DxgkDdiStopDeviceAndReleasePostDisplayOwnership = BddDdiStopDeviceAndReleasePostDisplayOwnership; InitialData.DxgkDdiResetDevice = BddDdiResetDevice; InitialData.DxgkDdiRemoveDevice = BddDdiRemoveDevice; InitialData.DxgkDdiQueryChildRelations = BddDdiQueryChildRelations; InitialData.DxgkDdiQueryChildStatus = BddDdiQueryChildStatus; InitialData.DxgkDdiQueryDeviceDescriptor = BddDdiQueryDeviceDescriptor; InitialData.DxgkDdiSetPowerState = BddDdiSetPowerState; InitialData.DxgkDdiUnload = BddDdiUnload; InitialData.DxgkDdiQueryAdapterInfo = BddDdiQueryAdapterInfo;
顯示函數
顯卡驅動的主要功能是配置物理設備,讓它能夠輸出圖片和動畫到外部顯示設備上。和這個功能相關的函數有很多,它包括對鼠標位置的更新,顯示器Mode的枚舉和設置等函數:
InitialData.DxgkDdiSetPointerPosition = BddDdiSetPointerPosition; InitialData.DxgkDdiSetPointerShape = BddDdiSetPointerShape; InitialData.DxgkDdiIsSupportedVidPn = BddDdiIsSupportedVidPn; InitialData.DxgkDdiRecommendFunctionalVidPn = BddDdiRecommendFunctionalVidPn; InitialData.DxgkDdiEnumVidPnCofuncModality = BddDdiEnumVidPnCofuncModality; InitialData.DxgkDdiSetVidPnSourceVisibility = BddDdiSetVidPnSourceVisibility; InitialData.DxgkDdiCommitVidPn = BddDdiCommitVidPn; InitialData.DxgkDdiUpdateActiveVidPnPresentPath = BddDdiUpdateActiveVidPnPresentPath; InitialData.DxgkDdiRecommendMonitorModes = BddDdiRecommendMonitorModes;
硬件操作
最後是和物理設備交互的一些函數,首先是中斷處理函數,然後有獲取設備屬性,讀寫設備幀內存、顯示桌面內容(Present)等函數。
InitialData.DxgkDdiDpcRoutine = BddDdiDpcRoutine; InitialData.DxgkDdiInterruptRoutine = BddDdiInterruptRoutine; InitialData.DxgkDdiQueryVidPnHWCapability = BddDdiQueryVidPnHWCapability; InitialData.DxgkDdiPresentDisplayOnly = BddDdiPresentDisplayOnly; InitialData.DxgkDdiSystemDisplayEnable = BddDdiSystemDisplayEnable; InitialData.DxgkDdiSystemDisplayWrite = BddDdiSystemDisplayWrite;
功能函數
這部分是顯示驅動作為一個驅動來講,它所實現的一般意義上的功能支持函數。這部分我只列了一個,是用戶程序和內核驅動交互用的IO控制函數。
InitialData.DxgkDdiDispatchIoRequest = BddDdiDispatchIoRequest;
完整的初始化函數:
extern "C" NTSTATUS DriverEntry( _In_ DRIVER_OBJECT* pDriverObject, _In_ UNICODE_STRING* pRegistryPath) { PAGED_CODE(); // Initialize DDI function pointers and dxgkrnl KMDDOD_INITIALIZATION_DATA InitialData = {0}; InitialData.Version = DXGKDDI_INTERFACE_VERSION_WIN8; InitialData.DxgkDdiAddDevice = BddDdiAddDevice; //…… 其它的回調函數賦值過程,上面已全部列舉,此處省略 NTSTATUS Status = DxgkInitializeDisplayOnlyDriver(pDriverObject, pRegistryPath, &InitialData); if (!NT_SUCCESS(Status)) { BDD_LOG_ERROR1("DxgkInitializeDisplayOnlyDriver failed with Status: 0x%I64x", Status); } return Status; }
可選的擴展
初始化部分到此本來可以結束,繼續講下面的回調函數實現。但其實依然可以擴展一下,不熟悉WDM的讀者可以跳過。針對所有的端口驅動框架都基於WDM來實現的這個事實,如果有的小端口驅動有必要想直接操作IRP的話,應該怎麼實現呢?
其實非常簡單。在DxgkInitializeDisplayOnlyDriver被調用過之後,驅動對象的初始化已經完成了。這時候我們可以對框架的分發函數進行Hook。
比如有一個很重要的功能,很多設備驅動都要做的。就是它希望自己能夠得到系統關機的訊息。通過一般意義上的PNP和Power事件,是沒有辦法得到系統關機訊息的。辦法是註冊自己的IRP_MJ_SHUTDOWN分發函數來接收此訊息。
下面是簡要的實現代碼:
// 下面代碼應在DxgkInitializeDisplayOnlyDriver被調用過後執行 pOldFunc = pDriverObject->MajorFunction[IRP_MJ_SHUTDOWN]; // 保存框架有可能已實現的函數 pDriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = BddDdiShutdown; // 此外還需要在StartDevice函數中調用IoRegisterShutdownNotificatin函數,本文不再繼續演示 NTSTATUS BddDdiShutdown (PDEVICE_OBJECT pDev, PIRP irp) { // do something you wanted here if (pOldFunc) return pOldFunc (pDev, irp); else return STATUS_SUCCESS; }
Callback function 回調函數實現
KMDOD項目定義了一個顯示驅動類,來封裝和實際功能相關的所有具體操作。這樣一來,大部分回調函數的實現都比較簡單。這個類是BASIC_DISPLAY_DRIVER,我們在第二節會具體地講它。
生命週期
WDDM框架在設計的時候,是能夠支持多個底層物理設備的。換句話說,如果系統中存在多個顯卡設備,WDDM框架都能夠很好地支持它們同時或者分別工作。作為這個支持的一部分,在PNP操作的起始,也就是AddDevice回調函數被調用的時候,框架要求顯示驅動返回一個當前物理設備的Context,作為識別此物理設備的標識。
那麼KMDOD也是可以支持多個顯卡設備的,所以為每個設備創建一個BASIC_DISPLAY_DRIVER對象,作為Context返回給框架。框架在以後調用任何一個回調函數時,都會把這個Context作為其中的一個輸入參數來使用。
所以我們看,DriverEntry是驅動的開始,卸載函數是驅動的結束;AddDevice函數是設備工作的開始,RemoveDevice是設備結束工作的標識。我們可以用下面的框圖來描述這個概念。
設備的Context作為一個標識物理顯卡的變量,在設備的PNP週期裡面一直運作著。當關機、設備禁用或者其他變故發生的時候,RemoveDevice回調被執行,顯示驅動將負責刪除它所創建的設備Context。
下面是 AddDevice 和 RemoveDevice 這兩個回調函數的實現。
NTSTATUS BddDdiAddDevice( _In_ DEVICE_OBJECT* pPhysicalDeviceObject, _Outptr_ PVOID* ppDeviceContext) { PAGED_CODE(); if ((pPhysicalDeviceObject == NULL) || (ppDeviceContext == NULL)) { BDD_LOG_ERROR2("One of pPhysicalDeviceObject (0x%I64x), ppDeviceContext (0x%I64x) is NULL", pPhysicalDeviceObject, ppDeviceContext); return STATUS_INVALID_PARAMETER; } *ppDeviceContext = NULL; BASIC_DISPLAY_DRIVER* pBDD = new(NonPagedPoolNx) BASIC_DISPLAY_DRIVER(pPhysicalDeviceObject); if (pBDD == NULL) { BDD_LOG_LOW_RESOURCE0("pBDD failed to be allocated"); return STATUS_NO_MEMORY; } *ppDeviceContext = pBDD; return STATUS_SUCCESS; } NTSTATUS BddDdiRemoveDevice( _In_ VOID* pDeviceContext) { PAGED_CODE(); BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext); if (pBDD) { delete pBDD; pBDD = NULL; } return STATUS_SUCCESS; }
其它回調函數實現
其它回調函數的實現,這裡僅僅以StartDevice為例講解。函數原型定義如下:
NTSTATUS DxgkDdiStartDevice( _In_ const PVOID MiniportDeviceContext, _In_ PDXGK_START_INFO DxgkStartInfo, _In_ PDXGKRNL_INTERFACE DxgkInterface, _Out_ PULONG NumberOfVideoPresentSources, _Out_ PULONG NumberOfChildren )
第一個參數即設備Context,毫無疑問,它就是我們剛剛在AddDevice中創建的BASIC_DISPLAY_DRIVER對象。所以我們第一步需要獲取對象指針,並且調用到BASIC_DISPLAY_DRIVER裡面的startDevice函數中去。其實現如下:
NTSTATUS BddDdiStartDevice( _In_ VOID* pDeviceContext, _In_ DXGK_START_INFO* pDxgkStartInfo, _In_ DXGKRNL_INTERFACE* pDxgkInterface, _Out_ ULONG* pNumberOfViews, _Out_ ULONG* pNumberOfChildren) { PAGED_CODE(); BDD_ASSERT_CHK(pDeviceContext != NULL); BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast<BASIC_DISPLAY_DRIVER*>(pDeviceContext); return pBDD->StartDevice(pDxgkStartInfo, pDxgkInterface, pNumberOfViews, pNumberOfChildren); }
其它的回調函數,實現方法和StartDevice很類似。唯一的區別是,如果StartDevice調用失敗的話,也就是設備啟動失敗的話,道理上講,很多後續的函數都不應該被調用,因為既然設備沒有啟動,就不應該有任何針對於它的動作存在。所以這些函數被調用的時候,很多都會先確認一下,設備是否處於啟動狀態(IsDriverActive),就是判斷StartDevice是否執行成功。
Windows显示驱动(WDDM)编程初步(2)
轉載自作者:張佩【原文: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: Source 2 -> HD15
Path3: Source 2 -> 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 元素關係
這是一張一對多和多對一的結構圖。理解它們的關係很重要。總結而言,可把上面的四種關係翻譯成下面的文字:
一個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相關的函數。