Shaderでライフゲーム

自分の実力ではないけど。
http://ja.doukaku.org/comment/5512/
使えるようにちょっとだけ変えた。
頂点シェーダを殺したのと、テクスチャ変数名の変更。
なんかおかしかったのを直したので差し替えといた。DXRuby1.1.9とruby1.8.7で確認。

このサイズのto_imageはかなり遅い。GetRenderTargetDataしてから更にMANAGEDで確保したテクスチャにコピーしているからだ。
新機能のパフォーマンスチューニングは必須だと思うが、それはまだ先。

require 'dxruby'

hlsl = <<EOS
// Cから変更できる変数の宣言。問題サイズに対応。座標計算に使う。
float F_X;
float F_Y;
// テクスチャの宣言
texture Tex0;
// テクスチャの定義
sampler Samp1 = sampler_state
{
    Texture        = <Tex0>;
    MinFilter    = POINT;
    MagFilter    = POINT;
    MipFilter    = NONE;
    AddressU    = WRAP;
    AddressV    = WRAP;
};
// 頂点シェーダの出力形式
struct VS_OUTPUT
{
    float4 Pos        : POSITION;
    float2 Tex        : TEXCOORD;
};
// 頂点シェーダ(未使用)
VS_OUTPUT VS_DEFAULT (
    float4 Pos        : POSITION,
    float2 Tex        : TEXCOORD
){
    VS_OUTPUT Out    = (VS_OUTPUT)0;
    return Out;
}
// ピクセルシェーダ
float4 PS_TEST ( VS_OUTPUT In ) : COLOR
{
    float4 Color        = {0.0f, 0.0f, 0.0f, 0.0f};
    float2 TexA            = {0.0f, 0.0f};
    float4 Color0        = {0.0f, 0.0f, 0.0f, 0.0f};
    int num;
    int sum;
    int ret;

    TexA                = In.Tex;

    Color                += tex2D( Samp1, TexA+float2(-F_X, -F_Y) );
    Color                += tex2D( Samp1, TexA+float2(0.0f, -F_Y) );
    Color                += tex2D( Samp1, TexA+float2( F_X, -F_Y) );
    Color                += tex2D( Samp1, TexA+float2(-F_X,  0.0f) );
    Color0                =  tex2D( Samp1, TexA+float2( 0.0f,  0.0f) );
    Color                += tex2D( Samp1, TexA+float2( F_X,  0.0f) );
    Color                += tex2D( Samp1, TexA+float2(-F_X,  F_Y) );
    Color                += tex2D( Samp1, TexA+float2(0.0f,  F_Y) );
    Color                += tex2D( Samp1, TexA+float2( F_X,  F_Y) );
    num                    = round(Color0.r);
    sum                    = round(Color.r);
    ret                    = (sum==3) + (sum==2) * num;
    /*
    // 上の行は以下の条件式をまとめたもの。
    // 分岐をなくしたので多くのGPUで実行可能なはず。
    if(num==0)
    {
        ret                = sum==3;
    }else{
        ret                = ((sum==2)+(sum==3)) != 0;
    }
    */
    return float4((float)(ret*0xff), 0.0f, 0.0f, 1.0f);    // RGBA
}
// テクニック
technique RenderScene
{
    pass P0
    {
//        VertexShader    = compile vs_2_0 VS_DEFAULT();
        PixelShader        = compile ps_2_0 PS_TEST();
    }
}
EOS

shader = Shader.new(hlsl, "RenderScene")
x = 1.0/640
y = 1.0/480
shader.setValue("F_X", x)
shader.setValue("F_Y", y)

rt = RenderTarget.new(640, 480)
DXRUBY_RENDERTARGET.push rt

image = Image.new(640, 480)
for i in 0..99999
  image[rand(640), rand(480)] = [255,0,0]
end

font = Font.new(32)
Window.loop do
  Window.drawShader(0, 0, image, shader)
  Window.update(rt)
  image = rt.to_image
  Window.draw(0, 0, image)
  Window.drawFont(0,40,Window.fps.to_s, font)
  Window.drawFont(0,0,Window.getLoad.to_i.to_s, font)
  GC.start
end