GLSL
GPGPU
WebGL2.0

WebGLでGPGPUできる計算機を作ってみた話

More than 1 year has passed since last update.

GPGPUとは

General-Purpose Computing on graphics Processing Unitsの略です
本来描画処理に利用するためのGPUを描画以外の計算にも利用してやろうって事ですね

transformFeedback

今年(2017年現在)webGL2.0が有効化され,それによってtransformFeedbackという関数が使用できるようになりました
こいつは頂点シェーダでの計算結果をバッファにバインドできる関数です
つまりこいつを使うとGPGPUができます!

transformFeedbackの使い方

まず頂点シェーダで計算した結果を出力するための変数をout修飾子を付けて定義しておきます

#version 300 es 
out float result;
void main(void){
result=2.0*3.0;
}

次にjavascript側でプログラムを作成する際に,gl.transformFeedbackVaryingsという関数を入れておきます
第一引数はリンクするプログラム,第二引数は頂点シェーダで計算した結果を保持している変数名,第三引数はとりあえず下記とします

var program = gl.createProgram();

gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.transformFeedbackVaryings(program, ["result"], gl.SEPARATE_ATTRIBS);
gl.linkProgram(program);
gl.useProgram(program);

いよいよ頂点シェーダでの結果をバッファにバインドします

var vTransformFeedback = gl.createBuffer();
var transformFeedback = gl.createTransformFeedback();

gl.bindBuffer(gl.ARRAY_BUFFER, vTransformFeedback);
gl.bufferData(gl.ARRAY_BUFFER, Float32Array.BYTES_PER_ELEMENT, gl.DYNAMIC_COPY);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, transformFeedback);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, vTransformFeedback);
gl.beginTransformFeedback(gl.POINTS);
gl.drawArrays(gl.POINTS, 0, 1);
gl.endTransformFeedback();

流れとしては頂点バッファを一度作成したバッファにバインドした後,その結果をgl.TRANSFORM_FEEDBACK_BUFFERにバインドしてやる,と言った感じです

上記のコードでgl.TRANSFORM_FEEDBACK_BUFFERに頂点シェーダでの計算結果をバインドすることができました

しかしこのままだとjavascript上でどのように結果を表示できるのかわかりません
バッファの計算結果を配列として取得できる関数としてgl.getBufferSubDataというものがあります

var arrBuffer = new ArrayBuffer(Float32Array.BYTES_PER_ELEMENT);
arrBuffer = new Float32Array(arrBuffer);
gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER, 0, arrBuffer);
console.log(arrBuffer);

これでコンソールに頂点シェーダでの計算結果が出力されるはずです
この場合であれば2.0*3.0=6.0が格納されている配列として出力されると思います

計算機作ってみた

ということでtransformFeedbackを利用してGPGPUができる計算機を作ってみました
https://ukeyshima.github.io/GPGPU-calculator/
webGL2.0をサポートしているブラウザでしか動きません,携帯とかたぶんほぼ無理...
普通にjavascriptでする計算と切り替えることができます
わかりにくいですがGPUって表示されてるときはjavascriptで計算されてますね,CPUって表示されてるときにGPGPUしてます

手順としては2.0*3.6+5.3などの数式を文字列として定義し,イコールのボタンが押された際にその計算をする頂点シェーダと何も出力しないフラグメントシェーダでwebGLのプログラムを作成し,transformFeedbackを利用して計算結果をcanvasに出力する,といった流れです

ちなみに超絶どうでもいいですが計算結果を出力する部分はcanvas2Dで普通に書いていますが,それ以外は全部GLSLで記述してます(1とか2とか÷とかです)

どうでもいいです

パフォーマンス

当たり前っちゃあ当たり前ですがGPUでこんな普通の計算してもなんの良いこともないです
イコールボタン押すたびにプログラム作成するわけですから普通に計算するより全然遅いです

しかし!つよいGPUを積んでいてめっちゃ長い計算だと普通にjavascriptで計算するよりも速い速度で計算ができます!
実際にGTX1080を積んでいるPCで動作を確認してみたところ,1.0を10万回ぐらい掛けた計算だと普通にjavascriptで計算するより速いよって結果が出ました
1000万回ぐらい掛けるともう二倍の速度ですね〜〜

まとめ

webGLを利用してGPGPUできる計算機を作ったお話ですが,transformFeedbackの使い方が載っているサイトなどがほとんどなく,使えるようになるまですごい苦労しました
なのでついでに使い方とかも書いておきました
まだ使えるようになった程度なのでもっと効率化できる,とかこここうしないほうが良いだろ,とかこれ違くね?等の御意見ありましたらどんどんお願いします
今回のプログラムはABPro2017という発表会で発表してきたものです