Windows Vista 的顯示驅動程序模型保證呈現設備的 DMA 緩沖區和修補程序位置列表的大小。 修補程序位置列表包含 DMA 緩沖區中命令引用的資源的物理內存地址。
在有保證的協定模式下,用戶模式顯示驅動程序知道 DMA 緩沖區和修補程序位置列表的確切大小,當用戶模式顯示驅動程序填充命令緩沖區并調用 pfnRenderCb 將它們提交到顯示微型端口驅動程序時,這些 DMA 緩沖區和修補程序位置列表可供轉換。 每次調用 pfnRenderCb 后,用戶模式顯示驅動程序將收到可用于以下轉換 (即對 pfnRenderCb 的以下調用) 的 DMA 緩沖區和修補程序位置列表的大小。
視頻內存管理器保證在下一次轉換完成之前不會剪裁該設備的 DMA 緩沖區和修補程序位置列表。 顯示微型端口驅動程序必須能夠將一個命令緩沖區轉換為恰好一個 DMA 緩沖區和一個修補程序位置列表。 如果無法進行此轉換,則根據定義,用戶模式命令緩沖區無效。 顯示微型端口驅動程序無法返回狀態,指示轉換期間它已超過 DMA 緩沖區空間和修補程序位置列表;這樣做會導致視頻內存管理器錯誤檢查系統,因為內存管理器無法滿足保證的 DMA 協定的要求。
1. 核心概念解析
(1) DMA 緩沖區與修補程序位置列表(Patch Location List)
- DMA 緩沖區:包含 GPU 可執行的硬件指令序列,由內核模式驅動(KMD)從用戶模式命令轉換而來。
- 修補程序位置列表:記錄 DMA 緩沖區中引用的資源(如紋理、頂點緩沖區)的 物理內存地址,用于運行時重定位(如內存被 VidMm 遷移時)。
(2) 有保證的協定(Guaranteed Contract)
- 關鍵保證:VidMm 承諾在每次 pfnRenderCb 調用后,為設備提供 固定大小的 DMA 緩沖區+修補列表空間,直到下次提交完成。
- 違反后果:若 KMD 無法在給定空間內完成轉換,系統會觸發 bugcheck(藍屏),因為協定被破壞。
2. 工作流程與責任劃分
sequenceDiagramparticipant UMD as 用戶模式驅動(UMD)participant VidMm as 視頻內存管理器participant KMD as 內核模式驅動(KMD)participant GPU as GPU硬件UMD->>VidMm: 1. 初始化時獲取DMA緩沖區大小VidMm-->>UMD: 返回保證的DMA緩沖區/修補列表大小loop 每幀渲染UMD->>UMD: 2. 生成命令緩沖區(用戶空間)UMD->>KMD: 3. 調用pfnRenderCb提交KMD->>KMD: 4. 驗證并轉換命令alt 轉換成功KMD->>GPU: 5. 提交DMA緩沖區+修補列表KMD->>VidMm: 6. 返回執行狀態VidMm->>UMD: 7. 更新下次可用緩沖區大小else 轉換失敗(空間不足)KMD->>System: 觸發bugcheckendend
3. 開發者視角的實現要求
用戶模式驅動(UMD)責任
// 示例:UMD提交命令的典型流程
void UmdSubmitCommands() {// 1. 從VidMm獲取當前可用空間D3DDDICB_RENDER renderCB = {0};pfnGetCaps(D3DDDICAPS_GET_DMA_BUFFER_SIZE, &renderCB);// 2. 生成不超過限制的命令BYTE* cmdBuffer = AllocCommandBuffer(renderCB.DmaBufferSize);GenerateCommands(cmdBuffer, renderCB.DmaBufferSize);// 3. 提交并獲取下次空間HRESULT hr = pfnRenderCb(&renderCB);if (SUCCEEDED(hr)) {UINT nextBufferSize = renderCB.NextDmaBufferSize; // 更新下次可用大小}
}
內核模式驅動(KMD)責任
// 在DxgkDdiRender回調中的處理
NTSTATUS DxgkDdiRender(IN_CONST_HANDLE hContext,INOUT_PDXGKARG_RENDER pRender)
{// 1. 必須在保證空間內完成轉換if (pRender->DmaBufferSize < RequiredSpace()) {return STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER; // 實際上會觸發bugcheck}// 2. 轉換命令到DMA格式ConvertToDmaBuffer(pRender->pCommand, pRender->DmaBuffer, pRender->DmaBufferSize);// 3. 填充修補位置列表GeneratePatchList(pRender->PatchLocationList,pRender->PatchLocationListSize);return STATUS_SUCCESS;
}
4. 關鍵設計原理
(1) 為什么需要固定大小保證?
- 確定性執行:避免動態內存分配導致的不確定性延遲。
- 安全隔離:防止用戶模式驅動通過故意溢出引發內核內存損壞。
- 性能優化:固定大小便于預分配內存池,減少運行時開銷。
(2) 修補列表的物理地址重定位
當 VidMm 遷移資源內存時(如顯存不足時逐出到系統內存),只需更新修補列表中的物理地址,無需修改 DMA 緩沖區本身:
// VidMm 的遷移處理示例
void VidMmRelocateResource(RESOURCE* res, PHYSICAL_ADDRESS newAddr) {foreach (PatchEntry* entry in res->PatchLocations) {*entry->DmaBufferPtr = newAddr; // 更新GPU指令中的地址}
}
5. 違反協定的典型場景
錯誤類型 | 后果 | 調試方法 |
---|---|---|
DMA 緩沖區溢出 | 立即觸發?VIDEO_TDR_FAILURE ?bugcheck | 檢查 KMD 的轉換邏輯大小計算 |
修補列表溢出 | 同上 | 驗證資源引用計數 |
無效命令引用 | 可能延遲觸發 GPU 掛起 | 使用 PIX 捕獲命令緩沖區 |
未處理的頁面錯誤 | GPU 訪問違例 | 檢查修補列表是否覆蓋所有資源引用 |
6. 現代演進(Windows 10+)
- 彈性內存管理:Windows 10 引入部分彈性分配,但核心協定仍保留。
- 直接提交(Direct Submission):允許繞過部分驗證,但需驅動顯式聲明能力。
- GPU 虛擬內存:物理地址重定位需求減少,但修補列表機制仍存在以兼容舊硬件。
總結
WDDM 的 DMA 保證協定通過:
- 固定大小預分配 確保確定性
- 嚴格空間強制 維護系統穩定性
- 物理地址重定向 實現內存虛擬化
開發者必須:
- UMD 嚴格遵守大小限制
- KMD 確保轉換絕不溢出
- 利用修補列表處理資源遷移
這種設計平衡了性能、安全與靈活性,是 Windows 圖形棧穩定性的基石。