気になった投稿
ちょっと、 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によって色を変更します。
結果はこのような画像です。
今回重要なのは出力結果の絵ではなく、内部です。そのためコンパイル結果を見ます。
Shaderを選択してCompileボタンを押せば見れます。
まずは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による方法は別に最適化されているわけではないことがわかりました。