2012-03-17 17 views
1

フラグメントシェーダは2つのアトミックカウンタを使用します。それは最初のものをインクリメントすることもインクリメントすることもできないこともあります。ただし、カウンターを変更する前に、現在の値が常に読み取られます。カウンターが後で変更されると、以前に読み取られた値がカスタムロジックに使用されます。すべてこれは(ほとんど可能性のあるunrollable)ループで発生します。フラグメントシェーダのGLSLアトミックカウンタ(および分岐)

Envisionのおおよそこのような流れ:いくつかの小さなunrollableループで

  • 、(コンパイル時に解決可能なのconst)0-20 FOR言う...
  • はAC1とAC2
  • のカウンタ値を取得します
  • チェックいくつかの値:
  • 場合X:インデックスAC1、
  • 他増分AC1でuimage1D_Aに設定テクセル:インデックスでuimage1D_Bに設定テクセル(imgwidth-AC2-1)、インクリメントAC2

質問:シェイダーは現在のカウンタ値を照会します。常に最新の値を取得していますか?現在の世代のGPUと将来のGPUとドライバに関してのみ、フラグメントシェーダの大規模な並列処理をここで失うことはありますか?分岐として

(もしx) - Iは、(uniformuintに別の(readonly restrict uniformuimage1Dにおけるテクセルを比較します。だから1つのオペランドは間違いなく均一なスカラーですが、もう1つはimageLoad().xですが、画像は一様ですが、この種の分岐はまだ完全に並列化されていますか?両方のブランチがまったく同じ2つの命令であることが分かります。 「完全に最適化された」GLSLコンパイラを仮定すると、この種の分岐はおそらくストールを招くでしょうか?

答えて

4

アトミックカウンタはアトミックです。しかし、各原子操作は、その操作のためにのみ原子です。だから、

あなたはすべてのシェーダがカウンターから一意の値を取得することを確実にしたい場合は、その後、すべてのシェーダはのみatomicCounterIncrementとそのカウンターにアクセスする必要があります(またはDecrementが、それらはすべて同じものを使用しなければなりません)。

あなたが示唆しているものを行うための正しい方法は次のとおりです。

  1. atomicCounterIncrement(AC1)、返された値を格納します。x場合
    1. いくつかの値を確認してください。
    2. 何かをuimage1D_Aに設定するテクセルとして、格納された値を使用します。他
    1. atomicCounterIncrement(AC2)、返された値を格納します。
    2. 格納された値を使用して、何かをuimage1D_Bに設定するテクセル(imgwidth - val - 1)を計算します。

あなたの「フェッチ以降の増分」戦略が起こるのを待って競合状態です。が壊れているため、完全に並列化されているかどうかは関係ありません。あなたはそれが速くなるかどうか疑問に思う前に働く必要があります。

GPUの問題に取り組む前に、CPUのアトミックとスレッドに詳しいことを強くお勧めします。これは、アトミックを扱う際の初心者の間違いです。 GLSLアトミックとイメージのロード/ストアを正常に使用するには、スレッド・エキスパート(または少なくとも中間レベル)が必要です。

+0

明確化してくれてありがとうと警告を:) – metaleap

1
ニコルボーラが示唆したように、あなたがこれまでに別のカーネルによって読み取られることはありませんアトミックカウンタから読み出した値を確認したい場合、あなたは返された値は、他のカーネルが意志をアトミックインクリメントを実行して使用する必要があります

インクリメントせずに値をチェックするatomicCounter(AC1)を実行しない限りはありません。あなたが原子的に値をインクリメントして元の値に戻す瞬間に、同じことをする他のすべての人がインクリメントされた値だけを取得するようにします。

あなたはA-Bufferをやっているようですが、なぜ2番目のカウンターが必要なのか不思議です。私はuimage1D_Aがuimage1D_Bに格納されているフラグメントリストへのポインタの画面サイズマップであると仮定します。あなたはuimage1D_Bの新しい未使用のメモリ部へのポインタを生成するために、AC2を使用しますが、あなたのAC1はあなたが徐々にので、私は完全に間違っている可能性がありますuimage1D_Aをアクセスしてる示唆:)