Open3

WebGL-Native: NCCC呼出しの準備

okuokuokuoku

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 を使用して実装する。

okuokuokuoku

関数ポインタの値が入っていない問題

https://github.com/okuoku/cwgl-proto/commit/188c77dd982e8e0815c0a32543b8852689c0c965

適当に諸々実装してみたものの、起動後直ぐ 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ユーザの悪い癖だった

https://github.com/okuoku/cwgl-proto/commit/6921ca861841f08e5756b9498317d7246f22f64e

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 以外の値は全て真値)

okuokuokuoku

Buffer と ArrayBufferは違う

https://github.com/okuoku/cwgl-proto/commit/78c220677a8b43000b21d7045ea5ddb2093c41a3

なんか書いたメモリが正常に読めていないと思ったら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);