Quantcast
Channel: windows –易春木
Viewing all articles
Browse latest Browse all 57

[轉] 筆記更新 – Windows顯示驅動 (WDDM) 編程初步

$
0
0

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作為其中的一個輸入參數來使用。

WDDM Life

所以我們看,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 元素關係

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相關的函數。


Viewing all articles
Browse latest Browse all 57

Trending Articles