クエリ (Direct3D 9)
リソースの状態を照会するように設計されたクエリには、いくつかの種類があります。 特定のリソースの状態には、グラフィックス処理装置 (GPU) の状態、ドライバーの状態、またはランタイムの状態が含まれます。 さまざまなクエリの種類の違いを理解するには、クエリの状態を理解する必要があります。 次の状態遷移図では、各クエリの状態について説明します。
クエリ状態 間の遷移を示す図
図は 3 つの状態を示しています。それぞれ円で定義されています。 実線はそれぞれ、状態遷移を引き起こすアプリケーションドリブン イベントです。 破線は、発行された状態からシグナル状態にクエリを切り替えるリソースドリブン イベントです。 これらの状態はそれぞれ異なる目的を持っています。
- シグナル状態はアイドル状態のようなものです。 クエリ オブジェクトが生成され、アプリケーションがクエリを発行するのを待機しています。 クエリが完了し、シグナル状態に戻ると、クエリに対する回答を取得できます。
- ビルド状態は、クエリのステージング領域に似ています。 ビルド状態から、(D3DISSUE_BEGINを呼び出して) クエリが発行されましたが、発行された状態にまだ遷移していません。 アプリケーションが (D3DISSUE_ENDを呼び出して) クエリの終了を発行すると、クエリは発行された状態に遷移します。
- 発行された状態は、クエリ対象のリソースがクエリを制御していることを意味します。 リソースの作業が完了すると、リソースはステート マシンをシグナル状態に移行します。 発行された状態の間、アプリケーションはポーリングして、シグナル状態への遷移を検出する必要があります。 シグナル状態への遷移が発生すると、GetData は、クエリ結果を (引数を介して) アプリケーションに返します。
次の表に、使用可能なクエリの種類を示します。
クエリの種類 | 問題イベント | GetData バッファー | 実行中 | クエリの暗黙的な開始 |
---|---|---|---|---|
BANDWIDTHTIMINGS | D3DISSUE_BEGIN、D3DISSUE_END | D3DDEVINFO_D3D9BANDWIDTHTIMINGS | Retail/Debug | N/A |
CACHEUTILIZATION | D3DISSUE_BEGIN、D3DISSUE_END | D3DDEVINFO_D3D9CACHEUTILIZATION | Retail/Debug | N/A |
出来事 | D3DISSUE_END | BOOL | Retail/Debug | CreateDeviceの |
INTERFACETIMINGS | D3DISSUE_BEGIN、D3DISSUE_END | D3DDEVINFO_D3D9INTERFACETIMINGS | Retail/Debug | N/A |
閉塞 | D3DISSUE_BEGIN、D3DISSUE_END | DWORD | Retail/Debug | N/A |
PIPELINETIMINGS | D3DISSUE_BEGIN、D3DISSUE_END | D3DDEVINFO_D3D9PIPELINETIMINGS | Retail/Debug | N/A |
RESOURCEMANAGER | D3DISSUE_END | D3DDEVINFO_ResourceManager | デバッグのみ | Present |
タイムスタンプ | D3DISSUE_END | UINT64 | Retail/Debug | N/A |
TIMESTAMPDISJOINT | D3DISSUE_BEGIN、D3DISSUE_END | BOOL | Retail/Debug | N/A |
TIMESTAMPFREQ | D3DISSUE_END | UINT64 | Retail/Debug | N/A |
VCACHE | D3DISSUE_END | D3DDEVINFO_VCACHE | Retail/Debug | CreateDeviceの |
VERTEXSTATS | D3DISSUE_END | D3DDEVINFO_D3DVERTEXSTATS | デバッグのみ | Present |
VERTEXTIMINGS | D3DISSUE_BEGIN、D3DISSUE_END | D3DDEVINFO_D3D9STAGETIMINGS | Retail/Debug | N/A |
クエリの中には開始イベントと終了イベントが必要なクエリもあれば、終了イベントのみが必要なクエリもあります。 終了イベントのみを必要とするクエリは、別の暗黙的なイベントが発生したときに開始されます (表に示されています)。 すべてのクエリは、常に 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 | 既存のクエリを破棄した後に発行された状態。 |
クエリの状態を確認し、クエリに対する回答を取得する
- リターン コードのクエリ状態を返します。
- pDataのクエリに対する回答を返します。
3 つのクエリ状態のそれぞれから、GetDataリターン コードを次に示します。
クエリの状態 | GetData リターン コード |
---|---|
シグナル | S_OK |
建物 | エラー コード |
発行 | S_FALSE |
たとえば、クエリが発行された状態にあり、クエリに対する回答が使用できない場合、GetDataはS_FALSEを返します。 リソースが作業を完了し、アプリケーションがクエリ終了を発行すると、リソースはクエリをシグナル状態に移行します。 シグナル状態から、GetData はS_OKを返します。つまり、クエリに対する回答も pData 返されます。 たとえば、レンダー シーケンスで描画されたピクセル数 (またはマルチサンプリングが有効な場合のサンプル) を返すイベントのシーケンスを次に示します。
- クエリを作成します。
- 開始イベントを発行します。
- 何かを描画します。
- 終了イベントを発行します。
対応するコードシーケンスを次に示します。
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以外の) エラーが発生したら、クエリを再作成する必要があります。この場合、シグナル状態からクエリ シーケンスが再開されます。
より多くの up-to-date 情報を提供する D3DGETDATA_FLUSHを指定する代わりに、クエリが発行された状態にある場合は、より軽量なチェックであるゼロを指定できます。 0 を指定すると、GetDataコマンド バッファーがフラッシュされません。 このため、無限ループを回避するには注意が必要です (詳細については、GetData を参照してください)。 ランタイムはコマンド バッファーで作業をキューに入れます。D3DGETDATA_FLUSH は、コマンド バッファーをドライバーにフラッシュするためのメカニズムです (そのため GPU です。Direct3D API 呼び出しの正確なプロファイリング (Direct3D 9) 参照)。 コマンド バッファーのフラッシュ中に、クエリがシグナル状態に遷移する可能性があります。
例: イベント クエリ
イベント クエリでは、開始イベントはサポートされていません。
- クエリを作成します。
- 終了イベントを発行します。
- 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 サイクルを無駄にすることも認識する必要があります。
クエリは、パフォーマンスを向上させるためにレンダリング中に使用される最適化です。 そのため、クエリの完了を待つ時間を費やすことは有益ではありません。 クエリが発行され、アプリケーションがクエリをチェックするまでに結果の準備ができていない場合、最適化の試行は成功せず、レンダリングは通常どおり続行されます。
この従来の例は、オクルージョン カリング中です。 上記のループ の代わりに、クエリを使用するアプリケーションはオクルージョン カリングを実装して、結果が必要な時点までにクエリが完了したかどうかを確認できます。 クエリが完了していない場合は、テスト対象のオブジェクトが隠されていない (つまり表示されている) かのように (最悪の場合のシナリオとして) 続行し、レンダリングします。 コードは次のようになります。
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();
}
関連トピック