WebGL-Native: UnityをAOTコンパイルして実行する
prev: https://zenn.dev/okuoku/scraps/2ebc38f03786f4
next: https://zenn.dev/okuoku/scraps/5777ef80737f27
ここまでで一旦記事にした。
とりあえず起動はする
WebAssemblyとのインターフェースに絞ってネイティブモジュール化してみた。
4コア8スレッドのマシンで試しているので、期待値としてはCPU利用率は12.5%くらいになるはず。それよりも高くなっているのは、Node.JS側のGCが コンカレントGCでマルチスレッド化 されているため。
CPU利用率が高いところはロード中で、ゲームシーンではおおむね10%程度なので、目標は達成できていると言える。ロード中というのは、WebAssemblyのimport / export処理で、これらはまだ ffi-napi
で実装されたままなので遅い。 ...というか ffi-napi
ってそんなに遅かったのか。。
例外どうすんのか問題
WebAssembly → JavaScript
呼出し中に発生した例外は、とりあえず(元々のJavaScriptコールバックの)生成元に伝達することにしてみた。
... これが正しいのかは何とも言えないが、EmscriptenはWebAssembly処理を中断するためにJavaScript側で例外を投げるケースがあり、そういうケースはこれで対応できる。
今のところ、WebAssembly MVPでは例外の処理ができず、 setjmp
/ longjmp
はこの方法で実装されているようだ。率直に言ってあんまり健康な気がしない。
setIntervalの挙動がブラウザとNode.JSで違う問題
WebブラウザのsetIntervalは整数値を返却する。
戻り値
intervalID
は、setInterval()
を呼び出して作成したタイマーを識別する、0 ではない正の整数値です。
しかし、Node.JSは整数ではなく Timeout
オブジェクトを返却する。
$ node
Welcome to Node.js v14.15.3.
Type ".help" for more information.
> setInterval(function(){}, 1000)
Timeout {
_idleTimeout: 1000,
_idlePrev: [TimersList],
_idleNext: [TimersList],
_idleStart: 12521,
_onTimeout: [Function (anonymous)],
_timerArgs: undefined,
_repeat: 1000,
_destroyed: false,
[Symbol(refed)]: true,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 185,
[Symbol(triggerId)]: 5
}
JavaScriptは動的型付なため、戻り値を消費しなければ問題にならない。しかし、EmscriptenではsetIntervalがWebAssembly側から呼ばれるため、戻り値の型は正確でなければならない ... はずなんだけど、Node.JSのWebAssembly実装はこの差をどうも無視しているっぽい。。
とりあえず setInterval
は常に -1
を返すようにしておく。
diff --git a/jstestapp/index.js b/jstestapp/index.js
index acd8229..4ed7602 100644
--- a/jstestapp/index.js
+++ b/jstestapp/index.js
@@ -45,6 +45,13 @@ const storage = require("./storage.js");
const EmuCanvas = require("./emucanvas.js");
const WebAssembly = require("./wasmproxy.js");
+const orig_setInterval = global.setInterval;
+
+function setInterval(cb, timeout){
+ orig_setInterval(cb, timeout);
+ return -1;
+}
+
const nav = {};
const doc = {};
const wnd = {};