日別アーカイブ: 2007年10月4日

Direct3D 10 HLSL の asint/asuint/asfloat 命令

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 の方が有利なので、整数演算の多用は
パフォーマンスとの相談になってくるかもしれません。この点は
要注意です。