Direct3D ShaderModel4.0 Shaderで迷路作成

2D 迷路を作成する Shader を作ってみました。
これは実際に見た方が早いと思うので、環境がある方はぜひ走らせて、
動いているところをご覧ください。

64×64 で作成中
maze 64x64

512×512 で作成中の一部
maze 512x512

前のフレームの画像をもとに Shader で発展させていくだけで迷路が
出来上がります。

同時に移動した場合の成長点同士の衝突判定を行う都合上、壁が 1dot
成長するために 2フレームかかります。
その代わり平行していくつでも成長点を設けることができ、
同時に複数の壁を伸ばすことができます。
画像1枚をレンダリングしているだけなので、成長点がいくつあっても
処理速度はほぼ一定です。

アーカイブ内の initdata.png が初期画像です。任意のサイズを与える
ことができます。また白黒2値の絵を描いておくとそれをもとに発展
させます。最外周には必ず白枠が必要です。

生成処理は PixelShader 1つで完結しています。
最適化は全くしておらず、命令数は 625 slot もあります。
ほとんど if 文の塊で、調べたら動的分岐が 9段もネストしていました。
temp register を 27個も使っているので並列度も低いです。
ループを unroll しているのは最適化のためではなく、ループを中断
終了するためです。

sampler を使わずに Buffer から読み込んで整数処理すれば、条件判断
も簡略化できるためずっと小さくなるでしょう。

乱数テーブルのみ CPU で生成しています。本当はこれもシェーダーで
やるべきところなのですが今回は見送りました。

成長点はランダムで壁が変質して出来ます。各ピクセルの意味は R で
判断しており、次の 4つの状態があります。

R = 状態
	0.0  = 空白 (黒)
	1.0  = 固定壁 (白)
	0.5  = 成長点 (壁相当)
	0.25 = 成長予約席 (空白相当)

成長点のみ G B A も意味を持ちます。

R = 0.5 (成長点)
G = 向き (0.0~0.25=上, 0.25~0.50=下, 0.50~0.75=左, 0.75~1.00=右)
B = 直進制限長
A = 回転タイムアウト

最後になかなか埋まらない隙間が出来るのは壁から成長点を作る判定が
単なる乱数だからです。全く発展できない場所にもどんどん成長点を
作ってしまい、それがちらついてノイズに見えてしまいます。
必要な場所に生成して、不要な場所には成長点を作らないようにすれば
隙間も埋まるしちらつきも減ると思います。
成長点の生成ノイズが縞々に見えるのは、乱数の質が悪い証拠です。

実行ファイルとソースのアーカイブはこちら。シェーダーも入ってます。
wheelhandle_ss03t.zip

動作には Vista + DirectX10 (August2007 Runtime) が必要です。

Folding@home 1PFLOPS 超えのその後

ここ数日ポイントの低いデータしか来なくなって、1日の平均が大幅に
下がっていました。チーム順位もすっかり変動が無くなっていて、
こまめなチェックも飽きが出はじめていた、ちょうどそんな頃。
たまたま全体の stat を見ていたら歴史的瞬間(?)が待っていたという
のがこれ。
Folding@Home 1PFLOPS 達成?
また止めるタイミングを逃してしまいました。

その後1日経過してさらにスコアは上昇しています。

Folding@Home Client statistics by OS

OS Type            Current TFLOPS    Active CPUs    Total CPUs 
Windows            163               171778         1793194 
Mac OS X/PowerPC   7                 9311           105296 
Mac OS X/Intel     13                4120           23248 
Linux              36                21427          244481 
GPU                42                704            4187 
PLAYSTATIONR3      793               35552          246939 
Total              1054              242892         2417345 

Last updated at Mon, 17 Sep 2007 02:51:22  

見所は Current TFLOPS の Total です。
これは参加している全計算機の演算能力の合計で、約 24万 CPU/GPU が
実稼動していて総計 1054 TFLOPS をたたき出していることになります。
1PETA FLOPS を超えています。
そのうち PS3 は 35552台。1台あたりおおよそ 22GFLOPS くらいです。
理論的なピーク値ではなくて、実稼動でこれだけ出ているのだから
かなりのものです。

PS3 は Version 1.2 への upgrade の影響が大きいとは思いますが、
さらにポイントが低い代わりに演算効率の良いデータが増えたのか、
涼しくなって純粋に参加台数が増えたのか、その両方かもしれません。

ちなみに GPU はもっと上を行っており、処理効率が非常に高くおよそ
58GFLOPS にも達しています。アルゴリズム的に相性の良いプログラム
ではさすがに爆発的なパワーを発揮しますね。
現状 ATI X1 系でしか動いていないのは、もしかしたら分岐のコストに
原因があるのかもしれません。R600 の HD 2 系ではこの辺若干仕様が
逆行しているため、今度は G80 系の方が中心になる可能性はあります。
ともあれ汎用化の道をたどって進化している GPU なので、今後 D3D10
クラスで動くようなれば、さらに高度かつ高速な運用が見込めるでしょう。

ゲーム機にビデオカードと、今家にはいったい何 GFLOPS 分くらいの
プロセッサがあるのでしょうか・・。

Folding@Home
Wikipedia Folding@Home
Wikipedia Folding@Home jp

Direct3D ShaderModel4.0 Shaderで HELLO WORLD

あまり実用性が無いサンプルですが、Shader で HELLO WORLD を
作ってみました。

ss02t

ポイントは直接 Buffer に strcpy で文字列を書き込んでいるところ。
つまり Shader で文字列をそのまま表示しています。

描画文字列の書き込み

const char*	Message= "HELLO WORLD!";

g_iStringBuffer->Map( D3D10_MAP_WRITE_DISCARD, 0, &ptr );
strcpy( ptr, Message );	// ←ここ
g_iStringBuffer->Unmap();

描画文字列の表示も、Draw() に strlen() した文字列長をそのまま
渡しているだけです。

g_iDevice->Draw( strlen( Message ), 0 );	// ←ここ

StringBuffer は Shader の IA に渡しています。つまり Vertex 相当です。
1文字が 1頂点で、書き込まれているのはアスキーコードのみ。
stride size (1頂点のサイズ) はわずか 1byte です。

UINT	vSize= sizeof(char);	// ←ここ
UINT	vOffset= 0;
g_iDevice->IASetVertexBuffers( 0, 1, &g_iStringBuffer, &vSize, &vOffset )

InputLayout も DXGI_FORMAT_R8_UINT の1つだけ。

static const D3D10_INPUT_ELEMENT_DESC	_inputDesc[]= {
{ "CODE", 0,  DXGI_FORMAT_R8_UINT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0, },
};

もちろん Message は任意の文字列にできます。
が、付属の適当フォントがだめだめなので小文字とか はみ出します。
もう少し実用的にするなら D3D10_MAP_WRITE_DISCARD でなく
D3D10_MAP_WRITE_NO_OVERWRITE にして、バッファ管理
してあげる必要があるでしょう。
あとシェーダーでごまかしていますが、ステート周りなども未設定です。
(終了も手抜き)

詳しくは下記ソースをご覧ください。
wheelhandle_ss02t.zip

D3D10/DX10 の ジオメトリシェーダーに頼りまくってます。

Folding@Home 1PFLOPS 達成?

久しぶりに stats を開いたら
いつのまにか総計が 997TFLOPS になっていました。
以前の 1PETA ショック があるので期待せずに見守っていたら、、
ついに 1000TFLOPS を達成したようです!

Folding@Home Client statistics by OS

OS Type            Current TFLOPS*   Active CPUs    Total CPUs 
Windows            163               171862         1792463 
Mac OS X/PowerPC   7                 9080           105018 
Mac OS X/Intel     13                4052           23160 
Linux              36                21284          244203 
GPU                42                717            4189 
PLAYSTATIONR3      741               34784          245854 
Total              1002              241779         2414887 

Last updated at Sun, 16 Sep 2007 03:01:58  
DB date 2007-09-16 03:03:06  

Direct3D ShaderModel4.0 HLSL GetDimensions() でサイズを受け取る

ShaderModel 4.0 では、Shader 内で Texture の画像サイズを簡単に
参照することができます。自前でフィルタリングする場合など、UV 値の
計算に使えます。
3.0 以前のように、わざわざテクスチャサイズを外部から constant で
渡さなくてすむわけです。

取得するには Texture のオブジェクトに対して GetDimensions します。
August 2007 SDK のマニュアルを見るとちょっと説明が不完全で、
引数も足りず使い方もいまいちよくわかりません。メソッド名も微妙に
間違っているようです。

実際にコンパイルしてエラーメッセージから調べてみました。
GetDimensions() が正しい名前で、後述するいくつかのオーバーロードが
あるようです。
例えば Texture2D の場合次のようになります。

Texture2D    diffuseTexture;
  :

float4 PS_Main( GS_OUTPUT In ) : SV_Target
{
    float2 size;
    float  level;
    diffuseTexture.GetDimensions( 0, size.x, size.y, level );
     :
}

最初の 0 は参照する MipLevel です。
次の2つが受け取る width と height で、texture の pixel サイズに
相当します。例えば 1024 x 1024 のテクスチャを渡すと、

 size.xy= float2( 1024, 1024 )

が入ります。最後は MipLevel の個数が返ります。

// Texture2D の場合
GetDimensions( in uint level, out uint width, out uint height, out uint levels );
GetDimensions( in uint level, out float width, out float height, out float levels );
GetDimensions( out uint width, out uint height );
GetDimensions( out float width, out float height );

2D の場合上記 4つのオーバーロードがあり、uint でも受け取れるし
miplevel が不要なら width と height だけ受け取ることもできるようです。
2D 以外は下記のとおり。

// Texture1D の場合
GetDimensions( in uint level, out uint width, out uint levels );
GetDimensions( in uint level, out float width, out float levels );
GetDimensions( out uint width );
GetDimensions( out float width );

// Texture3D の場合
GetDimensions( in uint level, out uint width, out uint height, out uint depth, out uint levels );
GetDimensions( in uint level, out float width, out float height, out float depth, out float levels );
GetDimensions( out uint width, out uint height, out uint depth );
GetDimensions( out float width, out float height, out float depth );

// TextureCube の場合
GetDimensions( in uint level, out uint width, out uint height, out uint levels );
GetDimensions( in uint level, out float width, out float height, out float levels );
GetDimensions( out uint width, out uint height );
GetDimensions( out float width, out float height );

Cube にも height がありますね。事実上 2DArray 相当だからでしょうか。