本文提供有关 Windows 10(WDDM 2.0)中引入的上下文监视的信息。
上下文监视允许 GPU 引擎或 CPU 核心和 GPU 引擎之间灵活同步。 受监视的围栏对象是隔离同步的高级形式,允许 CPU 核心或 GPU 引擎向特定围栏对象发出信号或等待。
监控围栏创建
Direct3D 运行时通过使用D3DDDICB_CREATESYNCHRONIZATIONOBJECT2结构调用用户模式驱动程序 (UMD) pfnCreateSynchronizationObject2Cb 回调来创建受监视的围栏对象。 此结构的 Info 成员是 D3DDDI_SYNCHRONIZATIONOBJECTINFO2 结构,用于描述要创建的同步对象。 运行时将 Info.Type 设置为 D3DDDI_MONITORED_FENCE ,以指示在创建过程中将使用 Info.MonitoredFence 结构。
创建的受监视围栏对象具有以下属性:
- 初始围栏值。
- 指定其等待和信号行为的标志。
创建后,返回包含以下信息的受监视围栏对象:
| 条目 | DESCRIPTION |
|---|---|
| hSyncObject | 同步对象的句柄。 此句柄用于对 Dxgkrnl 的后续调用。 |
| FenceValueCPUVirtualAddress | CPU 围栏值的只读映射(64 位)。 此地址在支持I/O一致性的那些平台上从CPU的角度来看是WB(可缓存),而在其他平台上是UC(未缓存)。 通过仅读取此内存位置,CPU 可以跟踪围栏进度。 不允许将 CPU 写入此内存位置。 若要向围栏发出信号,需要 CPU 才能调用 SignalSynchronizationObjectFromCpuCb。 支持 IoMmu 的适配器应使用此地址进行 GPU 访问。 在这种情况下,地址被映射为可读写。 |
| FenceValueGPUVirtualAddress | GPU 的围栏值(64 位)的读/写映射。 此地址被映射为在支持此功能的平台上需要 I/O 一致性。 为了向围栏发出信号,允许 GPU 直接写入此 GPU 虚拟地址。 IoMmu GPU 不应使用此地址。 |
围栏值是一个 64 位值,其各自的虚拟地址在 64 位边界上对齐。 GPU 应通过添加 DXGK_VIDSCHCAPS::No64BitAtomics 标志来声明它们是否能够以原子方式更新 64 位值,这些值对 CPU 是可见的。 如果 GPU 只能以原子方式更新 32 位值,则操作系统会自动处理栅栏回绕情况。 但是,它有一个限制,即未处理的等待和信号围栏值与最后一个信号围栏值之间的差距不能超过 UINT_MAX/2。
GPU 信号
如果 GPU 引擎无法使用其虚拟地址写入受监控的栅栏,UMD 使用 SignalSynchronizationObjectFromGpuCb 回调将软件信号包排入 GPU 上下文。
若要从 GPU 向围栏发出信号,UMD 会在上下文命令流中直接插入隔离写入命令,而无需通过内核模式。 内核监视隔离进度的机制因特定 GPU 引擎是否支持受监视围栏的基本实现或高级实现而异。
当命令缓冲区在 GPU 上完成执行时, Dxgkrnl:
- 浏览具有挂起等待的围栏对象列表,这些等待可以发出此过程的信号。
- 读取其当前围栏值。
- 确定是否存在需要被解除的等待。
GPU 等待
若要在 GPU 引擎上等待一个受监视的围栏,UMD 首先需要刷新其挂起的命令缓冲区,然后调用 WaitForSynchronizationObjectFromGpuCb 并指定同步对象(hSyncObject)和正在等待的围栏值。 Dxgkrnl 将依赖项排入其内部数据库,然后立即返回到 UMD,使其能够在等待操作期间继续排队工作。 在等待操作被满足之前,后提交的命令缓冲区不会被安排执行。
CPU 信号
添加了 SignalSynchronizationObjectFromCpuCb 回调,以允许 CPU 向受监视的围栏对象发出信号。 当 CPU 向受监视的围栏对象发出信号时, Dxgkrnl 将使用信号值更新围栏内存位置。 此值对任何用户模式读取器立即可见,并立即不需要任何满意的等待。
CPU 等待
添加了 WaitForSynchronizationObjectFromCpuCb 回调,以允许 CPU 在受监视的围栏对象上等待。 有两种形式的等待操作可供使用:
- 在第一种形式中,WaitForSynchronizationObjectFromCpuCb 会处于阻塞状态,直到等待条件满足。
- 第二种形式是 WaitForSynchronizationObjectFromCpuCb 处理 CPU 事件,该事件在满足等待条件后发出信号。