Windows 8 及更高版本中的 TDR
从 Windows 8 开始,GPU 超时检测和恢复 (TDR) 行为允许重置部分单个物理适配器,而无需进行适配器范围重置。
有关详细信息,请参阅超时检测和恢复 (TDR)。
要求
- 最低 WDDM 版本:1.2
- 最大 Windows 版本:8
- 驱动程序实现 - 仅完整图形和渲染:必需
- WHLK 要求和测试:Device.Graphics…TDRResiliency
TDR 设备驱动程序接口 (DDI)
为了适应此行为变更,内核模式显示微型端口驱动程序 (KMD) 可以实现以下函数:
KMD 通过设置 DXGK_DRIVERCAPS.SupportPerEngineTDR 成员来指示对这些函数的支持,在这种情况下它必须实现所有列出的函数。
支持这些函数的驱动程序还必须支持 DxgkDdiCollectDbgInfo 函数的零级同步。 此要求可确保在重置操作不影响零级 KMD 调用时,这些调用可以继续。 请参阅 DxgkDdiCollectDbgInfo 的“备注”。
以下结构与上述函数相关联:
- DXGK_DRIVERCAPS
- DXGK_ENGINESTATUS
- DXGKARG_QUERYDEPENDENTENGINEGROUP
- DXGKARG_QUERYENGINESTATUS
- DXGKARG_RESETENGINE
节点
如同用于列出的 TDR 函数那样,节点是单个物理适配器的多个部分之一,可以独立计划。 例如,三维节点、视频解码节点和复制节点可全部存在于同一物理适配器中,并且每个节点都可以分配有单独的节点序号。 在 DxgkDdiQueryDependentEngineGroup 调用中,此分配存储在 DXGKARG_QUERYDEPENDENTENGINEGROUP.NodeOrdinal 成员中。
物理适配器中的节点数由 DXGK_DRIVERCAPS.GpuEngineTopology 的 NbAsymetricProcessingNodes 成员中的显示微型端口驱动程序报告。
创建上下文时,在 DXGKARG_CREATECONTEXT 结构的 NodeOrdinal 成员中传递节点序号值。
引擎
如同用于 TDR DDI 函数那样,引擎是多个物理适配器(或 GPU)之一,一起充当一个逻辑适配器。 Dxgkrnl 支持此类配置,但要求每个引擎都必须具有相同数量的节点。
例如,GPU 计划程序将引擎 0 视为与物理适配器 0 对应。 引擎 0 的节点数必须与引擎 1 相同,这对应于适配器 1。
创建上下文时的引擎序号值
创建上下文时,将在 DXGKARG_CREATECONTEXT 结构的 EngineAffinity 成员中设置对应于引擎序号值的单个位。 此结构和其他计划程序相关结构的 EngineOrdinal 成员是一个从零开始的索引。 EngineAffinity 的值为 1 <<EngineOrdinal,而 EngineOrdinal 是 EngineAffinity 中的最高位位置。
不受引擎重置影响的数据包
GPU 计划程序可能会要求驱动程序重新提交如下数据包:提交到引擎硬件队列太迟而无法在引擎重置完成之前完全处理的数据包。 驱动程序必须遵循以下准则来重新提交此类数据包:
- 分页数据包:GPU 计划程序要求驱动程序使用原始围栏 ID 重新提交分页数据包,其顺序与最初提交顺序相同。 在将新数据包添加到硬件队列之前,将重新提交任何此类数据包。
- 渲染数据包:GPU 计划程序会为呈现数据包分配新围栏 ID,然后将其重新提交。
调用序列以重置引擎
当 DxgkDdiResetEngine 成功时,GPU 计划程序可确保从引擎重置调用返回的 LastAbortedFenceId 值对应于:
- 硬件队列中的现有围栏 ID。
- GPU 上最后一个完成的围栏 ID。 在检测到 GPU 超时之后,但在调用引擎重置回调之前,硬件队列将清空时,可能会出现这种情况。
驱动程序必须始终维护 GPU 上最后一个完成的围栏 ID 值,因为需要该围栏 ID 才能设置 DXGKARGCB_NOTIFY_INTERRUPT_DATA 抢占中断通知结构的 DmaPreempted.LastCompletedFenceId 成员。 最后一个完成的围栏 ID 仅在以下情况下才能提升:
- 当完成(未抢占)数据包时,应将最后一个完成的围栏 ID 设置为已完成数据包的围栏 ID。
- 当 DxgkDdiResetEngine 成功时,应将最后一个完成的围栏 ID 设置为引擎重置调用返回的 LastCompletedFenceId 成员的值。
- 对于适配器范围重置,应在重置时将所有节点上最后一个完成的围栏 ID 提升到最后一个提交的围栏 ID。
下面是成功重置引擎的按时间顺序排列,如 GPU 计划程序所示:
已发出抢占尝试。
检测到 GPU 超时。
GPU 计划程序会创建最后一个提交和已完成的围栏 ID 的快照,并忽略超时引擎的中断。 此组合是设备中断级别的一个原子操作。
如果此时硬件队列中没有数据包,请退出。 当在步骤 2 和步骤 3 之间的时间范围内完成数据包时,可能会出现这种情况。
所有排队的 DPC 都会被刷新。
准备引擎重置。
如果 LastAbortedFenceId 成员小于最后一个完成的围栏 ID 或大于最后一个提交的围栏 ID,Dxgkrnl 将导致进行系统 bug 检查。 在故障转储文件中,BugCheck 0x119 消息指出了此错误,其中包含以下四个参数:
- 0xA,这意味着驱动程序报告了无效的已中止围栏 ID
- 驱动程序返回的 LastAbortedFenceId 值
- 最后一个完成的围栏 ID
- 内部操作系统参数
如果 LastAbortedFenceId 值有效,请继续执行引擎重置恢复,具体如下。 如果引擎重置影响了分页数据包,则 GPU 计划程序会进行引擎重置和适配器范围重置。 拥有该分页数据包引用的分配的所有设备也将被置于错误状态。 系统设备本身未被置于错误状态,并且在重置完成后恢复执行。
特殊情况
在步骤 3 和 7 之间在 GPU 上完成数据包时,可能会出现特殊情况。 在这种情况下,如果从驱动程序的角度来看硬件队列中没有数据包,驱动程序应将 LastAbortedFenceId 设置为最后一个完成数据包的围栏 ID。 从计划程序的角度来看,此类数据包似乎已被中止。 因此,即使数据包最终完成,计划程序也会将对应的设备置于错误状态。
如果驱动程序由于以下任一原因而无法执行重置操作,它应返回失败状态代码:
- 硬件处于无效状态。
- 硬件无法重置节点。
如果 GPU 计划程序收到失败状态代码,则会在 Windows 8 之前遵循 TDR 行为,执行适配器范围重置和重启操作。
即使驱动程序选择进入 Windows 8 及更高版本的 TDR 行为,GPU 计划程序也会请求重置并重启整个逻辑适配器。 因此,驱动程序仍必须实现 DxgkDdiResetFromTimeout 和 DxgkDdiRestartFromTimeout 函数,并且其语义相比 Windows 8 之前保持不变。 尝试使用 DxgkDdiResetEngine 重置物理适配器会导致逻辑适配器重置时,Windows 调试器的 !analyze 命令将显示 TDR 恢复上下文的 TdrReason 值设置为 TdrEngineTimeoutPromotedToAdapterReset = 9 的新值。