功能概述
上下文监视机制是GPU与CPU协同计算的核心同步技术,通过受监视围栏(Monitored Fence)实现跨硬件单元的高效协调。其核心目标是解决以下场景的同步需求:
- GPU引擎间同步:例如在多渲染管线中,后处理阶段需等待前级计算完成。
- CPU-GPU异构同步:如CPU需确保GPU完成纹理上传后再进行后续逻辑处理。
- 跨设备资源访问:在异构计算集群中,避免多个GPU或CPU核心对共享资源的读写冲突。
传统信号量机制存在内核态切换开销大、灵活性不足等问题,而受监视围栏通过以下创新实现优化:
- 硬件级原子操作:直接通过GPU/CPU虚拟地址读写围栏值,减少软件层干预。
- 分离式地址映射:为CPU和GPU提供独立的内存视图,兼顾安全性与性能。
- 自适应环绕处理:针对32位原子操作硬件自动管理围栏值溢出,降低开发者负担。
受监视围栏的创建
创建流程与技术细节
1. 触发条件
- Direct3D运行时检测到应用程序调用CreateFence API时,生成D3DDDI_MONITORED_FENCE类型的请求。
- 用户模式驱动程序(UMD)通过CreateSynchronizationObjectCb回调接收创建指令。
2. 参数定义
- 初始值(InitialValue):通常设为0,表示围栏初始未触发状态。在环形缓冲区场景中可预设为历史最大值以规避环绕问题。
- 标志位(Flags):
- D3DDDI_FENCE_FLAG_SHARED:允许跨进程共享围栏对象。
- D3DDDI_FENCE_FLAG_CPU_WRITABLE:启用CPU直接写权限(需硬件支持MMIO)。
- D3DDDI_FENCE_FLAG_GLOBAL_TIMEOUT:设置全局等待超时阈值(默认禁用)。
3.内核资源分配
图形内核完成以下操作:
- 在非分页内存池分配同步对象控制块(Sync Control Block, SCB),存储围栏状态机信息。
- 建立双重地址映射:
- CPU端映射:将物理地址转换为FenceValueCPUVirtualAddress,属性为MEMORY_CACHED_TYPE_WRITEBACK,确保CPU读取时可通过缓存加速
- GPU端映射:若硬件支持Reserved Virtual Address (RVA),则分配固定GPU虚拟地址FenceValueGPUVirtualAddress,否则使用PCI BAR空间映射。
- 初始化围栏监控线程,该线程以10μs周期轮询高优先级围栏对象(可通过注册表调整间隔)。
4.返回值结构
字段 | 技术规格 |
hSyncObject | 64位内核对象句柄,包含版本号(高16位)和对象ID(低48位),有效周期与设备绑定。 |
FenceValueCPUVirtualAddress | 对齐至64字节缓存行,避免False Sharing。支持通过CLFLUSH指令手动刷新缓存。 |
FenceValueGPUVirtualAddress | 对于NVIDIA Ampere架构,强制使用Non-Coherent内存属性以兼容L2 TCC缓存策略。 |
原子操作兼容性处理
当GPU声明DXGK_VIDSCHCAPS::No64BitAtomics=1时,系统启用兼容模式:
- 将64位围栏拆分为两个32位寄存器(高32位为周期计数器,低32位为递增值)。
- 每次信号操作自动执行:
if (Low32 == UINT32_MAX) { High32++; Low32 = 0;
} else { Low32++;
}
- 等待逻辑改为:TargetValue ≤ (High32 << 32) | Low32
- 约束条件:TargetValue - LastSignaledValue < UINT32_MAX/2,防止高32位计数器溢出误判。
GPU信号机制
硬件信号路径
支持原子写入的GPU可直接生成以下机器指令:
; AMD GCN 示例
global_atomic_add_x2 v[FenceValueGPUVirtualAddress], v[signal_value]
s_waitcnt lgkmcnt(0)
此操作在GPU L1缓存中完成,约消耗40个时钟周期。
软件信号路径
当GPU无法直接访问围栏地址(如旧式移动GPU),UMD需构造信号包:
-
在命令缓冲区插入NOP占位符,预留12字节空间。
- 调用SignalSynchronizationObjectFromGpuCb,内核将填充以下微代码:
0xCAFEBABE // 魔数标识信号包
FenceObjectID
SignalValue
- GPU调度器执行到该包时,通过PM4引擎间接更新围栏值,增加约200ns延迟。
内核监控流程
- 每个GPU上下文维护一个PendingFenceList,记录未完成的围栏信号。
- 调度器在提交命令缓冲区前,执行以下操作:
- 将当前时间戳写入围栏值的Bit 63-62(保留位),用于死锁检测。
- 使用红黑树按信号值排序,优化遍历效率。
- 完成事件触发后,内核工作者线程:
- 锁定围栏对象的自旋锁(SpinLock)。
- 对比当前值与等待队列中的目标阈值。
- 对满足条件的等待项,调用KeSetEvent唤醒相关线程。
GPU等待操作
精细化依赖管理
1.多级等待链
例如:
UMD可提交嵌套等待指令
// 等待FenceA≥100 且 FenceB≥200
pfnWaitForSynchronizationObjectFromGpuCb(hFenceA, 100, D3DDDI_WAIT_FLAG_AND);
pfnWaitForSynchronizationObjectFromGpuCb(hFenceB, 200, D3DDDI_WAIT_FLAG_AND);
内核将其转换为依赖图节点,确保所有条件满足后才调度后续任务。
2.超时处理
若等待超过500ms(可配置),内核触发Timeout Workflow:
- 生成WER错误报告,包含当前所有围栏状态快照。
- 强制推进围栏值至目标值,标记设备为"丢失"状态。
- 向UMD返回STATUS_GRAPHICS_DRIVER_THREAD_REQUEST_TIMEOUT错误码。