いまさらDirect3D11入門

初めてグラフィックスAPIを触る人に向けて

クエリ

今パートではクエリについて見ていきます。

クエリとはGPUが行った情報を取得する際に使います。 取得できる情報はストリームアウトプットがいくつプリミティブを作成したかとか、描画したプリミティブがレンダーターゲットに書き込まれたのかなどがあります。

概要

それではクエリの使い方を見ていきましょう。 対応するプロジェクトはPart14_Queryになります。

ID3D11Query

クエリにはいくつかインターフェイスに分かれています。 まず、ID3D11Queryについて見ていきます。

ID3D11Queryはストリームアウトプットの出力数やグラフィックスパイプラインの統計情報について取得できます。

ドキュメント:ID3D11Query (日本語) (英語)

作成

// Scene::onInit関数の一部
D3D11_QUERY_DESC queryDesc;
queryDesc.Query = D3D11_QUERY_SO_STATISTICS_STREAM0;
queryDesc.MiscFlags = 0;
auto hr = this->mpDevice->CreateQuery(&queryDesc, this->mpQuery.GetAddressOf());
if (FAILED(hr)) {
  throw std::runtime_error("ID3D11Queryの作成に失敗。");
}

ID3D11Device::CreateQueryID3D11Queryを作成します。

ドキュメント:
ID3D11Device::CreateQuery (日本語) (英語)
D3D11_QUERY_DESC (日本語) (英語)

D3D11_QUERY_DESC

  • Query

    D3D11_QUERYで取得する情報指定します。
    ドキュメント:D3D11_QUERY (日本語) (英語)

  • MiscFlags

    その他のフラグになります。

情報の収集と取り出し

ID3D11Queryを作成した後は実際に情報を収集してからそれを取り出す必要があります。

void Scene::renderQuery()
{
  //情報の収集を行う
  this->mpImmediateContext->Begin(this->mpQuery.Get());
  {//三角形を生成する
    outputTriangles(4);
  }
  this->mpImmediateContext->End(this->mpQuery.Get());
  //this->mpImmediateContext->Flush();
  //生成した三角形の個数を取得する
  D3D11_QUERY_DATA_SO_STATISTICS soStatistics = {};
  UINT flag = 0;
  HRESULT hr;
  do {
    hr = this->mpImmediateContext->GetData(this->mpQuery.Get(), &soStatistics, sizeof(soStatistics), flag);
  } while (hr != S_OK);
  //生成した三角形を描画する
  renderTriangles(static_cast<UINT>(soStatistics.NumPrimitivesWritten));
}

ID3D11DeviceContext::BeginID3D11DeviceContext::Endの間で実行されたコマンドの情報を収集します。

ドキュメント:
ID3D11DeviceContext::Begin (日本語) (英語)
ID3D11DeviceContext::End (日本語) (英語)

収集を終えた後はID3D11DeviceContext::GetDataを使用して情報を取得します。 コマンドがいつ実行されるかはGPU依存のため、ID3D11DeviceContext::GetDataを呼び出したからといって必ず情報を取得できるわけではありません。 その場合は戻り値としてS_FLASEを返します。取得できた場合はS_OKを返しますので確認は忘れないで下さい。

ドキュメント:ID3D11DeviceContext::GetData (日本語) (英語)

ID3D11DeviceContext::GetData

  • 第1引数:pAsync

    収集した情報を取得するインタフェースを渡します

  • 第2引数:pData

    取得した情報を格納する変数へのポインタを渡します。 ここに渡す変数の型はD3D11_QUERYごとに用意されていますのでドキュメントを参考にしてください。

  • 第3引数:DataSize

    第2引数に渡した変数のサイズを指定します。

  • 第4引数:GetDataFlags

    0またはD3D11_ASYNC_GETDATA_FLAGで列挙されているフラグを組み合わせたものを渡してください。

ID3D11Predicate

次にID3D11Predicateについて見ていきます。

ID3D11Predicateはストリームアウトプットの上限を超えたのを検出することと深度ステンシルテストに成功したかどうか調べることが出来ます。

作成

作成にはID3D11Device::CreatePredicateを使います。 ID3D11Queryと同じくD3D11_QUERY_DESCを使って収集する対象を決めます。 この時、MiscFlagsD3D11_QUERY_MISC_PREDICATEHINTを指定する必要があります。

ドキュメント:ID3D11Device::CreatePredicate (日本語) (英語)

// Scene::onInit関数の一部
D3D11_QUERY_DESC predicateDesc;
predicateDesc.Query = D3D11_QUERY_OCCLUSION_PREDICATE;
predicateDesc.MiscFlags = D3D11_QUERY_MISC_PREDICATEHINT;
hr = this->mpDevice->CreatePredicate(&predicateDesc, this->mpPredicate.GetAddressOf());
if (FAILED(hr)) {
  throw std::runtime_error("ID3D11Predicateの作成に失敗。");
}

情報の収集と取り出し

情報の収集にはID3D11Queryと同じくID3D11DeviceContext::BeginID3D11DeviceContext::Endを使用します。 収集した結果はID3D11DeviceContext::GetDataで取り出せますが、ID3D11Predicateの場合はID3D11DeviceContext::SetPredicationで任意のドローコールを無効化することが出来ます。 一見無駄な機能に見えますが、オクルージョンカリングと呼ばれる画面上に描画されないプリミティブの描画を省く手法に活用することが出来ます。

ドキュメント:ID3D11DeviceContext::SetPredication (日本語) (英語)

// Scene::renderPredicateの一部
//わざと深度テストを失敗させる
this->mpImmediateContext->Begin(this->mpPredicate.Get());
{
  this->mpImmediateContext->OMSetDepthStencilState(this->mpDSNotDepthTestPass.Get(), 0);
  this->renderTriangles(-1);
}
this->mpImmediateContext->End(this->mpPredicate.Get());
this->mpImmediateContext->Flush();
//Predicateの効果を確認する
if (this->mOnPredicate) {
  //this->mpPredicateがfalseの場合、ドローコールを無視する
  this->mpImmediateContext->SetPredication(this->mpPredicate.Get(), false);
}
this->mpImmediateContext->OMSetDepthStencilState(nullptr, 0);
this->renderTriangles(-1);
this->mpImmediateContext->SetPredication(nullptr, true);

ID3D11Counter

最後にID3D11Counterについて見ていきたいのですが、使い方がわからなかったので省きます。 ID3D11CounterはGPUのタイムスタンプを取得でき、処理時間の計測に適しているそうです。 が、こちらで試したところ同じ値しか取得出来ませんでした。 サンプルではコードが一応有りますので興味がある方は御覧ください。

時間計測には各GPUベンダーが用意しているAPIやツールを利用することが出来ます。 またVisual Studio 2015上でも計測できますので、それらを活用するのが手っ取り早い方法になるかと思います。

GPU使用率 NVIDIA Nsight AMD GPU PerfStudio

まとめ

今回のパートではクエリについて見てきました。 必ず使うものとは言えませんが、知っていれば便利な機能です。

<前 トップ 次>