gpu.jsでライフゲーム

#GPGPUでライフゲームをやってみる
##GPGPUとは
General-purpose computing on graphics processing units の略らしい
要は、本来なら画像処理に用いられるGPUを汎用計算（特に並列処理）に用いようという考え
WebGL2.0を使えば、面倒なソフトウェアも必要なしにGPGPUが試せる
##gpu.jsとは
詳しくは[gpu.js - GPU Accelerated JavaScript](http://gpu.rocks/)を参照
要は、WebGLでGLSLの知識なしに汎用計算ができるライブラリ
特に難しい記法でもない
HTMLなら

```html
<script src="https://cdn.rawgit.com/gpujs/gpu.js/45103b7f154a12ec639990c6896b21a053eb6a6c/bin/gpu.js"></script>
```

Node.jsの場合は

```
npm install gpu.js --save
```

で利用できる

##ライフゲームを作る
詳しい記法はほかの記事を見るか[GitHub - gpu.js](https://github.com/gpujs/gpu.js)を参照のこと
ここでは必要なことのみ解説する

オブジェクトを初期化する
この時、引数にcanvasエレメントを指定してやると、後で描画ができる

```javascript
const gpu = new GPU({canvas:[エレメント]});
```

次に、`gpu.createKearnel(function,option)`で、GPU内で実行する関数を定義する
普通のjavascriptのように書けるが、内部でこの関数はいったんフラグメントシェーダに変換されるので、いろいろと制約がある模様
例えばアロー関数は使えないとかネイティブ関数でも数学系以外は使えないなど
[Supported-Math-Functions](https://github.com/gpujs/gpu.js#supported-math-functions)

このライフゲームは上下左右は絶対に死んでいる

```javascript
const life = gpu.createKernel(function(board) {
　//現在位置とワールドの大きさ、集計用の変数
  const x = this.thread.x;
  const y = this.thread.y;
  const l = this.constants.size;
  let sum = 0;
  //盤面の端なら絶対死亡
  if(x==0||y==0||x==l-1||y==l-1){
    return 0;
  }
  //周囲八マスの生きているセルを集計
  sum = board[y-1][x-1] + board[y-1][x  ] + board[y-1][x+1] +
        board[y  ][x-1]                   + board[y  ][x+1] +
        board[y+1][x-1] + board[y+1][x  ] + board[y+1][x+1];
  //ルールに照らし合わせて生死を判断（1が生、0が死）
  if(board[y][x]==1&&(sum==2||sum==3)){
    return 1;
  }
  if(board[y][x]==0&&sum==3){
    return 1;
  }
  return 0;
}, {
  constants: { size: 128 },
  output: [128,128]
});
```

第二引数にある`constants`には、関数内で使う定数を指定できる
`output:[128,128]`とあるように、出力は128*128の配列である
つまり、この関数は128*128回実行されることになる
この値は自由に設定できる

せっかくなのでcanvasに出力する用の関数も作る

```javascript
const render = gpu.createKernel(function(board) {
  this.color(
    board[this.thread.y][this.thread.x],
    board[this.thread.y][this.thread.x],
    board[this.thread.y][this.thread.x],
  1);
}, {
  output: [128,128],
  graphical:true
});
```

`graphical:true`にしたうえで`this.color(r,g,b,a)`を使って色を指定する
出力と描画を一緒の関数にすることはできないらしい（？）

あとは普通の関数のように使える

```javascript
output = life(board);
```

`graphical:true`にした関数は出力がないので

```javascript
render(board);
```

これだけで、初期化時に指定したcanvasに描画される

##全コード
```javascript
//初期化
const gpu = new GPU({canvas:[エレメント]});

const life = gpu.createKernel(function(board) {
  //現在位置とワールドの大きさ、集計用の変数
  const x = this.thread.x;
  const y = this.thread.y;
  const l = this.constants.size;
  let sum = 0;
  //盤面の端なら絶対死亡
  if(x==0||y==0||x==l-1||y==l-1){
    return 0;
  }
  //周囲八マスの生きているセルを集計
  sum = board[y-1][x-1] + board[y-1][x  ] + board[y-1][x+1] +
        board[y  ][x-1]                   + board[y  ][x+1] +
        board[y+1][x-1] + board[y+1][x  ] + board[y+1][x+1];
  //ルールに照らし合わせて生死を判断（1が生、0が死）
  if(board[y][x]==1&&(sum==2||sum==3)){
    return 1;
  }
  if(board[y][x]==0&&sum==3){
    return 1;
  }
  return 0;
}, {
  constants: { size: 128 },
  output: [128,128]
}); 

//描画用
const render = gpu.createKernel(function(board) {
  this.color(
    board[this.thread.y][this.thread.x],
    board[this.thread.y][this.thread.x],
    board[this.thread.y][this.thread.x],
  1);
}, {
  output: [128,128],
  graphical:true
});

//盤面を作る
let board = [];
for (let i = 0; i < 128; i++) {
  let p = [];
  for (let j = 0; j < 128; j++) {
    p.push(Math.round(Math.random()));
  }
  board.push(p);
}

//fps60で計算と描画
var fps = 60;
setInterval(function(){
  board = life(board);
  render(board);
},1000/fps);
```

##触ってみて
かなり扱いやすい
WebGLをゼロから書くよりも、面倒な処理がない
何よりjavascriptそのままの記法で関数が書けるというのは、GLSLをあまり触ったことのないものにとっては素晴らしい利点である

ただし、このライフゲームでは、GPU本来の速度は出せていなかった
GPU以外の部分でボトルネックがあったのだろうと推測される

ともかく、GPGPUのすごさを体感するには十分だった