Open3

WebGL-Native: ネイティブコード越しのJavaScript例外のサポート

okuokuokuoku

prev: https://zenn.dev/okuoku/scraps/594b49004593a7
next: https://zenn.dev/okuoku/scraps/f7364055268608

emscripten_set_loop の simulate_infinite_loop に対応したい

前回DOSboxの動作が遅い って書いたけど、そもそもWebGL-Nativeの構成では別にnonblockingであることに拘る必要性はそんなに無いので、Emterpreterを省略して動作させてしまうことにした。

このためには、 simulate_infinite_loop フラグに対応する必要がある。このフラグを使用すると emscripten_set_loop から戻らなくなり 、かつ、 メインループを延々と実行する ことができる。このフラグにより、

main(void){
    初期化();
    while(! 終了リクエスト){
      描画処理();
    }
    終了処理();
}

となっているプログラムを、

main(void){
    初期化();
    emscripten_set_main_loop(描画処理, 0, 1 /* Simulate infinite loop */);
    終了処理(); // ★ これは実行されなくなる
}

と書き換えることで移植でき、終了処理の省略のために main を大巾に書き換えるのを避けられる。

simulate infinite loop の原理

かなり複雑な処理をしているように見えるが実装は単純で、単にWASMから呼出される emscripten_set_loop 内で例外を投げているだけである。

      if (simulateInfiniteLoop) {
        throw 'unwind';
      }

この例外により、 emscripten_set_loop を呼出していたWASMはカッ飛ばされ、Emscriptenのランタイムが何事もなかったかのように main loop として設定された関数を呼び続けるという処理になっている。

okuokuokuoku

DOSBoxの例

DOSBoxの場合はちょっと特殊で、 emscripten_set_main_loop を呼出すまではブラウザ側に一切制御を戻さずにしばらく実行してから emscripten_set_main_loop を呼出している。

void DOSBOX_RunMachine(void){
#if defined(EMSCRIPTEN) && !defined(EMTERPRETER_SYNC)
	if(runcount<2)
		runcount ++;
	else if(runcount==2)
	{
		runcount++;
		if(useRAF)
			emscripten_set_main_loop(em_main_loop, 0, 1);
		else
			emscripten_set_main_loop(em_main_loop, 100, 1);
	}	
	Uint32 ticksStart = GetTicks();	
#endif
	Bitu ret;
	do {
		ret=(*loop)();

これは、DOSboxの構造上、DOSプログラムを1つ実行完了するたびにこの DOSBOX_RunMachine から抜ける必要がどうしてもあるため。この制約のため、デフォルトでは2つ (mount とゲーム本体) プログラムを起動してから emscripten_set_main_loop している。

↓ の ROLLING'95 では、音源ドライバを常駐させる必要があるため 2 → 5 に初期化用の起動枠を増やしている。