window 显示驱动开发-线程同步和 TDR
下图显示了 Windows 显示驱动程序模型 (WDDM) 中显示微型端口驱动程序的线程同步的工作原理
如果发生硬件超时,则会启动 超时检测和恢复 (TDR) 进程。 GPU 计划程序调用驱动程序的 DxgkDdiResetFromTimeout 函数,这将重置 GPU。 DxgkDdiResetFromTimeout 与任何其他显示微型端口驱动程序函数同步调用,运行时电源管理功能 DxgkDdiSetPowerComponentFState 和 DxgkDdiPowerRuntimeControlRequest 除外。 也就是说, 在 DxgkDdiResetFromTimeout 线程运行时,驱动程序中没有其他线程运行。 操作系统还保证在调用 DxgkDdiResetFromTimeout 期间,任何应用程序都无法访问帧缓冲区;因此,驱动程序可以重置内存控制器相位锁定循环 (PLL) 等。
当恢复线程执行 DxgkDdiResetFromTimeout 时,可以继续调用中断和延迟过程调用 (DPC) 。 KeSynchronizeExecution 函数可用于将重置过程的某些部分与设备中断同步。
驱动程序从 DxgkDdiResetFromTimeout 返回后,可以再次调用大多数驱动程序函数,并且操作系统开始清理不再需要的资源。 在清理期间,出于指示的原因调用以下驱动程序函数:
- 调用驱动程序以通知正在逐出分配。
例如,如果分配是在内存段中分页的,则会调用驱动程序的 DxgkDdiBuildPagingBuffer 函数,并将 DXGKARG_BUILDPAGINGBUFFER 结构的 Operation 成员设置为 DXGK_OPERATION_TRANSFER,Transfer.Size 成员设置为零,以通知驱动程序有关逐出的信息。 请注意,由于内容在重置期间丢失,因此不涉及内容传输。
如果分配是在光圈段中分页的,则会调用驱动程序的 DxgkDdiBuildPagingBuffer 函数,并将 DXGKARG_BUILDPAGINGBUFFER 的 Operation 成员设置为 DXGK_OPERATION_UNMAP_APERTURE_SEGMENT,以通知驱动程序取消映射光圈中的分配。
- 调用驱动程序的 DxgkDdiReleaseSwizzlingRange 函数来释放不重排光圈和段光圈范围。
除非绝对必要,否则驱动程序不应在上述调用期间访问 GPU。
清理期结束后,操作系统调用驱动程序的 DxgkDdiRestartFromTimeout 函数,以通知驱动程序清理已完成,操作系统将恢复使用适配器进行呈现。
1. TDR 触发条件
- GPU 长时间未响应(通常由于 命令队列卡死、硬件故障 或 驱动死锁)。
- Windows GPU 调度器检测到超时,并启动恢复流程。
2. TDR 核心流程
(1) 调用 DxgkDdiResetFromTimeout(GPU 重置)
同步性:
- 此函数是 完全同步调用,即在执行期间:
- 禁止其他任何驱动线程运行(除了 DxgkDdiSetPowerComponentFState 和 DxgkDdiPowerRuntimeControlRequest)。
- 应用程序无法访问帧缓冲区(确保内存安全)。
- 操作系统保证 GPU 处于 可安全重置 的状态。
驱动职责:
- 重置 GPU 硬件(如寄存器、PLL、内存控制器等)。
- 清理 GPU 内部状态(如命令队列、缓存)。
可调用 KeSynchronizeExecution 与设备中断同步(避免竞争条件)。
(2) 中断和 DPC 处理
中断和 DPC(延迟过程调用)仍可继续执行,但驱动程序需确保:
- 不会与 DxgkDdiResetFromTimeout 冲突(如使用自旋锁保护关键代码)。
(3) 重置完成后的资源清理
在 DxgkDdiResetFromTimeout 返回后,操作系统开始 清理无效资源,并调用以下驱动函数:
驱动函数 | 调用原因 | 参数说明 |
---|---|---|
DxgkDdiBuildPagingBuffer | 通知分配被逐出 | - Operation = DXGK_OPERATION_TRANSFER (Transfer.Size = 0 ):表示内存段分配被逐出(无数据传输,因内容已丢失)。- Operation = DXGK_OPERATION_UNMAP_APERTURE_SEGMENT :取消映射光圈段(Aperture Segment)中的分配。 |
DxgkDdiReleaseSwizzlingRange | 释放 Swizzling Range | 清理所有不再需要的重排范围(Swizzling Range)。 |
关键注意事项:
- 驱动程序应避免访问 GPU,除非绝对必要(如释放硬件资源)。
- 不涉及数据传输(因为 GPU 重置后显存内容已丢失)。
(4) 调用 DxgkDdiRestartFromTimeout(恢复呈现)
清理完成后,操作系统调用 DxgkDdiRestartFromTimeout,通知驱动:
- TDR 恢复流程结束。
- GPU 可以重新用于图形渲染。
驱动应:
- 重新初始化必要的硬件状态(如默认分辨率、颜色格式)。
- 恢复 GPU 调度器工作(允许提交新命令)。
3. 驱动开发注意事项
(1) 线程安全性
DxgkDdiResetFromTimeout 运行期间,禁止其他驱动线程运行,但仍需:
- 保护共享硬件资源(如 MMIO 寄存器)。
- 避免死锁(如不等待可能被阻塞的锁)。
(2) 内存管理
显存内容在重置后丢失,因此:
- 驱动不应尝试恢复旧数据。
- 应用程序需重新提交丢失的资源(如纹理、缓冲区)。
(3) 错误恢复
如果 DxgkDdiResetFromTimeout 失败:
- 系统可能 完全禁用 GPU(降级到基本显示模式)。
- 用户可能需要 重启系统 恢复功能。
(4) 调试支持
在 DxgkDdiCollectDbgInfo(Reason = VIDEO_TDR_TIMEOUT_DETECTED)中:
- 记录足够信息(如最后提交的命令、GPU 状态)。
- 确保代码可分页(PASSIVE_LEVEL)。
4. 典型 TDR 时序图
1. GPU 超时发生│
2. WDDM 检测超时,暂停 GPU 调度│
3. 调用 DxgkDdiResetFromTimeout (同步执行)│ ├─ 重置 GPU 硬件│ └─ 清理内部状态│
4. 操作系统清理资源:│ ├─ DxgkDdiBuildPagingBuffer (逐出分配)│ └─ DxgkDdiReleaseSwizzlingRange (释放范围)│
5. 调用 DxgkDdiRestartFromTimeout│
6. GPU 恢复工作,应用程序重新提交命令
5. 总结
阶段 | 关键动作 | 驱动职责 |
---|---|---|
检测超时 | GPU 无响应 | 无(系统自动触发) |
重置 GPU | DxgkDdiResetFromTimeout | 重置硬件,避免竞争 |
清理资源 | BuildPagingBuffer / ReleaseSwizzlingRange | 释放无效分配 |
恢复工作 | DxgkDdiRestartFromTimeout | 重新初始化 GPU |
TDR 是 Windows 图形稳定性的关键机制,驱动程序需正确处理重置和资源清理,以避免系统崩溃或图形异常