2013-07-25 10 views
5

Appleはシェイダーのベストプラクティスで、可能であればavoid branchingと言い、特にシェーダー内で計算された値を分岐します。そこで、if文を組み込みのclamp()関数に置き換えました。私の質問は、clamp()min()、およびmax()がより効率的であるか、単にifブロックに単純に拡張される便利な(つまりマクロ)関数なのでしょうか?条件式のOpenGL ESのベストプラクティス

私は答えが実装に依存する可能性があります実現します。いずれの場合でも、関数は明白に明確であり、意図を明白にします。コンパイラで何かできます。

+2

私はあなたの最後の文章はすでにそれに非常によく答えると思います。より合理化された見解とは別に、高速のハードウェア命令によって実装される可能性は、少なくとも「普通」よりも高くなります。しかし、この一般的な提案(これでも十分であるはずです)とは別に、実際には特殊なハードウェア命令や条件付き代入を使用し、 'if'をラップする関数ではありません。 –

答えて

9

歴史的に言えば、GPUは、任意の条件分岐をサポートしていたよりもはるかに長い間、MINMAXなどのフラグメント単位の命令をサポートしています。デスクトップOpenGLにおけるこの例の1つは、GL_ARB_fragment_program拡張(現在はGLSLに置き換えられている)です。これは、分岐をサポートしないことを明示していますが、MINおよびMAXの命令とその他の条件付き命令を提供します。

min()max()clamp()がどのようにシェーダに含まれているかを考えれば、すべてのGPUにこれらの操作用の専用ハードウェアがまだあると確信しています。実装はコードを最適化することができますが、現実世界では独自のものを使うのではなく、GLSLの組み込み関数を使用する必要があるため、この仕様では保証されていません。

唯一の例外は、大量の追加フラグメント処理を避けるために条件を使用していた場合です。ある時点では、ブランチのコストはブランチ内のすべてのコードを実行するコストよりも低くなりますが、ここでのバランスはハードウェアに非常に依存するため、実際にアプリケーションで役立つかどうかをベンチマークする必要がありますターゲットハードウェアここで私が意味するもののようなものだ:

だけで0-1に NDotLをクランプ
void main() { 
    vec3 N = ...; 
    vec3 L = ...; 
    float NDotL = dot(N, L); 
    if (NDotL > 0.0) 
    { 
     // Lots of very intensive code for an awesome shadowing algorithm that we 
     // want to avoid wasting time on if the fragment is facing away from the light 
    } 
} 

NDotLならば、常にのみNDotLして、最終的な影の用語によって乗算するすべてのフラグメントに影コードを処理することは無駄な努力がたくさんありますもともとは< = 0でした。理論的には、このオーバーヘッドをブランチで回避できます。このようなことが常にパフォーマンス上の勝利をもたらすわけではない理由は、ハードウェアがどのようにシェーダの分岐を実装するかに大きく依存しているからです。

+0

優れたレビュー。私は 'MIN'と' MAX'が一般的な 'if'ブロックよりも前になっていたはずです。ハードウェアのサポートが正しいと確信しています。どうもありがとう! –

+0

私は何か言及する必要があると思う、いくつかのGPUは標準を尊重せず、ブランチをまったく実装しないということです。私が遭遇した唯一のものはサムスンのギャラクシータブシリーズです。彼らはメッセージや何もせずに沈黙するだけです。 –