window显示驱动开发—从 GPU 查询信息
Direct3D 运行时可能需要来自图形处理单元的信息 (GPU) ,而不是输出呈现目标或输出顶点缓冲区。 由于 GPU 与 CPU 并行执行,因此用户模式显示驱动程序应提供有效公开与 GPU 通信的异步性质的函数。
查询对象是运行时和驱动程序用于异步通知的资源。 若要创建查询对象,运行时首先调用驱动程序的 CalcPrivateQuerySize 函数,以便驱动程序可以提供驱动程序对查询对象所需的内存区域大小。 然后,运行时调用驱动程序的 CreateQuery (D3D10) 函数来创建查询对象。 在 CalcPrivateQuerySize 和 CreateQuery (D3D10) 调用中,运行时从 pCreateQuery 参数指向的 D3D10DDIARG_CREATEQUERY 结构的 Query 成员中的 D3D10DDI_QUERY 枚举中提供查询类型值。
每个查询对象实例都存在于以下三种状态之一: 生成、 发出和 已发出信号。 运行时调用驱动程序的 QueryBegin 函数,将查询对象转换为生成状态。
注意 除D3D10DDI_QUERY_EVENT和D3D10DDI_QUERY_TIMESTAMP外,所有查询类型都支持 QueryBegin 。 D3D10DDI_QUERY_EVENT和D3D10DDI_QUERY_TIMESTAMP不存在建筑概念。
运行时调用驱动程序的 QueryEnd 函数,将查询对象转换为已颁发状态。 在一段时间后异步转换到信号状态。 运行时调用驱动程序的 QueryGetData 函数来检测查询是否已转换为信号状态。 如果查询处于信号状态, QueryGetData 可以传回应用于 pData 参数指向的内存区域中的查询的数据。
同一类型的所有查询对象都是 FIFO (即先入先出) 。 例如,D3D10DDI_QUERY_EVENT类型的所有查询对象都根据其发出的顺序按 FIFO 顺序完成。 但是,不同类型的查询对象可以按重叠顺序完成或发出信号。 例如,D3D10DDI_QUERY_EVENT 类型的查询可以在D3D10DDI_QUERY_OCCLUSION类型的查询之前完成,即使运行时在运行时发出D3D10DDI_QUERY_OCCLUSION查询后发出了D3D10DDI_QUERY_EVENT查询。
当运行时不再需要查询对象时,运行时会释放运行时以前为该对象分配的内存区域,并调用驱动程序的 DestroyQuery (D3D10) 函数来通知驱动程序驱动程序无法再访问此内存区域。
1. 查询对象的核心作用
查询对象是 GPU-CPU 异步通信 的核心机制,用于获取无法直接从渲染目标/顶点缓冲区读取的GPU信息,例如:
- 遮挡查询 (D3D10DDI_QUERY_OCCLUSION):统计像素通过深度测试的数量。
- 时间戳 (D3D10DDI_QUERY_TIMESTAMP):获取GPU时钟计数。
- 事件标记 (D3D10DDI_QUERY_EVENT):确认GPU命令执行完成。
2. 查询对象的生命周期与状态机
(1) 三种状态转换
QueryBegin
调用后QueryEnd
调用后QueryGetData
读取结果(2) 状态转换流程图
[创建] → (闲置) ↓ [QueryBegin] → [生成] ↓ [QueryEnd] → [已发出] → (GPU处理) → [已信号]
3. 驱动实现关键函数
(1) 创建与销毁
// 计算查询对象所需内存SIZE_T APIENTRY CalcPrivateQuerySize( D3D10DDI_HDEVICE hDevice, const D3D10DDIARG_CREATEQUERY* pCreateQuery) { return sizeof(MyQueryPrivateData); // 通常为固定大小}// 初始化查询对象HRESULT APIENTRY CreateQuery( D3D10DDI_HDEVICE hDevice, const D3D10DDIARG_CREATEQUERY* pCreateQuery, D3D10DDI_HQUERY hQuery, D3D10DDI_HRTQUERY hRTQuery) { MyQueryPrivateData* pQuery = (MyQueryPrivateData*)hQuery.pDrvPrivate; pQuery->type = pCreateQuery->Query; // 保存查询类型 pQuery->state = QUERY_STATE_IDLE; return S_OK;}
(2)状态控制
void APIENTRY QueryBegin(D3D10DDI_HDEVICE hDevice, D3D10DDI_HQUERY hQuery) { MyQueryPrivateData* pQuery = (MyQueryPrivateData*)hQuery.pDrvPrivate; pQuery->state = QUERY_STATE_BUILDING; WriteGPUCommand(QUERY_START, pQuery->gpuAddress); // 通知GPU开始记录}
(3) 数据获取
HRESULT APIENTRY QueryGetData( D3D10DDI_HDEVICE hDevice, D3D10DDI_HQUERY hQuery, void* pData, UINT DataSize, D3D10_DDI_GET_DATA_FLAGS Flags) { MyQueryPrivateData* pQuery = (MyQueryPrivateData*)hQuery.pDrvPrivate; if (pQuery->state != QUERY_STATE_SIGNALED) { return S_FALSE; // 数据未就绪 } memcpy(pData, &pQuery->result, min(DataSize, sizeof(pQuery->result))); return S_OK;}
4. 查询类型与FIFO规则
(1) 支持的查询类型
D3D10DDI_QUERY
)OCCLUSION
TIMESTAMP
EVENT
PIPELINE_STATS
(2) FIFO 执行规则
- 同类型查询:严格按提交顺序完成(如 OCCLION_1 → OCCLUSION_2)。
- 跨类型查询:可能乱序完成(如 EVENT 可能早于先提交的 OCCLUSION)。