Open3

WebGL-Native: UnityをAOTコンパイルして実行する

okuokuokuoku

prev: https://zenn.dev/okuoku/scraps/2ebc38f03786f4
next: https://zenn.dev/okuoku/scraps/5777ef80737f27

ここまでで一旦記事にした。

https://zenn.dev/okuoku/articles/5a7a04e75234b3

とりあえず起動はする

WebAssemblyとのインターフェースに絞ってネイティブモジュール化してみた。

4コア8スレッドのマシンで試しているので、期待値としてはCPU利用率は12.5%くらいになるはず。それよりも高くなっているのは、Node.JS側のGCが コンカレントGCでマルチスレッド化 されているため。

CPU利用率が高いところはロード中で、ゲームシーンではおおむね10%程度なので、目標は達成できていると言える。ロード中というのは、WebAssemblyのimport / export処理で、これらはまだ ffi-napi で実装されたままなので遅い。 ...というか ffi-napi ってそんなに遅かったのか。。

okuokuokuoku

例外どうすんのか問題

https://github.com/okuoku/cwgl-proto/commit/c9ef92a754c6e51f19a10cf8a5868be709a3f6df

WebAssembly → JavaScript 呼出し中に発生した例外は、とりあえず(元々のJavaScriptコールバックの)生成元に伝達することにしてみた。

... これが正しいのかは何とも言えないが、EmscriptenはWebAssembly処理を中断するためにJavaScript側で例外を投げるケースがあり、そういうケースはこれで対応できる。

今のところ、WebAssembly MVPでは例外の処理ができず、 setjmp / longjmp はこの方法で実装されているようだ。率直に言ってあんまり健康な気がしない。

okuokuokuoku

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実装はこの差をどうも無視しているっぽい。。

https://github.com/okuoku/cwgl-proto/commit/085faaf3706eadb8cc7a2ca9b87a7d49dd95db95

とりあえず 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 = {};