D3D10/DX10 の ShaderModel4.0 では整数演算を行うことができます。
この整数演算系の命令出力を見ていると、従来の浮動少数演算と
全く同じレジスタを使っていることがわかります。
Buffer やレジスタ、リソースアクセスは TYPELESS 宣言しなければ
型付けされますが、Shader の命令的には float だろうが int だろうが
特に区別が無いようです。
これは SSE 等の CPU 命令でも同じで、32bit ×4 の 128bit の値と
いうだけで特に動的な型情報はなさそうです。データの中身を利用する
側が便宜上区別しているだけに過ぎません。
例えば条件判定の結果によって 0.0f か 1.0f を代入したい場合、
HLSL コンパイラはこんなコードを出力します。
// HLSL v.z= v.x < v.y ? 1.0f : 0.0f; // asm lt r0.x, v0.x, v0.y and o0.z, r0.x, l(0x3f800000)
ここでは直後が ret なので o0 に代入されています。
上の v は float4 で宣言しています。
0x3f800000 は 32bit 浮動少数の 1.0f を bit 表現したものです。
r0.x が 0 なら o0.z もそのまま 0.0f になり、r0.x の bit が
すべて 1 なら 1.0f が代入されます。
つまり実数の 0.0f か 1.0f か選択するために整数演算の bit mask を
活用して最適化をはかっています。演算上整数データと浮動少数データの
区別がないことがわかります。
またこのことから lt 命令は結果を整数値で返し、さらに成立すれば
bit が全部 1 (-1)、不成立なら 0 を返すのだと予想できます。
(昔の basic 言語のように)
ちなみに、代入先の 0 と 1.0 を交換するとこんなコードになります。
// HLSL v.z= v.x < v.y ? 0.0f : 1.0f; // asm lt r0.x, v0.x, v0.y movc o0.z, r0.x, l(0), l(1.000000)
movc は条件付転送命令のようです。
条件成立時の値を 1.0f 以外にしてみます。
// HLSL v.z= v.x < v.y ? 4095.0f : 0.0f; // asm lt r0.x, v0.x, v0.y and o0.z, r0.x, l(0x457ff000)
条件式の結果からマスクで値を生成しているだけなので、どんな値でも
ペナルティが無いようです。
条件結果を整数で返す場合は次のようになりました。
// HLSL uint c= v.x < v.y; // asm lt r0.x, v0.x, v0.y and o0.x, r0.x, l(1)
HLSL/C言語の仕様的には条件式は -1 or 0 ではなく 1 or 0 なので、
こちらでも and 演算が入ってしまいます。
このような型を無視したデータの取り扱いを HLSL 上で行うためには
asint(), asuint(), asfloat() 命令を使います。
型変換ですがデータは変更しないので型キャストとは異なります。
キャストでは実際にデータを変換する命令に置き換わりますが、
これらの命令は実行時には何もしません。コンパイラに対して型が
変わったことを通知しているだけです。
例えば asfloat() の動作を C/C++ で書くとこんな感じです。
float asfloat( int b ) { return *(float*)(&b); } // もしくは float asfloat( int b ) { union { float _f; int _i; } temp; temp._i= b; return temp._f; }
asfloat を使うと 1.0f を直接代入する代わりに次のように記述できます。
float b= asfloat( 0x3f800000 );
この as~系命令はかなり使えそうです。
例えば RGBA32F のテクスチャやレンダーターゲットで、R, G だけ
float とみなし、B, A を uint とみなして処理することもできます。
データのパックや圧縮など、開いているチャンネルの有効利用にも
応用できそうです。
以前作った迷路のシェーダーも、これを使っていればもっと判定が
簡略化できました。
・Direct3D ShaderModel4.0 Shaderで迷路作成
・Direct3D 10 ShaderModel4.0 迷路の自動探索Shader
ただ頂点データに使うなど、バーテックスシェーダーや
ジオメトリシェーダー から ピクセルシェーダー へ値がわたるときは
難しいでしょう。異なる型で補間が発生してしまうので、型を混用した
ようなデータは向いてないかもしれません。
GPU によっては浮動少数演算に特化されていて、整数専用の演算は
まだ特殊な ALU を必要としている可能性があります。演算性能を
最大限引き出すには float の方が有利なので、整数演算の多用は
パフォーマンスとの相談になってくるかもしれません。この点は
要注意です。