> 技术文档 > window显示驱动开发—从 GPU 查询信息

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) 三种状态转换

状态 触发条件 允许操作 生成 (Building) QueryBegin 调用后 持续记录GPU数据(如遮挡统计) 已发出 (Issued) QueryEnd 调用后 等待GPU异步完成 已信号 (Signaled) GPU完成数据处理 通过 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) 用途 是否需要 QueryBegin OCCLUSION 统计可见像素数 是 TIMESTAMP 获取GPU时间戳 否 EVENT 标记GPU命令完成 否 PIPELINE_STATS 获取管线统计信息(如VS调用次数) 是

(2) FIFO 执行规则

  • 同类型查询:严格按提交顺序完成(如 OCCLION_1 → OCCLUSION_2)。
  • 跨类型查询:可能乱序完成(如 EVENT 可能早于先提交的 OCCLUSION)。