1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Unity】【Shader】stepの分岐と三項演算子のコンパイル結果を見てみた

Posted at

気になった投稿

ちょっと、 mix () +step ()で条件付き移動を「最適化」しないでください。そのアドバイスはやめてください。理由:

ifを書くのを避けるテクニックとして、lerpとstepを使用する方法があります。
※iq氏の投稿はGLSLのためmixとなっている

なぜか

stepを使用すると
float step( float x, float y )
{
return x < y ? 1.0 : 0.0;
}
このように三項演算子と0.0と1.0が生成される?ようです。見た目では三項演算子を使用しないコードを書いたにも関わらず、結局内部で三項演算子が出現するため無駄のようです。

試してみる

float4 frag(Varyings i) : SV_Target
{
    float4 col = float4(0.0, 0.0, 0.0, 1.0);
    
    float c = lerp(0.0, 1.0, step(0.5, i.uv.x));
    //float c = i.uv.x >= 0.5 ? 1.0 : 0.0;

    col.rgb = c;
    col.a = 1.0;

    return col;
}

こちらのコードを使用します。
uvのxによって色を変更します。

image.png

結果はこのような画像です。
今回重要なのは出力結果の絵ではなく、内部です。そのためコンパイル結果を見ます。
Shaderを選択してCompileボタンを押せば見れます。
image.png

まずはlerp(0.0, 1.0, step(0.5, i.uv.x))を行った結果です。

0: ge r0.x, v1.x, l(0.500000)
1: and o0.xyz, r0.xxxx, l(0x3f800000, 0x3f800000, 0x3f800000, 0)
2: mov o0.w, l(1.000000)
3: ret 

geではgreater or equalを行います。入力値が0.5より大きいかを比較しています。
andではANDを行います。geで0か1が来るのでそれを使用して比較します。
movではw(アルファ値)に1.0を入れています。

次はfloat c = i.uv.x > 0.5 ? 1.0 : 0.0の処理です。

0: lt r0.x, l(0.500000), v1.x
1: and o0.xyz, r0.xxxx, l(0x3f800000, 0x3f800000, 0x3f800000, 0)
2: mov o0.w, l(1.000000)
3: ret

stepを使用したときとほぼ同じですね。
ltはless thanのようなのでstepは以上かを知らべているのでそちらに合わせたコードにしてもう一度コンパイル結果を見ます。

float c = i.uv.x >= 0.5 ? 1.0 : 0.0の処理です。

0: ge r0.x, v1.x, l(0.500000)
1: and o0.xyz, r0.xxxx, l(0x3f800000, 0x3f800000, 0x3f800000, 0)
2: mov o0.w, l(1.000000)
3: ret 

同じになりました。

つまり、lerpとstepを使って最適なコードにしているように思えて内部的に行っているコードは三項演算子使って分岐するのと変わらないようです。(今回のコードでは)

iq氏のサイトに載っていたコードをコンパイルしてみる

三項演算子を使用したコード

float2 snap45( in float2 v )
{
    float2 s = sign(v);
    float x = abs(v.x);
    return x>0.923880?float2(s.x,0.0):
           x>0.382683?s*sqrt(0.5):
                    float2(0.0,s.y);
}
0: lt r0.xy, l(0.000000, 0.000000, 0.000000, 0.000000), v1.xyxx
1: lt r0.zw, v1.xxxy, l(0.000000, 0.000000, 0.000000, 0.000000)
2: iadd r0.xy, -r0.xyxx, r0.zwzz
3: itof r0.xy, r0.xyxx
4: mul r1.xy, r0.xyxx, l(0.707107, 0.707107, 0.000000, 0.000000)
5: lt r1.zw, l(0.000000, 0.000000, 0.923880, 0.382683), |v1.xxxx|
6: mov r0.z, l(0)
7: movc r0.yw, r1.wwww, r1.xxxy, r0.zzzy
8: movc o0.xy, r1.zzzz, r0.xzxx, r0.ywyy
9: mov o0.zw, l(0,0,0,1.000000)
10: ret 

比較やmovを行っているようで分岐は無いようです。

次は最適化したように見えるコードです。

float2 snap45( in float2 v )
{
    float2 s = sign(v);
    float x = abs(v.x);

    float w0 = step(0.92387953,x);
    float w1 = step(0.38268343,x)*(1.0-w0);
    float w2 = 1.0-w0-w1;

    float2 res0 = float2(s.x,0.0);
    float2 res1 = float2(s.x,s.y)*sqrt(0.5);
    float2 res2 = float2(0.0,s.y);

    return w0*res0 + w1*res1 + w2*res2;
}
0: mov r0.y, l(0)
1: lt r0.zw, l(0.000000, 0.000000, 0.000000, 0.000000), v1.xxxy
2: lt r1.xy, v1.xyxx, l(0.000000, 0.000000, 0.000000, 0.000000)
3: iadd r0.zw, -r0.zzzw, r1.xxxy
4: itof r1.xy, r0.zwzz
5: ge r0.zw, |v1.xxxx|, l(0.000000, 0.000000, 0.923880, 0.382683)
6: movc r1.w, r0.z, l(0), l(1.000000)
7: and r0.zw, r0.zzzw, l(0, 0, 0x3f800000, 0x3f800000)
8: mul r2.x, r1.w, r0.w
9: mad r0.w, -r0.w, r1.w, r1.w
10: mul r0.x, r1.x, r0.z
11: mul r1.xw, r1.xxxy, r2.xxxx
12: mad r0.xy, r1.xwxx, l(0.707107, 0.707107, 0.000000, 0.000000), r0.xyxx
13: mov r1.z, l(0)
14: mad o0.xy, r0.wwww, r1.zyzz, r0.xyxx
15: mov o0.zw, l(0,0,0,1.000000)
16: ret 

命令数が増えています。
Stepを使ったせいでandや他の計算のためにmadが出てきており、増えています。

まとめ

この結果から、lerp+stepによる方法は別に最適化されているわけではないことがわかりました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?