この話の続きです
https://qiita.com/nanikore55554/items/6da8a40dbc6258b819a2
要約すると「Electron上でVulkanを動かすプログラムを現実的な範疇まで軽くしてみた」という話です。
このプログラムをもっと軽くする事に挑戦しました。
1.そもそも何故重いのか
詳しい話は上のURLを見ていただくとして、この文が原因です。
pixels = new Uint8Array(bar.send_raw_image().buffer.slice(0, texWidth*texHeight*4));
このbar.send_raw_image()から呼び出されるBuffer型の配列からUint8Array型に変えている部分が重くなっている原因です。
Buffer型の配列はvulkanで生成されている画像の生データなのですが、20万程度の要素数があります。これだけ大量の要素数をコピーして新しい型の変数に変える処理をJavascript上でやればプログラムが遅くなるのは当然でしょう。
つまり、この部分を直接Uint8Array型に変えるプログラムを書けば軽くなるはずです。
2.どうやって生データの画像をを直接Uint8Arrayに変えられるのか
このbar.send_raw_image()は元々DLLにある関数で、Vulkanで生成された画像をスクリーンショットとしてCのconst char*型で返す関数です。
このbar.send_raw_image()をnode-ffi-napiというDLLから関数を呼ぶnode.jsのアドオンを経由して呼び出しています。
この際、const char*はBuffer型として返されており、Uint8ArrayにC++上で直接変換して呼び出すのはnode-ffi-napiでは不可能です。
ではどうするのかというと今回はnapiとnode-gypという物を用いてnode.jsのネイティブアドオンを作成する方法を取りました。
3.napiとnode-gypを経由してDLLを呼び出す
napiとは簡潔に言うとnode.jsのCやC++製のアドオンを作るためのプログラムであり、node-gypはそのアドオンをビルドするためのプログラムです。詳しいことは下記のURLを参考にすると良いと思います。
https://dev.classmethod.jp/articles/native-extension-using-n-api/
上にあるURLを元にDLLのネイティブアドオンを作りました。その際に使われるC++のソースコードを抜粋するとこうなります。(バグがあると思われる事とあくまで実験のため、Vulkanの終了処理を書いていませんので参考にする方はご注意ください)
#include <napi.h>
#include <vector>
#include <DllHelloVulkan.h>
intwidth=800;intheight=600;Napi::ValuevulkanStart(constNapi::CallbackInfo&info){start();returninfo.Env().Null();}Napi::Valueget(constNapi::CallbackInfo&info){//Vulkanで画像を生成する関数main_loop();//スクリーンショットを書く関数make_ss();//send_raw_imageは撮ったスクリーンショットを取り出す関数returnNapi::TypedArrayOf<uint8_t>::New(info.Env(),4*width*height,Napi::ArrayBuffer::New(info.Env(),send_raw_image(),4*width*height),0);}Napi::Objectinit(Napi::Envenv,Napi::Objectexports){exports.Set(Napi::String::New(env,"get"),Napi::Function::New(env,get));exports.Set(Napi::String::New(env,"start"),Napi::Function::New(env,vulkanStart));//exports.Set( Napi::String::New( env, "main_loop" ), Napi::Function::New( env, main_loop ) );returnexports;}NODE_API_MODULE(addon,init)
4.実際に作ったネイティブアドオンを使用した結果
実際にこのネイティブアドオンを使用するソースコードを書き、Electron上で動かしてみました。
すると、CPUの負担が約半分に減りました
大体30fpsで5~6%程度、60fpsで10~12%程度です。30fpsで8~10%、60fpsで20%以上CPUに負担がかかっていた前回と比べれば、かなりの進歩です。
おそらく、ElectronでVulkanを動かすプログラムでこれ以上速度を追求するのはほぼ不可能だと思います。
5.最後に分かった事
1.もうWebGPUいらなくね?
2.けどWebGLの力を借りているのでWebGPUの力を借りられればもっと高速になるのか?
3.うまくやればFlutterにも応用可能か?
4.作ったのはいいけど役に立つのかは微妙・・・なのか?(この辺の話は後ほど)