Windows 图形显示驱动开发-WDDM 3.2-本机 GPU 围栏对象(八)
同步更新本机围栏日志
GPU 写入以更新围栏值和相应的日志缓冲区必须确保在 CPU 读取之前写入已完全传播。 此要求需要使用内存屏障。 例如:
- 信号围栏 (N):将 N 写入为新的当前值
- 写入日志条目,包括 GPU 时间戳
- MemoryBarrier
- 递增 FirstFreeEntryIndex
- MemoryBarrier
- 受监视的围栏中断 (N):读取地址“M”,并将该值与 N 进行比较,以决定是否传递 CPU 中断
在每个 GPU 信号上插入两个屏障的成本太高,尤其是在条件中断检查不满足且不需要 CPU 中断时。 因此,设计将插入其中一个内存屏障的成本从 GPU(生产者)转移到 CPU(消费者)。 Dxgkrnl 调用引入的 DxgkDdiUpdateNativeFenceLogs 函数,使 KMD 按需同步刷新挂起的本机围栏日志写入(类似于为 HW 翻转队列日志刷新引入 DxgkddiUpdateflipqueuelog 的方式)。
对于 GPU 操作:
- 信号围栏 (N):将 N 写入为新的当前值
- 写入日志条目,包括 GPU 时间戳
- 递增 FirstFreeEntryIndex
- MemoryBarrier => 确保 FirstFreeEntryIndex 完全传播
- 受监视的围栏中断 (N):读取地址“M”,并将该值与 N 进行比较,以决定是否传递中断
对于 CPU 操作:
- 在 Dxgkrnl 的本机围栏信号中断处理程序 (DISPATCH_IRQL) 中:
- 对于每个 HWQueue 日志:读取 FirstFreeEntryIndex,并确定是否写入新条目。
- 对于每个包含新条目的 HWQueue 日志:调用 DxgkDdiUpdateNativeFenceLogs,并为这些 HWQueue 提供内核句柄。 在此 DDI 中,KMD 向每个给定的 HWQueue 插入一个内存屏障,以确保提交所有日志条目写入。
- Dxgkrnl 读取日志条目以提取时间戳有效负载。
因此,只要硬件在写入 FirstFreeEntryIndex 后插入内存屏障,Dxgkrnl 就会始终调用 KMD 的 DDI,从而允许 KMD 在 Dxgkrnl 读取任何日志条目之前插入内存屏障。
未来硬件要求
大多数当前一代硬件可能只支持在本机围栏信号中断中写入它发出信号的本机对象的内核句柄。 此设计在前面的本机围栏信号中断中进行了介绍。 在这种情况下,Dxgkrnl 处理中断有效负载,如下所示:
- OS 对围栏值执行读取(可能跨 PCI)。
- 知道哪个围栏已发出信号和围栏值,OS 会唤醒正在等待该围栏/值的 CPU 等待程序。
- 另外,对于此围栏的父设备,OS 会扫描其所有 HWQueues 的日志缓冲区。 然后,OS 读取最后一个写入的日志缓冲区条目,以确定哪个 HWQueue 发出了信号,并提取相应的时间戳有效负载。 这种方法可能会在 PCI 上冗余地读取一些围栏值。
在未来的平台上,Dxgkrnl 更倾向于在本机围栏信号中断中获取内核 HwQueue 句柄数组。 此方法使 OS 能够:
- 读取该 HwQueue 的最新日志缓冲区条目。 中断处理程序不知道用户设备;因此,此 HwQueue 句柄需要是一个内核句柄。
- 扫描日志缓冲区中的日志条目,以指示哪些围栏已发出信号,以及信号的值。 仅读取日志缓冲区可确保通过 PCI 进行单次读取,而不必冗余地读取围栏值和日志缓冲区。 只要日志缓冲区未溢出(删除 Dxgkrnl 从不读取的条目),此优化就会成功。
- 如果 OS 检测到日志缓冲区已溢出,它会返回到读取同一设备所拥有的每个围栏的实时值的非优化路径。 性能与设备拥有的围栏数量成正比。 如果围栏值位于视频内存中,则这些读取在 PCI 之间是缓存一致的。
- 知道哪些围栏已发出信号和围栏值,OS 会唤醒正在等待这些围栏/值的 CPU 等待程序。
优化的本机围栏信号中断
除了本机围栏信号中断中所述的更改外,还进行了以下更改以支持优化的方法:
- 为 DXGK_VIDSCHCAPS 添加了 OptimizedNativeFenceSignaledInterrupt 上限。
如果硬件支持,则 GPU 应只提及引发中断时运行的 HWQueue 的 KMD 句柄,而不是填写发出信号的围栏句柄数组。 Dxgkrnl 扫描此 HWQueue 的围栏日志缓冲区,并读取自上次更新以来 GPU 完成的所有围栏操作,并取消阻止任何相应的 CPU 等待程序。 如果 GPU 无法确定哪个子集的围栏已发出信号,则应指定 NULL HWQueue 句柄。 当 Dxgkrnl 看到 NULL HWQueue 句柄时,它会回退以重新扫描此引擎上所有 HWQueue 的日志缓冲区,以确定哪些围栏得到了信号。
对此优化的支持是可选的;KMD 应设置 DXGK_VIDSCHCAPS:OptimizedNativeFenceSignaledInterrupt 上限(如果硬件支持)。 如果未设置 OptimizedNativeFenceSignaledInterrupt 上限,则 GPU/KMD 会遵循本机围栏信号中断中所述的行为。
优化的本机围栏信号中断的示例
-
HWQueueA:GPU 信号到围栏 F1、值 V1 -> 写入日志缓冲区条目 E1 -> 无需中断
-
HWQueueA:GPU 信号到围栏 F1、值 V2 -> 写入日志缓冲区条目 E2 -> 无需中断
-
HWQueueA:GPU 信号到围栏 F2、值 V3 -> 写入日志缓冲区条目 E3 -> 无需中断
-
HWQueueA:GPU 信号到围栏 F2、值 V3 -> 写入日志缓冲区条目 E4 -> 引发中断
DXGKARGCB_NOTIFY_INTERRUPT_DATA FenceSignalISR = {}; FenceSignalISR.NodeOrdinal = 0; FenceSignalISR.EngineOrdinal = 0; FenceSignalISR.hHWQueue = A;
-
Dxgkrnl 读取 HWQueueA 的日志缓冲区。 它读取日志缓冲区条目 E1、E2、E3 和 E4,观察信号围栏 F1 @ Value V1、F1 @ Value V2、F2 @ Value V3 和 F2 @ Value V3,并取消阻止在这些围栏和值上等待的任何等待程序.
可选和强制日志记录
必须支持 DXGK_NATIVE_FENCE_LOG_TYPE_WAITS 和DXGK_NATIVE_FENCE_LOG_TYPE_SIGNALS 的本机围栏日志记录。
未来,只有当 GPUView 等工具在 OS 中启用详细的 ETW 日志记录时,才可能添加其他日志记录类型。 OS 必须通知 UMD 和 KMD 何时启用和禁用详细日志记录,以便选择性地启用这些详细事件的日志记录。