約2年ぶりにEmscriptenを使ってハマった点など
はじめに
この記事は、以前作ったz-music.jsを最新のEmscriptenを使ってコンパイルし直した際に出くわした問題をまとめています。「以前」というのは2017末くらいの事。WebAssembly対応が始まった頃に実験的にコンパイルしたのが最後で、公式にはasm.js版がリリース版といった感じでした。「最新」としているのは、2020年7月時点での最新版です。
もともとの動機はwasm対応ではなく、Web Audioで自動再生ができなくなった事に対する回避策の追加で、久々に環境を整えるところからスタートしたら「おや、動かんぞ」という事で四苦八苦するところから始まります。
ハマった点
Pointer_stringifyの廃止
C/C++側からJavaScriptの関数を呼び出す際、JavaScript側で受け取ったポインタからC文字列を復元するための便利関数としてModule.Pointer_stringify
という関数が存在していたのですが、これが廃止になっていました。もともと文字列を雑に扱っていた関数だったので、これが文字コードを考慮した関数に置き換わったのは歓迎すべき点かと。
新しい関数はpreamble.jsにより提供される、AsciiToString
、UTF8ToString
あたり。C/C++側でUTF8を使ってるなら良いのですが、今回のケースのようにレガシーなSJIS環境から持ってくる場合には、真面目にSJISからUnicodeに変換してあげるか、強引にAsciiToString
に渡して日本語は雑に処理するか、ですね。Unicode変換自体、地雷が多いので、今回は雑に処理しました。日本語ファイル名の扱いでバグるのは諦めるとして、メッセージ表示に関しては機種依存文字、エスケープシーケンス、ユーザー定義文字などが使われていたので、適当に正規表現で置換して対処してます。
-s WASM=1がデフォルト
ハマってはいないですが、気になった変更点として、デフォルトのリンクモードがasm.jsではなくwasmになってました。今まではwasm build時に-s WASM=1
を指定していたのですが、今回はasm.js build時に-s WASM=0
の指定が必要でした。
Module.argumentsが変更できない
以前は、Module.preInit
でaddRunDependency
を呼んでEmscripten側のコードを待機させ、その間にModule.arguments
を設定・変更。removeRunDependency
で待機解除する事で、指定した引数でEmscripten側のコードを実行する事ができました。
今回のバージョンではこのタイミングで更新しても間に合わない!というか、なぜかpreInit
よりも前のタイミングでModule.arguments
のコピーを作り、removeRunDependency
後の実行開始時にはModule.arguments
を確認せず、事前コピーした内容を参照する、といった動作に変更になっていました。他の実行環境(nodeとか)との兼ね合いで修正されたコードがたまたまそういう挙動になっているのかも。Emscriptenで作るバイナリは大きい事も多く、実行パラメータが決まるまで読み込みを遅延させるのは得策ではないため、読み込みつつModule.arguments
相当の事ができないか試してみました。
var Module = {
// Default arguments.
arguments: [],
preInit: function () {
Module.addRunDependency("initialize");
},
// Dirty hacks to replace previously evaluated Module.arguments during
// Module.run().
onRuntimeInitialized: function () {
// Resets |calledRun| as this was already set in doRun().
calledRun = false;
// Resets this function so that this should not be called recursively during
// the following run().
Module.onRuntimeInitialized = null;
// Calls run() again, but with the revised arguments.
run(Module.arguments);
// Restores the original |shouldRunNow| so that doRun() does not call main()
// again with the original arguments after this function finishes.
// |calledRun| should be already restored during the run() call above.
shouldRunNow = false;
}
};
Module.arguments = [...];
Module.removeRunDependency("initialize");
あまり行儀のよう方法ではなく、内部のコードを触ってるので、今後のバージョンでまた動かなくなる可能性は高いですが。
addRunDependency
するまでは同じで、その他にonRuntimeInitialized
にもフックをかけます。これはremoveRunDependency
後にC側のmainを呼ぶ直前に呼ばれます。
ここでまずcalledRun
を解除。このフック自体がrun
から呼ばれるのですが、そのrun
を内部からもう一度呼ぶためのトリックです。この解除をしないとrun
が何もせずに終了します。
続けてフック自体を削除。これは自身の呼び出すrun
が再帰的にフックを呼び出すのを避けるためです。
続けてrun
を新たな引数で呼び出し。これにより少し前に呼ばれていたはずのrun
から新しい引数を用いて安全にやり直しができます。
最後にshouldRunNow
を解除して復帰。これにより、もともとこのフックを呼んでいた最初のrun
が、このフックから戻るとmainを呼び出さずにすぐに終了します。
Web XRとrequestAnimationFrame
もう1つ今時な修正をしようとしてハマったのがWeb XR対応。Emscriptenでメインループを回そうと思った時は、emscripten_set_main_loop()をfps=0で呼び出すことで登録した関数がwindow.requestAnimationFrame
から定期的に呼び出されるようになります。
ところがWeb XRでXRSessionを開始した場合、window.requestAnimationFrame
は実行を停止するため、代わりにXRSession.requestAnimationFrame
を使いメインループを実行する必要があります。polyfillで実行しているぶんにはwindow側のループが停止しないため気づかないのですが、実機に持っていったら突然うずまきアイコンでハングアップ……何が起きているんだろう……と悩みました。
実機だけを考えた場合の対処はやるべき事をやるだけ。
XRSession.requestsAnimationFrame
を使ってXR用のメインループを書き、そこからBrowser.mainLoop.runner
を呼び出せば登録されたメインループ関数が動きます。ただ、デスクトップでWeb XRのpolyfillを経由して実行している際などは、window側のループも停止しないため、毎フレーム2回ずつメインループが呼び出されるようになってしまいます。また、XRSession.requestAnimationFrame
自体がwindow.requestAnimationFrame
を使ってpolyfillされているため、
window.requestAnimationFrame = session.requestAnimationFrame.bind(session);
みたいな事をやると再帰呼び出しで無限ループ。このあたりは状況に合わせてうまく対処する必要がありそうです。自分の場合はwindow.requestAnimationFrame
を予め乗っ取って最後のRequest IDを保存するようにし、XR開始時にwindow.cancelAnimationFrame
で明示的にwindow側のループを止めることにしました。
まとめ
以上、ざっくりと。Module.argumentsなんかは小細工してないで公式に問い合わせて安定した方法を提供してもらえよって感じですけど、ひとまずはメモ書きで。落ち着いたらまた考えます。
Author And Source
この問題について(約2年ぶりにEmscriptenを使ってハマった点など), 我々は、より多くの情報をここで見つけました https://qiita.com/toyoshim/items/cc89e4351227d5a2bc60著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .