web-dev-qa-db-ja.com

なぜドローコールは高価ですか?

テクスチャ、頂点、シェーダーデータが既にグラフィックスカードにあると仮定すると、多くのデータをカードに送信する必要はありません。データを識別するための数バイト、おそらく4x4マトリックス、およびその他のさまざまなパラメーターがあります。

オーバーヘッドはどこから来るのでしょうか?操作には、GPUとの何らかのハンドシェイクが必要ですか?

cPUで計算された小さなモデルの束を含む単一のメッシュを送信するのは、多くの場合、頂点IDと変換行列を送信するよりも速いのはなぜですか? (2番目のオプションは、モデルが4x4マトリックスよりも小さい場合を除き、送信されるデータが少ないはずです)

50
notallama

まず、「描画呼び出し」では、特定の頂点セットを特定の状態(シェーダー、ブレンド状態など)の三角形としてレンダリングするようGPUに指示するコマンドを意味すると想定しています。

描画呼び出しは必ずしも高価ではありません。 Direct3Dの古いバージョンでは、多くの呼び出しでコンテキストスイッチが必要でしたが、これは高価でしたが、新しいバージョンではそうではありません。

描画呼び出しを少なくする主な理由は、グラフィックスハードウェアが、三角形を送信するよりもはるかに高速に変換およびレンダリングできることです。それぞれに三角形をほとんど送信しない場合コールすると、CPUに完全にバインドされ、GPUはほとんどアイドル状態になります。 CPUはGPUに十分な速度でフィードできません。

2つの三角形で1回の描画呼び出しを行うのは安価ですが、呼び出しごとに送信するデータが少なすぎると、GPUにできるだけ多くのジオメトリを送信するのに十分なCPU時間がありません。

描画呼び出しを行うにはいくつかの実際のコストがあり、状態のセット(使用する頂点のセット、使用するシェーダーなど)を設定する必要があり、状態の変更にはハードウェア側のコストがかかります(束の更新(レジスタの)およびドライバー側(状態を設定する呼び出しの検証と変換)。

ただし、描画呼び出しの主なコストは、各呼び出しが送信するデータが少なすぎる場合にのみ適用されます。これにより、CPUに縛られ、ハードウェアを完全に活用します。

ジョシュが言ったように、描画呼び出しによってコマンドバッファーがフラッシュされることもありますが、私の経験では、ジオメトリを送信するときではなく、通常SwapBuffersを呼び出すときに起こります。通常、ビデオドライバーは、GPUから可能な限り多くの並列処理を実行するために、逃げることができる範囲(場合によっては複数のフレーム!)でバッファリングを試みます。

NVidiaのプレゼンテーション Batch Batch Batch! をお読みください。かなり古いですが、まさにこのトピックを扱っています。

57
Joakim Hårsman

Direct3DなどのグラフィックスAPIは、APIレベルの呼び出しをデバイスに依存しないコマンドに変換し、バッファーに入れます。実際の作業を実行するためにそのバッファをフラッシュすると、実際の作業が現在実行されていることを意味し、チップ上でユーザーモードからカーネルモードへの切り替えが発生する可能性があります安いです。

バッファーがフラッシュされるまで、GPUはCPUがブロッキング要求(CPUへのデータのマッピングなど)を行わない限り、CPUと並行していくつかの準備作業を行うことができます。しかし、GPUは実際に描画する必要があるまで、すべてを準備することはできません。一部の頂点またはテクスチャデータがカード上にあるからといって、まだ適切に配置されているわけではなく、頂点レイアウトが設定されるかシェーダーがバインドされるまで配置できない場合があります。実際の作業の大部分は、コマンドのフラッシュおよび描画呼び出し中に発生します。

DirectX SDKには D3Dパフォーマンスの正確なプロファイリングに関するセクション があります。これは、質問に直接関連するものではありませんが、何が高価ではないか(場合によっては)理由についてのヒントを提供できます。

より関連性の高いものは、 このブログ投稿 (およびフォローアップ投稿 here および here )です。 GPUのレベルの運用プロセス。

しかし、本質的に(あなたの質問に直接答えるために)、呼び出しが高価な理由は、転送する多くのデータがあるためではありません、というよりむしろ、大量の作業beyondがあり、コマンドバッファがフラッシュされるまで延期されるデータをバス経由で送信するだけです。

12
Josh

簡単な答え:ドライバは、drawを呼び出すまで、実際の作業の一部またはすべてをバッファします。これは、どの程度の状態が変化したかに応じて、描画呼び出しに費やされる比較的予測可能な時間として表示されます。

これにはいくつかの理由があります。

  • 不要な作業を回避するために:描画する前に(不必要に)同じ状態を複数回設定すると、これが発生するたびに高価な作業を行うことを回避できます。これは、実稼働のゲームエンジンなど、大規模なコードベースで実際にかなり頻繁に発生します。
  • 不完全な情報で即座に​​処理するのではなく、内部的に相互依存状態であるものを調整できるようにする

別の回答:

  • ドライバーがレンダリングコマンドを格納するために使用するバッファーがいっぱいになり、アプリは事実上、GPUが以前の作業の一部を処理するのを待っています。これは通常、フレーム内のランダム描画呼び出しで非常に大きな時間ブロックの塊として表示されます。
  • ドライバーがバッファリングできるフレーム数に達し、アプリはGPUでそれらの1つを処理するのを待機しています。これは通常、フレーム内の最初の描画呼び出しで、または前のフレームの最後の存在で、大きな時間ブロックの塊として表示されます。
2
Zoner