window 显示驱动开发-修补 DMA 缓冲区

1. 修补阶段的核心作用

graph TD
    A[UMD提交命令] --> B(生成DMA缓冲区)
    B --> C[资源虚拟地址]
    C --> D{DxgkDdiPatch}
    D -->|插入物理地址| E[可执行DMA缓冲区]
    E --> F[GPU执行]
  • 地址转换桥梁:将UMD提供的虚拟资源引用转换为硬件可识别的物理地址
  • 内存虚拟化关键:支持VidMm动态迁移资源内存位置(显存↔系统内存)
  • 安全隔离:确保用户模式无法直接操作物理地址

2. 修补流程详解

(1) 触发时机
在以下操作后由GPU调度器触发:

  • 分页操作完成(DxgkDdiBuildPagingBuffer)
  • 资源驻留状态变更(显存不足时逐出到系统内存)

(2) 内核驱动处理(DxgkDdiPatch)

NTSTATUS DxgkDdiPatch(
    IN_CONST_HANDLE hDevice,
    INOUT_PDXGKARG_PATCH pPatchArgs)
{
    // 1. 遍历修补位置列表
    for (UINT i = 0; i < pPatchArgs->PatchLocationListSize; i++) {
        DXGK_PATCHLOCATION* pLoc = &pPatchArgs->pPatchLocationList[i];
        
        // 2. 从VidMm获取物理地址
        PHYSICAL_ADDRESS pa = VidMmGetPhysAddr(pLoc->hAllocation);
        
        // 3. 写入DMA缓冲区指定位置
        WriteGpuAddress(
            pPatchArgs->pDmaBuffer + pLoc->DmaBufferOffset,
            pa + pLoc->AllocationOffset);
    }
    
    return STATUS_SUCCESS;
}

(3) 关键数据结构

// 修补位置描述符
struct DXGK_PATCHLOCATION {
    UINT   DmaBufferOffset;  // DMA缓冲区中的偏移量(字节)
    HANDLE hAllocation;      // 资源分配句柄
    UINT   AllocationOffset; // 资源内部的偏移量
    UINT   PatchType;        // 地址类型(如32/64位)
};

// DMA缓冲区示例(修补前)
struct DmaBuffer {
    BYTE  commandHeader[16];
    UINT64 textureVirtualAddr;  // 待修补位置
    // ...其他命令数据
};

3. 硬件兼容性处理

(1) 不同地址类型处理

void WriteGpuAddress(void* pDest, PHYSICAL_ADDRESS pa) {
    switch (patchType) {
    case DXGK_PATCH_32BIT:
        *(UINT32*)pDest = (UINT32)pa.QuadPart;
        break;
    case DXGK_PATCH_64BIT:
        *(UINT64*)pDest = pa.QuadPart;
        break;
    case DXGK_PATCH_GPUVA:  // GPU虚拟地址
        *(GPU_VIRTUAL_ADDRESS*)pDest = ConvertToGpuVa(pa);
        break;
    }
}

(2) 多GPU架构适配

GPU类型修补特点驱动实现要点
独立显卡(dGPU)物理地址为显存绝对地址直接写入PCI BAR空间偏移量
集成显卡(iGPU)物理地址为系统内存地址需转换为GTT(图形转换表)索引
UMA架构统一内存空间,地址连续可跳过部分修补操作

4. 性能优化技巧

(1) 批处理修补请求

// 合并连续修补位置(示例优化)
for (UINT i = 0; i < listSize; ) {
    if (IsContiguousPatch(&list[i], &list[i+1])) {
        PerformBulkPatch(&list[i], 2);  // 批量处理
        i += 2;
    } else {
        PerformSinglePatch(&list[i]);
        i++;
    }
}

(2) 缓存友好布局

// 优化前(碎片化修补)
DmaBuffer: [Draw][TexAddr1][Transform][TexAddr2]
// 优化后(集中修补)
DmaBuffer: [TexAddr1][TexAddr2][Draw][Transform]

5. 安全与错误处理

(1) 验证机制

# 查看待修补DMA缓冲区
!dxgkd_ext.dmabuffer 0xADDR -patch

# 检查资源物理地址
!dxgkd_ext.allocation 0xHANDLE -physaddr

(2) 错误恢复策略

错误类型恢复动作
无效分配句柄标记设备为错误状态,触发TDR
偏移量越界丢弃当前DMA缓冲区,记录调试信息
硬件访问违例重置GPU引擎,重建命令队列

6. 调试与诊断

(1) WinDbg扩展命令

# 查看待修补DMA缓冲区
!dxgkd_ext.dmabuffer 0xADDR -patch

# 检查资源物理地址
!dxgkd_ext.allocation 0xHANDLE -physaddr

(2) ETW事件日志

// 驱动中添加诊断事件
void TracePatchEvent(DXGK_PATCHLOCATION* pLoc) {
    EVENT_DATA_DESCRIPTOR desc[3];
    EventDataDescCreate(&desc[0], &pLoc->hAllocation, sizeof(HANDLE));
    EventDataDescCreate(&desc[1], &pLoc->DmaBufferOffset, sizeof(UINT));
    EventWrite(g_ProviderHandle, &EVT_PatchOperation, 3, desc);
}

7. 现代演进(Windows 10+)

(1) GPU虚拟地址支持

// DX12引入的GPUVA模式
if (Caps.SupportsGpuVirtualAddress) {
    pLoc->PatchType = DXGK_PATCH_GPUVA;  // 避免频繁修补
}

(2) 直接捕获支持

// 通过DXGK_PATCHFLAG_NO_PATCH跳过冗余修补
if (IsReplayMode()) {
    pPatchArgs->Flags |= DXGK_PATCHFLAG_NO_PATCH;
}

最佳实践总结

  1. 最小化修补点:合并资源绑定操作
  2. 预计算偏移量:UMD应在生成命令时记录精确修补位置
  3. 处理多引擎:为3D/计算/拷贝引擎维护独立修补逻辑
  4. 监控修补开销:通过!dxgkd_ext.patchstats分析性能瓶颈
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值