クエリ (Direct3D 9)
リソースの状態を照会するように設計されたクエリには、いくつかの種類があります。 特定のリソースの状態には、グラフィックス処理装置 (GPU) の状態、ドライバーの状態、またはランタイムの状態が含まれます。 さまざまなクエリの種類の違いを理解するには、クエリの状態を理解する必要があります。 次の状態遷移図では、各クエリの状態について説明します。
この図は、それぞれ円で定義された 3 つの状態を示しています。 実線はそれぞれ、状態遷移を引き起こすアプリケーション駆動型イベントです。 破線は、クエリを発行された状態からシグナル状態に切り替えるリソースドリブン イベントです。 これらの状態にはそれぞれ異なる目的があります。
- シグナル状態はアイドル状態に似ています。 クエリ オブジェクトが生成され、アプリケーションがクエリを発行するのを待機しています。 クエリが完了し、シグナル状態に戻ると、クエリに対する回答を取得できます。
- ビルド状態は、クエリのステージング領域に似ています。 ビルド状態から、クエリは ( D3DISSUE_BEGINを呼び出すことによって) 発行されましたが、発行された状態にまだ移行されていません。 アプリケーションが ( D3DISSUE_END を呼び出して) クエリの終了を発行すると、クエリは発行された状態に遷移します。
- 発行された状態は、クエリ対象のリソースがクエリを制御していることを意味します。 リソースの作業が完了すると、リソースはステート マシンをシグナル状態に移行します。 発行された状態の間、アプリケーションはポーリングしてシグナル状態への遷移を検出する必要があります。 シグナル状態への遷移が発生すると、 GetData はクエリ結果を (引数を介して) アプリケーションに返します。
次の表に、使用可能なクエリの種類を示します。
クエリの型 | Issue イベント | GetData バッファー | ランタイム | クエリの暗黙的な開始 |
---|---|---|---|---|
BANDWIDTHTIMINGS | D3DISSUE_BEGIN、 D3DISSUE_END | D3DDEVINFO_D3D9BANDWIDTHTIMINGS | Retail/Debug | なし |
CACHEUTILIZATION | D3DISSUE_BEGIN、 D3DISSUE_END | D3DDEVINFO_D3D9CACHEUTILIZATION | Retail/Debug | 該当なし |
イベント | D3DISSUE_END | BOOL | Retail/Debug | CreateDevice |
INTERFACETIMINGS | D3DISSUE_BEGIN、 D3DISSUE_END | D3DDEVINFO_D3D9INTERFACETIMINGS | Retail/Debug | なし |
閉塞 | D3DISSUE_BEGIN、 D3DISSUE_END | DWORD | Retail/Debug | 該当なし |
PIPELINETIMINGS | D3DISSUE_BEGIN、 D3DISSUE_END | D3DDEVINFO_D3D9PIPELINETIMINGS | Retail/Debug | 該当なし |
Resourcemanager | D3DISSUE_END | D3DDEVINFO_ResourceManager | デバッグのみ | 存在 |
timestamp | D3DISSUE_END | UINT64 | Retail/Debug | なし |
TIMESTAMPDISJOINT | D3DISSUE_BEGIN、 D3DISSUE_END | BOOL | Retail/Debug | 該当なし |
TIMESTAMPFREQ | D3DISSUE_END | UINT64 | Retail/Debug | なし |
VCACHE | D3DISSUE_END | D3DDEVINFO_VCACHE | Retail/Debug | CreateDevice |
VERTEXSTATS | D3DISSUE_END | D3DDEVINFO_D3DVERTEXSTATS | デバッグのみ | 存在 |
VERTEXTIMINGS | D3DISSUE_BEGIN、 D3DISSUE_END | D3DDEVINFO_D3D9STAGETIMINGS | Retail/Debug | 該当なし |
クエリの中には begin イベントと end イベントが必要なクエリもあれば、終了イベントのみが必要なクエリもあります。 終了イベントのみを必要とするクエリは、別の暗黙的なイベントが発生したときに開始されます (表に示されています)。 すべてのクエリは、常に TRUE であるイベント クエリを除き、回答を返します。 アプリケーションは、クエリの状態または GetData のリターン コードを使用します。
クエリを作成する前に、次のような NULL ポインターを使用して CreateQuery を呼び出すことで、ランタイムがクエリをサポートしているかどうかを確認チェック。
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);
この呼び出しが成功すると、クエリ オブジェクトが作成されます。 クエリは、基本的にシグナル状態 (初期化されていない回答を含む) でアイドル状態になり、発行を待機しています。 クエリが完了したら、他のインターフェイスと同様に解放します。
アプリケーションは、クエリを発行してクエリの状態を変更します。 クエリを発行する例を次に示します。
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 | ビルド状態。 |
D3DISSUE_END | 発行済み状態。 |
ビルド状態のクエリは、発行時に次のように切り替わります。
問題の種類 | クエリが に切り替わる。 . . |
---|---|
D3DISSUE_BEGIN | (切り替えなし、建物の状態のままです。クエリ角かっこを再起動します。) |
D3DISSUE_END | 発行済み状態。 |
発行された状態のクエリは、発行時に次のように切り替わります。
問題の種類 | クエリが に切り替わる。 . . |
---|---|
D3DISSUE_BEGIN | 状態を構築し、クエリ ブラケットを再起動します。 |
D3DISSUE_END | 既存のクエリを破棄した後に発行された状態。 |
GetData では、次の 2 つの処理が行われます。
- リターン コードのクエリ状態を返します。
- pData のクエリに対する回答を返します。
3 つのクエリ状態のそれぞれから、 GetData リターン コードを次に示します。
クエリの状態 | GetData リターン コード |
---|---|
シグナル | S_OK |
ビルド | エラー コード |
発行済み | S_FALSE |
たとえば、クエリが発行された状態で、クエリに対する回答が使用できない場合、 GetData はS_FALSEを返します。 リソースが作業を完了し、アプリケーションがクエリ終了を発行すると、リソースはクエリをシグナル状態に移行します。 シグナル状態から GetData は S_OKを返します。つまり、クエリに対する回答も pData で返されます。 たとえば、レンダリング シーケンスで描画されるピクセル数 (またはマルチサンプリングが有効な場合はサンプル) を返すイベントのシーケンスを次に示します。
- クエリを作成します。
- begin イベントを発行します。
- 何かを描画します。
- 終了イベントを発行します。
対応するコード シーケンスを次に示します。
IDirect3DQuery9* pOcclusionQuery;
DWORD numberOfSamplesDrawn;
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( &numberOfSamplesDrawn,
sizeof(DWORD), D3DGETDATA_FLUSH ))
;
// To get the number of pixels drawn when multisampling is enabled,
// divide numberOfSamplesDrawn by the sample count of the render target.
これらのコード行は、次のいくつかのことを行います。
- GetData を呼び出して、描画されたピクセル/サンプルの数を返します。
- D3DGETDATA_FLUSHを指定して、リソースがクエリをシグナル状態に移行できるようにします。
- ループから GetData を 呼び出して、クエリ リソースをポーリングします。 GetData がS_FALSEを返す限り、これはリソースがまだ回答を返していない場合を意味します。
GetData の戻り値は、基本的にクエリの状態を示します。 使用可能な値は、S_OK、S_FALSE、およびエラーです。 ビルド状態のクエリで GetData を呼び出さないでください。
- S_OKは、リソース (GPU またはドライバー、またはランタイム) が完了したことを意味します。 クエリがシグナル状態に戻ります。 回答 (ある場合) は 、GetData によって返されます。
- S_FALSEは、リソース (GPU またはドライバー、またはランタイム) が応答をまだ返すことができない場合を意味します。 これは、GPU が完了していないか、まだ作業を見ていないことが原因である可能性があります。
- エラーは、クエリが復旧できないエラーを生成したことを意味します。 これは、クエリ中にデバイスが失われた場合に発生する可能性があります。 クエリで (S_FALSE以外の) エラーが生成されたら、クエリを再作成する必要があります。これにより、シグナル状態からクエリ シーケンスが再起動されます。
より最新の情報を提供するD3DGETDATA_FLUSHを指定する代わりに、クエリが発行された状態にある場合は、より軽量なチェックであるゼロを指定できます。 0 を指定すると、 GetData はコマンド バッファーをフラッシュしません。 このため、無限ループを回避するには注意が必要です (詳細については 、「GetData 」を参照してください)。 ランタイム キューはコマンド バッファーで機能するため、 D3DGETDATA_FLUSH はコマンド バッファーをドライバーにフラッシュするためのメカニズムです (したがって GPU です。 Direct3D API 呼び出しの正確なプロファイリング (Direct3D 9) を参照してください)。 コマンド バッファーフラッシュ中に、クエリがシグナル状態に遷移する可能性があります。
イベント クエリでは、begin イベントはサポートされていません。
- クエリを作成します。
- 終了イベントを発行します。
- 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 ))
;
これは、イベント クエリがアプリケーション プログラミング インターフェイス (API) 呼び出しのプロファイリングに使用するコマンドのシーケンスです (「 Direct3D API 呼び出しの正確なプロファイリング (Direct3D 9)」を参照)。 このシーケンスでは、マーカーを使用して、コマンド バッファー内の作業量を制御します。
アプリケーションでは、コマンド バッファーのフラッシュに関連する大きなコストに特別な注意を払う必要があることに注意してください。これは、オペレーティング システムがカーネル モードに切り替わるため、パフォーマンスが大幅に低下するためです。 アプリケーションでは、クエリの完了を待って CPU サイクルを無駄にすることにも注意する必要があります。
クエリは、パフォーマンスを向上させるためにレンダリング中に使用される最適化です。 そのため、クエリの完了を待つ時間を費やすことは有益ではありません。 クエリが発行され、アプリケーションがクエリをチェックするまでに結果の準備ができていない場合、最適化の試行は成功せず、レンダリングは通常どおりに続行されます。
この従来の例は、オクルージョン カリング中です。 上記の while ループの代わりに、クエリを使用するアプリケーションはオクルージョン カリングを実装して、結果が必要な時点でクエリが完了したかどうかを確認チェックできます。 クエリが完了していない場合は、テスト対象のオブジェクトがオクルージョンされていない (つまり表示されている) かのように (最悪のシナリオとして) 続行し、レンダリングします。 コードは次のようになります。
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();
}