D3D9查询(Queries Direct3d9)
来源:d3d9帮助文档 Queries(Direct3d9)
翻译:游蓝海(http://blog.csdn.net/you_lan_hai)
说明:费了好大劲,终于看懂了d3d9 queries,翻译一下跟大家共享。第一次翻译文章,有很多地方不准确,请多多见谅。
概述
有很多种查询,用来查询资源的状态,这些资源状态包括图形处理单元(GPU)的状态、驱动状态、运行时状态。为了理解不同类型的查询,你需要先理解查询状态。下面的状态流图,说明了每个查询的状态。
查询状态图
图表列举出了3种状态(Signaled State, Building State, Issued State),分别用圆圈表示。实线表示应用程序驱动事件引发的状态传递,虚线表示资源驱动事件将一个查询从完成状态(Issued State)切换到了就绪状态(Signaled State),每种状态都有不同的用处:
·就绪状态(Signaled State):像是一个空闲的状态。查询对象已经生成,等待应用程序来完成此次查询。当查询结束,查询状态转变为就绪状态时,就可以获取到查询结果了。
·创建状态(Building State): 像是一个暂存区域。在创建状态,查询已经被发起(通过调用D3DISSUE_BEGIN),但还没有转换到完成状态。当应用程序申明查询结束(通过调用D3DISSUE_END),查询便转换到了完成状态。
·完成状态(Issued State): 表示被查询的资源已经接管了查询。当资源完成工作后,会将状态机转换到就绪状态。在完成阶段,应用程序必须检测是否已经转变成就绪状态,如果是就绪状态,那么GetData返回的就是需要的查询结果了。
一些查询需要开始和结束事件,但是有些仅需要结束事件。仅需要一个结束事件的查询,会在一些暗含的事件发生后自动开始。所有的查询都会返回一个结果,除了事件查询(event query)总是会返回TRUE。
创建查询(Create a query)
在创建查询之前,你可以检查运行时(runtime)是否支持此查询,调用CreateQuery,并传入一个NULL,如下:
IDirect3DQuery9* pEventQuery; // Create a device pointer m_pd3dDevice // Create a query object HRESULT hr = m_pd3dDevice->CreateQuery(D3DQUERYTYPE_EVENT, NULL);
如果该查询可以被创建,改方法返回成功码;否则,返回一个错误码。一旦CreateQuery调用成功,你可以这样创建一个查询:
IDirect3DQuery9* pEventQuery; m_pd3dDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery);
如果函数调用成功,一个查询对象
就创建完成了。查询初始为就绪状态,等待被激活。当查询对象使用完毕之后,像其他d3d对象一样,释放掉它。
发起查询Issue a Query
应用程序通过发起查询来改变一个查询的状态,这里有一个发起查询的例子:
IDirect3DQuery9* pEventQuery; m_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery); // Issue a Begin event pEventQuery->Issue(D3DISSUE_BEGIN); or // Issue an End event pEventQuery->Issue(D3DISSUE_END);
当发起之后,一个处于就绪状态的查询,将会这样改变:
发起类型 | 查询变成这样 . . . |
---|---|
D3DISSUE_BEGIN | 创建状态(Building state). |
D3DISSUE_END | 完成状态(Issued state). |
发起之后,一个处于创建状态的查询,将会这样改变:
发起类型 | 查询变成这样 . . . |
---|---|
D3DISSUE_BEGIN | (仍然是创建状态,会 重置查询框架 .) |
D3DISSUE_END | 完成状态. |
发起之后,一个处于完成状态的查询,将会这样改变:
发起类型 | 查询变成这样 . . . |
---|---|
D3DISSUE_BEGIN | 创建状态并且重置查询框架。 |
D3DISSUE_END | 放弃已经存在的查询,仍然是完成状态。 |
检查查询状态以及获得查询结果(Check the Query State and Get the Answer to the Query)
GetData做了两件事情:
1.通过函数返回值,返回查询状态。
2.通过参数pData,返回查询结果。
针对三种查询状态,GetData的返回值如下:
查询状态 | GetData 返回值 |
---|---|
Signaled | S_OK |
Building | Error code |
Issued | S_FALSE |
例如,当查询处于完成状态时,查询结果不可用,GetData会返回S_FALSE。当资源完成它的工作并且应用程序已经发起过查询结束事件,资源将使查询状态转为就绪状态。在就绪状态中,如果GetData返回S_OK,则表示查询结果已经写入了pData中。举个例子,以下查询事件,返回了一次渲染中绘制的像素个数:
·创建查询。
·发起查询开始事件。
·渲染一些东西。
·发起查询结束事件。
下面是该查询的源码:
IDirect3DQuery9* pOcclusionQuery; DWORD numberOfPixelsDrawn; m_pD3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery); // Add an end marker to the command buffer queue. pOcclusionQuery->Issue(D3DISSUE_BEGIN); // API render loop ... Draw(...) ... // Add an end marker to the command buffer queue. pOcclusionQuery->Issue(D3DISSUE_END); // Force the driver to execute the commands from the command buffer. // Empty the command buffer and wait until the GPU is idle. while(S_FALSE == pOcclusionQuery->GetData( &numberOfPixelsDrawn, sizeof(DWORD), D3DGETDATA_FLUSH )) ;
这些代码做了这些事情:
·调用GetData获取已经绘制的像素个数。
·设定 D3DGETDATA_FLUSH参数,表明允许资源将查询转换成就绪状态。
·不断的检查资源,只要返回S_FALSE,就表示还没有拿到结果。
GetData返回了当前的查询状态。他的返回值可能是S_OK,S_FALSE或者是一个错误码。注意,不要在创建状态调用GetData.
- S_OK表示资源 (GPU or driver, or runtime) 已经完成了工作,此时查询已经变成了就绪状态,GetData已经返回了查询结果。
- S_FALSE 表示资源 (GPU or driver, or runtime) 目前还不能返回结果.。这表示GPU还没完成工作或者还没执行到查询。
- 出现错误,表示查询出现了致命错误,这可能是设备丢失导致的。如果查询已经出现了错误(不是S_FALSE),查询必须要重新创建,然后从就绪状态重新开始。
除了指定 D3DGETDATA_FLUSH参数外,也可以传入0,进行轻量级的查询状态检测。传入0时,GetData将不会刷新命令缓冲区,因此,需要小心查询陷入死循环。当运行时工作在命令缓冲区时,D3DGETDATA_FLUSH模式将有一次机会刷新缓冲区,将查询转变成就绪状态。
例子: 事件查询
事件查询不支持begin event.
- 创建查询。
- 产生一个结束事件。
- 一直检测,直到GPU完成工作。
- 产生一个结束事件。
IDirect3DQuery9* pEventQuery = NULL; m_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery); // Add an end marker to the command buffer queue. pEventQuery->Issue(D3DISSUE_END); // Empty the command buffer and wait until the GPU is idle. while(S_FALSE == pEventQuery->GetData( NULL, 0, D3DGETDATA_FLUSH )) ; ... // API calls // Add an end marker to the command buffer queue. pEventQuery->Issue(D3DISSUE_END); // Force the driver to execute the commands from the command buffer. // Empty the command buffer and wait until the GPU is idle. while(S_FALSE == pEventQuery->GetData( NULL, 0, D3DGETDATA_FLUSH )) ;
应用程序需要特别注意刷新命令缓冲区产生的开销,这源于操作系统会切换到内核模式,因此产生了相当大的性能损耗。应用程序也应该意识到等待查询结束时浪费的cpu循环。
在渲染期间,查询可作为优化来增加性能。花费时间等待查询结束没有益处。取代上面循环的位置,如果查询已经是完成状态,但是应用程序还没有拿到查询结果,优化尝试失败,渲染应该像平常一样继续进行。经典的例子就是隐藏面剔除。如果查询完成,应用程序可以使用查询结果来实现隐藏面剔除;如果查询没有完成,这时跳过隐藏面剔除测试,直接渲染物体,就仿佛是测试不成立,物体没有被裁减。
IDirect3DQuery9* pOcclusionQuery = NULL; m_pD3DDevice->CreateQuery( D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery ); // Add a begin marker to the command buffer queue. pOcclusionQuery->Issue( D3DISSUE_BEGIN ); ... // API calls // Add an end marker to the command buffer queue. pOcclusionQuery->Issue( D3DISSUE_END ); // Avoid flushing and letting the CPU go idle by not using a while loop. // Check if queries are finished: DWORD dwOccluded = 0; if( S_FALSE == pOcclusionQuery->GetData( &dwOccluded, sizeof(DWORD), 0 ) ) { // Query is not done yet or object not occluded; avoid flushing/wait by continuing with worst-case scenario pSomeComplexMesh->Render(); } else if( dwOccluded != 0 ) { // Query is done and object is not occluded. pSomeComplexMesh->Render(); }