コンピューティングシェーダーは、画像フィルタリングのピクセルシェーダーよりも効率的ですか?


37

ブラー、SSAO、ブルームなどの画像フィルタリング操作は、通常、ピクセルシェーダーと「収集」操作を使用して行われます。各ピクセルシェーダーの呼び出しは、隣接するピクセル値にアクセスするために多数のテクスチャフェッチを発行し、単一のピクセルの価値を計算します結果。このアプローチには、多くの冗長なフェッチが行われるという理論上の非効率があります。近くのシェーダー呼び出しは、同じテクセルの多くを再フェッチします。

別の方法は、計算シェーダーを使用することです。これらには、シェーダー呼び出しのグループ全体で少量のメモリを共有できるという潜在的な利点があります。たとえば、各呼び出しで1つのテクセルをフェッチして共有メモリに保存し、そこから結果を計算できます。これは、高速かもしれませんし、そうでないかもしれません。

質問は、どのような状況下で(実際に)コンピューティングシェーダーメソッドピクセルシェーダーメソッドよりも実際に高速であるかということです。カーネルのサイズ、どんな種類のフィルタリング操作などに依存しますか?明らかに、答えはGPUのモデルによって異なりますが、一般的な傾向があるかどうか聞いてみたいと思います。


計算シェーダーが適切に行われていれば、答えは「常に」だと思います。これを達成するのは簡単ではありません。コンピューティングシェーダーは、画像処理アルゴリズムの概念上、ピクセルシェーダーよりも優れています。ただし、ピクセルシェーダーでは、パフォーマンスの低いフィルターを書き込む余裕が少なくなります。
バーニー

@bernieコンピューティングシェーダーを「適切に」行うために必要なものを明確にできますか?たぶん答えを書く?主題に関するより多くの視点を得るために常に良い。:)
ネイサンリード

2
今、あなたが私にさせたものを見てください!:)
バーニー

スレッド間で作業を共有することに加えて、非同期計算を使用する機能は、計算シェーダーを使用する大きな理由の1つです。
JarkkoL

回答:


23

画像処理用の計算シェーダーのアーキテクチャ上の利点は、ROPをスキップすることですステップことです。ピクセルシェーダーからの書き込みは、使用しなくても通常のブレンドハードウェアをすべて通過する可能性が非常に高いです。一般的に、コンピューティングシェーダーは、メモリへの別の(そしてより直接的な)パスを通過するため、そうでない場合のボトルネックを回避できます。これに起因するかなり大きなパフォーマンスの勝利を聞いたことがあります。

コンピューティングシェーダーのアーキテクチャ上の欠点は、GPUがどの作業項目がどのピクセルにリタイアするかを認識しなくなることです。ピクセルシェーディングパイプラインを使用している場合、GPUは、メモリ内で連続しているレンダーターゲットの領域に書き込むワープ/ウェーブフロントに作業をパックする機会があります(Zオーダータイルまたはパフォーマンスのためにそのようなものがあります)理由)。計算パイプラインを使用している場合、GPUは最適なバッチで作業を開始できなくなり、より多くの帯域幅の使用につながる可能性があります。

ただし、特定の操作に、関連する作業を同じスレッドグループにパックすることで活用できる下位構造があることがわかっている場合は、その変更されたワープ/ウェーブフロントパッキングを再び利点に変えることができます。あなたが言ったように、理論的には、レーンごとに1つの値をサンプリングし、結果を他のレーンのグループ共有メモリに入れてサンプリングせずにアクセスすることにより、サンプリングハードウェアにブレークを与えることができます。これが勝利であるかどうかは、グループ共有メモリがどれだけ高価であるかによって決まります。最低レベルのテクスチャキャッシュよりも安い場合、これは勝利かもしれませんが、それを保証するものではありません。GPUは、非常にローカルなテクスチャフェッチをすでに処理しています(必要に応じて)。

結果を共有したい操作の中間段階がある場合、グループ共有メモリを使用する方が合理的かもしれません(実際に中間結果をメモリに書き出さずにテクスチャサンプリングハードウェアにフォールバックできないため)。残念ながら、他のスレッドグループからの結果に依存することもできないため、2番目のステージでは、同じタイルで使用可能なものだけに制限する必要があります。ここでの標準的な例は、自動露出のために画面の平均輝度を計算することだと思います。テクスチャのアップサンプリングを他の操作と組み合わせることも考えられます(ダウンサンプリングやブラーとは異なり、アップサンプリングは特定のタイルの外側の値に依存しないため)。


ブレンディングが無効になっている場合、ROPがパフォーマンスのオーバーヘッドを追加することを真剣に疑います。
グローバーマンハイム

@GroverManheimアーキテクチャに依存します!ブレンディングが無効になっている場合でも、出力マージ/ ROPステップは順序保証も処理する必要があります。フルスクリーンの三角形の場合、実際の注文の危険性はありませんが、ハードウェアはそれを認識していない可能性があります。ハードウェアには特別な高速パスが存在する場合がありますが、それらの資格があることは確かに知っています…
ジョンカルスビーク

10

ジョンはすでに素晴らしい答えを書いているので、この答えを彼の延長と考えてください。

現在、さまざまなアルゴリズムの計算シェーダーで多くの作業を行っています。一般的に、コンピューティングシェーダーは、同等のピクセルシェーダーよりもはるかに高速であるか、フィードバックベースの代替を変換できることがわかりました。

計算シェーダーがどのように機能するかについて頭を包むと、多くの場合、それらはより意味があります。ピクセルシェーダーを使用して画像をフィルター処理するには、フレームバッファーの設定、頂点の送信、複数のシェーダーステージの使用などが必要です。画像のフィルター処理にこれが必要なのはなぜですか?私の意見では、画像処理のためにフルスクリーンクワッドをレンダリングすることに慣れていることが、それらを引き続き使用する唯一の「有効な」理由です。コンピューティンググラフィックスの分野の新参者は、テクスチャへのレンダリングよりもコンピューティングシェーダーが画像処理にはるかに自然に適合すると確信しています。

あなたの質問は特に画像フィルタリングに関するものなので、他のトピックについてはあまり詳しく説明しません。一部のテストでは、変換フィードバックを設定するか、フレームバッファオブジェクトを切り替えてテクスチャにレンダリングするだけで、パフォーマンスコストが約0.2ms発生する可能性があります。これはレンダリングを除外することに注意してください!あるケースでは、計算シェーダーに移植されたまったく同じアルゴリズムを維持し、顕著なパフォーマンスの向上が見られました。

計算シェーダーを使用する場合、GPUのより多くのシリコンを使用して実際の作業を行うことができます。ピクセルシェーダールートを使用する場合は、これらすべての追加手順が必要です。

  • 頂点アセンブリ(頂点属性、頂点除数、型変換の読み取り、vec4への展開など)
  • 頂点シェーダーは、最小限であってもスケジュールする必要があります
  • ラスタライザは、頂点出力をシェーディングおよび補間するピクセルのリストを計算する必要があります(おそらく、画像処理のテクスチャ座標のみ)
  • すべての異なる状態(深度テスト、アルファテスト、はさみ、ブレンド)を設定および管理する必要があります

前述のパフォーマンス上の利点はすべて、スマートドライバーによって無効にされる可能性があると言えます。あなたは正しいでしょう。このようなドライバーは、深度テストなどを行わずにフルスクリーンクワッドをレンダリングしていることを識別し、ピクセルシェーダーをサポートするために行われた無駄な作業をすべてスキップする「高速パス」を構成できます。一部のドライバーが特定のGPU向けのAAAゲームのポストプロセッシングパスを高速化するためにこれを実行しても驚かないでしょう。もちろん、AAAゲームに取り組んでいない場合は、そのような処理を忘れることができます。

ただし、ドライバーができないことは、計算シェーダーパイプラインによって提供されるより良い並列処理の機会を見つけることです。ガウスフィルターの典型的な例を見てみましょう。計算シェーダーを使用すると、次のようなことができます(フィルターを分離するかどうか)。

  1. ワークグループごとに、ソースイメージのサンプリングをワークグループサイズ全体に分割し、結果をグループ共有メモリに保存します。
  2. 共有メモリに保存されたサンプル結果を使用して、フィルター出力を計算します。
  3. 出力テクスチャに書き込む

ここで重要なのはステップ1です。ピクセルシェーダーバージョンでは、ソースイメージはピクセルごとに複数回サンプリングされます。計算シェーダーバージョンでは、各ソーステクセルはワークグループ内で1回だけ読み取られます。テクスチャ読み取りは通常、タイルベースのキャッシュを使用しますが、このキャッシュは共有メモリよりもはるかに低速です。

ガウスフィルターは、より単純な例の1つです。他のフィルタリングアルゴリズムは、共有メモリを使用してワークグループ内で中間結果を共有する他の機会を提供します。

ただし、キャッチがあります。計算シェーダーでは、出力を同期するために明示的なメモリバリアが必要です。また、誤ったメモリアクセスから保護するための保護手段も少なくなります。並列プログラミングの知識が豊富なプログラマーの場合、計算シェーダーははるかに高い柔軟性を提供します。ただし、この柔軟性は、通常のC ++コードのように計算シェーダーを扱い、遅いコードまたは誤ったコードを記述する方が簡単であることを意味します。

参照資料


あなたが説明する改善されたサンプリング並列性は興味深いです-ピクセルごとに複数のサンプルの多くのインスタンスを持つ計算シェーダーで既に実装されている流体シミュレーションがあります。しかし、私は少し待っています-隣接するピクセルが別のワークグループに分類される場合、どのようにアクセスするのですか?たとえば、numthreads(16,16,1)のdispatch(2,2,1)に広がる64x64シミュレーションドメインがある場合、id.xy == [15,15]のピクセルはどのようにその隣接ピクセルを取得しますか?
トスロック

その場合、2つの主な選択肢があります。1)グループサイズを64以上に増やし、64x64ピクセルの結果のみを書き込みます。2)64x64ワークグループで最初のサンプル64 + nX64 + nを何らかの方法で分割し、計算にその大きな「入力」グリッドを使用します。最良の解決策はもちろんあなたの特定の条件に依存します。コメントはこれにはあまり適していないので、さらなる情報を得るために別の質問を書くことをお勧めします。
バーニー

3

私はこのブログで偶然見つけました: AMDのための計算シェーダー最適化

コンピューティングシェーダー(コンピューティングシェーダーのみに固有)で実行できるトリックを考えると、コンピューティングシェーダーの並列削減がピクセルシェーダーよりも高速かどうかに興味がありました。著者のWolf Engelにメールを送り、ピクセルシェーダーを試したかどうかを尋ねました。彼は、コンピューティングシェーダーバージョンがピクセルシェーダーバージョンよりもかなり高速だったブログ投稿を書いたときに、「はい」と答えました。彼はまた、今日の違いはさらに大きいと付け加えました。そのため、コンピュートシェーダーを使用すると非常に有利な場合があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.