WebGL-Native: NCCC呼出しの準備
prev: https://zenn.dev/okuoku/scraps/b5e6c0fb9b2ce8
next: https://zenn.dev/okuoku/scraps/f7ac420090e881
呼出し / コールバックプロトコルの実装
前回は各種ランタイム(やwrapper -- 構成の都合上これは別のエントリで)を NCCC で実装した。これにより、wasm2cで変換したWebAssembly関数や、そのために必要なランタイムは全て、
-
uint64_t
の配列にデータを詰めてジャンプし、戻ってきたらuint64_t
の配列からデータを取り出す
という単一のプロトコル(これをNCCCと呼んでいる)で呼び出せるようになった。
今回は、JavaScript側でこのプロトコルの呼出しとコールバックの両方を ffi-napi
を使用して実装する。
関数ポインタの値が入っていない問題
適当に諸々実装してみたものの、起動後直ぐ main
からreturnしてきてしまう。node.jsのWebAssemblyと動作を比較したところ、何故かwasm2c版は即イベントハンドラをunregisterしていることに気付いた。
- (正常動作時)
allowsDeferredCalls: true,
eventTypeString: 'gamepaddisconnected',
callbackfunc: 437,
handlerFunc: [Function: gamepadEventHandlerFunc],
useCapture: 0
- (wasm2c時)
allowsDeferredCalls: true,
eventTypeString: 'gamepadconnected',
callbackfunc: 0, // ★ ↑ では 437 が入っている
handlerFunc: [Function: gamepadEventHandlerFunc],
useCapture: 0
このobjectを埋めるステップを1つ1つ確認する必要がある。。
戻り値が変?
ログを良く見ると、一度は登録に成功していることがわかった。
でイベントハンドラの登録に失敗した場合に EMSCRIPTEN_JoystickQuit
を呼ぶコードがあり、どうやらこれが発動しているようだ。。なので関数ポインタの問題ではなく、戻り値が正常にネイティブ側に渡っていないと見られる。
Schemeユーザの悪い癖だった
diff --git a/jstestapp/nccc-node.js b/jstestapp/nccc-node.js
index 0d79159..b4de327 100644
--- a/jstestapp/nccc-node.js
+++ b/jstestapp/nccc-node.js
@@ -124,7 +124,7 @@ function make_callsite(shortcircuit, shufflecall_ptr){
if(outcount == 0){
return;
}else if(outcount == 1){
- if(rt){ // rt can be undefined (e.g. _emscripten_memcpy_big)
+ if(rt !== undefined){ // rt can be undefined (e.g. _emscripten_memcpy_big)
outmapper[0](outp, rt);
}
}else{
JavaScriptではゼロが偽になるのを忘れてた。。このため関数がゼロを返却するとネイティブコード側のデータが設定されず未定義値になっていた。(Schemeでは false
に相当する #f
以外の値は全て真値)
Buffer と ArrayBufferは違う
なんか書いたメモリが正常に読めていないと思ったらArrayBufferのつもりでBufferを渡しているところがあった。。_●■=
Emscriptenは、WASM側のメモリを各種ワードサイズでアクセスするためにArrayBufferのViewとして配列を用意している。Bufferを渡すと、これらはコピーになってしまいメモリ実体を共有しないのでおかしなことになってしまう。
function updateGlobalBufferAndViews(buf) {
buffer = buf;
Module['HEAP8'] = HEAP8 = new Int8Array(buf);
Module['HEAP16'] = HEAP16 = new Int16Array(buf);
Module['HEAP32'] = HEAP32 = new Int32Array(buf);
Module['HEAPU8'] = HEAPU8 = new Uint8Array(buf);
Module['HEAPU16'] = HEAPU16 = new Uint16Array(buf);
Module['HEAPU32'] = HEAPU32 = new Uint32Array(buf);
Module['HEAPF32'] = HEAPF32 = new Float32Array(buf);
Module['HEAPF64'] = HEAPF64 = new Float64Array(buf);