为了帮助评估图形硬件性能,Windows 显示驱动程序模型 (WDDM) 1.3 及更高版本驱动程序可以选择为 GPU 处理的 API 调用提供准确的计时信息。 从 Windows 8.1 开始,此功能是新的。
内核性能参考
这些参考主题介绍如何在显示微型端口驱动程序和用户模式显示驱动程序中实现此功能:
DxgkDdiCalibrateGpuClock
DxgkDdiFormatHistoryBuffer
DXGK_HISTORY_BUFFER
DXGK_HISTORY_BUFFER_HEADER
DXGKARG_CALIBRATEGPUCLOCK
DXGKARG_FORMATHISTORYBUFFER
DXGKARG_HISTORYBUFFERPRECISION
DRIVER_INITIALIZATION_DATA (新的 DxgkDdiCalibrateGpuClock 和 DxgkDdiFormatHistoryBuffer 成员)
DXGK_ALLOCATIONINFOFLAGS (新的 HistoryBuffer 成员)
DXGK_QUERYADAPTERINFOTYPE (新的 DXGKQAITYPE_HISTORYBUFFERPRECISION 常量值)
DxgkDdiCreateAllocation 中的“分配历史记录缓冲区”
1. 核心功能概述
Windows 显示驱动模型(WDDM)从 Windows 8 开始引入 GPU 性能历史缓冲区 机制,用于高精度记录 GPU 活动(如引擎利用率、温度、时钟频率等)。显示微型端口驱动(KMD)需实现以下关键功能:
组件 | 作用 |
---|---|
历史缓冲区 | 环形缓冲区,存储 GPU 性能数据(由驱动定义格式)。 |
时钟校准 | 同步 GPU 时钟与系统时钟,确保时间戳准确性。 |
数据格式化 | 将原始二进制性能数据转换为可读格式(供性能工具解析)。 |
2. 关键数据结构
(1) DXGK_HISTORY_BUFFER
typedef struct _DXGK_HISTORY_BUFFER {
DXGK_HISTORY_BUFFER_HEADER Header; // 缓冲区元数据
BYTE Data[ANYSIZE_ARRAY]; // 原始性能数据(驱动自定义格式)
} DXGK_HISTORY_BUFFER;
(2) DXGK_HISTORY_BUFFER_HEADER
typedef struct _DXGK_HISTORY_BUFFER_HEADER {
ULONG BufferSize; // 缓冲区总大小(字节)
ULONG WrittenBytes; // 已写入数据量
LARGE_INTEGER BeginTimestamp; // 缓冲区起始时间(系统时钟)
LARGE_INTEGER EndTimestamp; // 缓冲区结束时间
ULONG Reserved[4]; // 保留字段
} DXGK_HISTORY_BUFFER_HEADER;
(3) DXGKARG_CALIBRATEGPUCLOCK
typedef struct _DXGKARG_CALIBRATEGPUCLOCK {
LARGE_INTEGER GpuClockCounter; // 输出:当前 GPU 时钟值
LARGE_INTEGER CpuClockCounter; // 输出:对应的 CPU 时钟值
LARGE_INTEGER GpuFrequency; // 输出:GPU 时钟频率(Hz)
} DXGKARG_CALIBRATEGPUCLOCK;
3. 驱动实现步骤
(1) 初始化历史缓冲区
在 DxgkDdiCreateAllocation 中创建专用分配:
NTSTATUS DxgkDdiCreateAllocation(
DXGKARG_CREATEALLOCATION* pArgs
) {
if (pArgs->Flags.HistoryBuffer) {
// 分配物理连续内存(DMA 可访问)
pArgs->pAllocationInfo->Size = HISTORY_BUFFER_SIZE;
pArgs->pAllocationInfo->Alignment = PAGE_SIZE;
pArgs->pAllocationInfo->HistoryBuffer = TRUE;
}
return STATUS_SUCCESS;
}
(2) 实现 DxgkDdiCalibrateGpuClock
NTSTATUS DxgkDdiCalibrateGpuClock(
DXGKARG_CALIBRATEGPUCLOCK* pArgs
) {
pArgs->GpuClockCounter = ReadGpuTimestamp();
pArgs->CpuClockCounter = KeQueryPerformanceCounter(NULL);
pArgs->GpuFrequency = GetGpuClockFrequency();
return STATUS_SUCCESS;
}
(3) 实现 DxgkDdiFormatHistoryBuffer
NTSTATUS DxgkDdiFormatHistoryBuffer(
DXGKARG_FORMATHISTORYBUFFER* pArgs
) {
DXGK_HISTORY_BUFFER* pHistory = pArgs->pHistoryBuffer;
// 将二进制数据转换为工具可读格式(如 CSV/JSON)
FormatToHumanReadable(pHistory->Data, pArgs->pFormattedBuffer);
return STATUS_SUCCESS;
}
4. 性能数据记录流程
硬件中断触发:GPU 引擎在关键事件(如任务开始/结束)时写入时间戳到历史缓冲区。
环形缓冲区管理:当缓冲区满时,驱动覆盖最旧数据(类似循环队列)。
工具查询数据:
- 通过 DXGKQAITYPE_HISTORYBUFFERPRECISION 查询精度,调用 DxgkDdiFormatHistoryBuffer 获取格式化数据。
5. WHCK 认证要求
测试项 | 验证目标 | 工具依赖 |
---|---|---|
Device.Graphics.HistoryBuffer |
缓冲区分配、写入和格式化的正确性。 | GPUView、Windows Performance Analyzer |
Device.Graphics.GpuClockCalibration |
GPU/CPU 时钟同步误差 < 1μs。 | 高精度计时器(如 ETW 事件)。 |
6. 调试与优化建议
- ETW 事件:监控 Microsoft-Windows-DxgKrnl 的 HistoryBuffer_* 事件。
- 缓冲区大小:根据 GPU 活动频率调整(通常 4MB-16MB)。
- 数据压缩:对高频采样数据使用增量编码(如 RLE)。
7. 示例数据格式
Timestamp(us),Engine,Utilization(%),Temperature(°C)
12345678,3D,85,72
12345680,Compute,45,68
12345682,VideoDecode,30,65
8. 总结
必要性:WDDM 1.2+ 驱动需实现历史缓冲区以支持性能诊断工具。
关键点:
- 时钟校准确保跨设备时间戳一致性。
- 缓冲区需物理连续,避免 DMA 访问延迟。