WebAssembly をウェブブラウザーで活用する(1)

この記事は ゆるWeb勉強会@札幌 Advent Calendar 2020 の 12日目です 😀

今年 2020年、WebAssembly は W3C 勧告に到達し、モダンウェブブラウザーで安心して WebAssembly を活用できるようになった年となりました。

また、ウェブブラウザー外で WebAssembly を動作させ、さまざまな環境で動作するユニバーサルバイナリーとして、コンテナー技術やマイコンなどで動作させる動きも広がった年でもありました。

この記事では、まずはウェブブラウザー視点から、WebAssembly が現在どのように活用され初めているのかを紹介してみたいと思います。

WebAssembly でできること、できないこと

WebAssembly の実態は低レベルマシンコードで、ウェブブラウザーなどに実装される WebAssembly のランタイムがこれを読み込み、プログラムとして実行する環境です。

現在、さまざまなコンピューター言語が WebAssembly に対応しており、それらの言語でかかれたプログラムを WebAssembly にコンパイルし、ウェブブラウザーで実行することができます。

ウェブブラウザーではこれまで、プログラム言語としては JavaScript しか動作しませんでしたが、WebAssembly の登場により、JavaScript 以外の言語のプログラムも実行することができるようになりました。

WebAssembly に「ネイティブ」対応している言語は多くありますが、自分が把握している言語は次のとおりです。

  • C/C++
  • Rust
  • AssemblyScript
  • Go
  • Swift

「ネイティブ」と書いたのには訳があり、WebAssembly 上で動作する言語はその他にも多数存在し、たとえば Python なども動作しますが、スクリプト言語のインタープリタや JIT は、元をただせば C/C++ でつくられており、WebAssembly が C/C++ に対応していることに立脚すると、それらのインタープリタ自体を WebAssembly で動作させてしまえば、ウェブブラウザーで Python が動作する、、そういう仕組みになっています。(C#/WebAssembly などはこの方式です)

さて、このように WebAssembly では多数の言語が動作しますが、その言語でつくられたソフトウェアはそのままでは動作するというわけではありません。ウェブブラウザーで動作する WebAssembly は現在のところ、DOM などのウェブブラウザーの機能(Web IDL)にアクセスすることができないからです。

つまりユーザーへの入出力部分(画面を描くであるとか、クリックを受け付けるとか)は、従来の JavaScript で作成し、処理の部分のみを WebAssembly に投げるようにプログラムを構成します。これが現在のウェブブラウザー上の WebAssembly でできない部分です。

ちなみに、今年 W3C 勧告となった WebAssembly の仕様は MVP(最初)の仕様群となっています。

ウェブブラウザーを含む各 WebAssembly ランタイムは次の仕様となる Proposals を絶賛先行実装中で、これらの実装が進むと WebAssembly から直接ウェブブラウザーの機能(Web IDL) へのアクセスも可能になります。(そのようになるように作業が進められています)

実際に使えるようになるのは、来年か再来年かになるかと思いますが、、楽しみです!

WebAssembly 活用例

WebAssembly のできることできないことが分かったところで、WebAssembly の活用例をみながら、どうやって実装しているのかを紹介していきたいと思います。

Ruffle A Flash Player emulator written in Rust

まずは、ニュースなどでもでていましたので知っている方もいらっしゃるかもですが、Adobe Flash を WebAssembly で実装した Ruffle。

https://github.com/ruffle-rs/ruffle

A Flash Player emulator written in Rust

ウェブブラウザーのプラグインで Flash サポート終了するなら、WebAssembly でつくってしまえば良いのでは?という発想よりつくられた、そのまま .swf ファイルが実行できるプログラムで Rust 製です。

デモが次のサイトから見ることができます。お手持ちの .swf があれば動かしてみると面白いかもしれません。

https://ruffle.rs/demo/

描画は canvas を、音声は WebAudio を JavaScript でインターフェースし、.swf の解析や実行を Rust 側で行っています。

同様に Microsoft Silverlight も WebAssembly に移植した実装が存在します。

https://opensilver.net/

ffmpeg.wasm

ウェブブラウザーが、どのような画像形式や動画再生をサポートするのかでやきもきする時代は WebAssembly の登場により終焉を迎えました。なぜならば、それらのデコーダーは C/C++ でかかれているからです。つまりそのプログラムをそのまま WebAssembly にしてしまえば、どのような画像でも動画でも再生できてしまいます。

というわけで、ffmpeg は様々な動画コーデックをもつ有名な C/C++ でかかれたオープンソースですが、これを WebAssembly コンパイルにしてインターフェースしたのが ffmpeg.wasm になります。動画再生だけはなく生成やエンコードも可能です。

https://github.com/ffmpegwasm/ffmpeg.wasm

FFmpeg for browser and node, powered by WebAssembly

ffmpeg.wasm は npm パッケージ化されていますので、使いたいなぁと思ったら、package.json の依存に加えるだけで使うことができます。

このような感じで WebAssembly できていると知らずに node のパッケージを使っていることも増えているのではないかと思います。(自分が確認したものでは .gz を展開するライブラリーが .wasm を使っているパターンがありました。

poton Rust/WebAssembly image processing library

動画に続いて画像処理系のライブラリーです。

これまでも画像に対してフィルターをかけたいなどといった場合は、CSS でネイティブに備わる機能が使えましたが、自分の思ったとおりの処理をしたい場合は、JavaScript で行う必要がありました。

もちろん JavaScript でもプログラミング可能ですが、画像処理に関してはプログラムの書きやすさを考慮すると他の言語を使ったほうが有利です。また WebAssembly にすることで高速化がかなり期待できます。

poton は Rust で実装された画像処理ライブラリーで、減色やリサイズ、フィルター、回転などなどの処理を WebAssemby で行うことができます。

デモが次のサイトから確認できます。

https://silvia-odwyer.github.io/photon/demo.html

こちらのライブラリーも npm 化されていますので、手軽に自分のプログラムから利用可能です。


ウェブブラウザーで動作する WebAssembly は、高速性もさることながら、さまざまな言語のエコシステムをそのまま活用できるのが大きな魅力の一つです。

WebAssembly 登場以前は、行いたいと思った処理を JavaScript で書き直す必要がありましたが、今後はその必要がなくなり、特に C/C++/Rust でかかれたすぐれたライブラリーをそのまま使うことができます。

次の回では、C/C++ もしくは Rust でかかれたプログラムを WebAssembly に移植して動作させるデモをやってみたいと思います!(続く

関連

マイコン MCU で AssemblyScript + WebAssembly/Wasm3 を動かす

WebAssembly の登場でウェブブラウザー上で C/C++/Rust といった高速で動作する言語を使うことができるようになりましたが、一方でそのコンパクトな実装は、マイコンなどの小型のコンピューターでスクリプト言語を省メモリーで素早く実行する環境ももたらすことになりそうです。

この記事では WebAssembly のインタープリター実装のひとつである Wasm3 を活用して、ESP32 / K210 MPU で TypeScript のサブセットである AssemblyScript を動かす方法を解説しています。

ウェブブラウザーとマイコンで同じスクリプトが動作するのは感動的です。 🙂

https://raw.githubusercontent.com/h1romas4/maixduino-wasm3-testing/master/docs/images/maixduino-wasm3-02.jpg
Maixduino マイコンとウェブブラウザーで動作する同じ Conway’s Game of Life スクリプト

本稿は 2020年2月 の AssemblyScript 0.9.2、Wasm3 は 0.4.6 時点の情報です。API フリーズはしていませんので、今後のバージョンアップで変わる部分がある可能性があることだけご留意ください。


(2023年1月追記) 以下の GitHub リポジトリーと記事に、より新しいバージョンの利用例があります。

https://github.com/h1romas4/m5stamp-c3dev

This is a development board for the M5Stamp C3 (RISC-V/FreeRTOS).

また M5Stack Core2 バージョンもあります。

https://github.com/h1romas4/m5stack-core2-wasm3-as

M5Stack Core2 with WebAssembly. Wasm3/AssemblyScript Demo


ソースコードやビルド方法は以下の github リポジトリーにコミットしてあります。Wasm3 と AssemblyScript や各マイコンの SDK のバージョンは git のタグなどで固定していますので、どの時期でもビルドし動作させられると思います。

M5Stack (ESP32) 版:

https://github.com/h1romas4/m5stack-wasm3-testing
WebAssembly interpreter Wasm3 on M5Stack (work in progress)

Maixduino (K210) 版:

https://github.com/h1romas4/maixduino-wasm3-testing
WebAssembly interpreter Wasm3 on Maixduino (work in progress)

Wasm3

Wasm3 は C言語でかかれた WebAssembly のインタープリター実装です。非常にコンパクトで高速に動作し、ESP32 や K210、ARM などの MCU(マイコン)を含む、さまざまな実行環境で動作を検証しながらつくられています。

https://github.com/wasm3/wasm3
The fastest WebAssembly interpreter

マイコンの C/C++ ツールチェイン/SDK により Wasm3 をビルドし、(例えば)main.c の中から Wasm3 の実行環境コールし、事前に AssemblScript などでかかれたプログラムをビルドして出力しておいた .wasm バイナリーを読ませることで WebAssembly を実行することが可能です。

Wasm3 は小さなメモリーで動くことも特徴となっており、公式ドキュメントで

Minimum useful system requirements: ~64Kb for code and ~10Kb RAM

となっています。 Limited support ですが最少で flash 128KB、RAM 16KB の AVR マイコンでも動作するようです。

なお、この記事で紹介している M5Stack と Maixduino のスペックは次のようになっています。

NameMCUClockFlashRAM
M5Stack BasicESP32240MHz4MB520KB
MaixduinoK210400MHz(600MHz)16MB6MB(8MB)

M5Stack は RAM がいくつかのエリアに分かれていて大きなメモリーの malloc に少々コツがいりますので、M5Stack Basic よりも 追加で PSRAM が 4MB ついている M5Stack Fire のほうが試しやすいかもしれません。

Maixduino については AI コア使用可否とクロック設定により括弧内の性能で動作させています。

AssemblyScript

AssemblyScript は WebAssembly 向けのコンパイラー言語です。TypeScript のサブセットとしてつくられており、WebAssembly アセンブラ命令へのバインディングと、それを活用してつくられた JavaScript の標準関数によく似た Standard library を持ちます。 Map や Array といった関数をスクリプトで利用可能です。

一部 Math や Date 関数、ウェブブラウザーでよく使われる console.log() 関数などを使う場合は、ホスト環境上に定義された関数に依存があり、マイコンで動作させる場合はそれらを C言語の関数として準備してあげます。実行に必要なバインディングは std/bindings にて export declare function 定義されています。

assemblyscript/std/assembly/bindings/

一点不明だったのが、env.abort() 関数で、Standard library を使おうとすると export されるようです。ドキュメントに記載がありました!

assemblyscript/std/assembly/builtins.ts

// @ts-ignore: decorator
@external("env", "abort")
declare function abort(
  message?: string | null,
  fileName?: string | null,
  lineNumber?: u32,
  columnNumber?: u32
): void;

ちょっとあれこれやってみたのですが、うまく C言語の関数にバインドできなかったので AssemblyScript の –use オプションでブランクを設定し export しないようにしています。

asc assembly/index.ts -b build/app.wasm -t build/app.wat --runtime full --use abort=

さて、同じ原理で、AssemblyScript のユーザー関数も export declare function としてホスト環境上の関数にリンクすることができますので、マイコンのハードウェア操作を行う関数を準備しておけば、AssemblyScript 内からマイコンの機能を呼び出すことができます。

Wasm3 で AssemblyScript から Arduino の digitalWrite 関数を呼び出す例:

AssemblyScript – arduino.ts

@external("arduino", "digitalWrite")
export declare function digitalWrite(pin: u32, value: u32): void;

// C側の関数呼び出し
arduino.digitalWrite(2, 1);

Arduino ホスト – main.cpp (m3 が Wasm3 です)

#include <m3_api_defs.h>
#include <m3_env.h>
#include <Arduino.h>

m3ApiRawFunction(m3_arduino_digitalWrite)
{
  // 引数取得
  m3ApiGetArg(uint32_t, pin)
  m3ApiGetArg(uint32_t, value)
  // Arduino 関数呼び出し
  digitalWrite(pin, value);
  m3ApiSuccess();
}

M3Result m3_LinkArduino(IM3Runtime runtime)
{
  IM3Module module = runtime->modules;
  const char *arduino = "arduino";
  // arduino.digitalWrite 関数を m3_arduino_digitalWrite にリンク
  m3_LinkRawFunction(module, arduino, "digitalWrite", "v(ii)", &m3_arduino_digitalWrite);
}

また、WebAssembly からホストするマシンのファイルシステムやネットワークにアクセスする WASI API への対応も進められているようです。WASI はまだ策定段階ですが、これらの API も将来マイコンで使えるようになるかもしれません。(まだ関数名が違う部分もありそうですが Wasm3 でも一部対応しています

AssemblyScript とホスト間のインターフェースは関数呼び出し以外にもメモリーを共有する方法が準備されており、AssemblyScript のコンパイルオプションの -memoryBase が Wasm3 との組み合わせで便利でした。(そして、WASM ネイティブ命令でアクセスできるため恐らく高速です)

Static memory

Memory starts with static data, like strings and arrays (of constant values) the compiler encountered while translating the program. Unlike in other languages, there is no concept of a stack in AssemblyScript and it instead relies on WebAssembly’s execution stack exclusively.

A custom region of memory can be reserved using the --memoryBase option. For example, if one needs an image buffer of exactly N bytes, instead of allocating it one could reserve that space, telling the compiler to place its own static data afterwards, partitioning memory in this order:

これは WebAssembly でアロケートするメモリーの 0番地から指定した任意のバイト数をリザーブするオプションで、AssemblyScript の load / store 命令によりアクセスすることができます。

package.json

asc assembly/index.ts -b build/app.wasm -t build/app.wat --memoryBase 57600 --runtime none --validate --sourceMap --optimize

後述のサンプルではこの機能を使い、アロケートしたメモリーを仮想 VRAM として見立て、AssemblyScript からメモリー書き込み後、マイコン側で LCD に転送することで画面描画を行っています。

index.ts

@inline
function pget(x: u32, y: u32): u8 {
    return load<u8>(y * width + x);
}

@inline
function pset(x: u32, y: u32, v: u8): void {
    store<u8>(y * width + x, v);
}

main.cpp – Wasm3 の m3_GetMemory 関数で memoryBase のポインターを取得して LCD に転送する例:

// bitblt
uint8_t* vram = (uint8_t*)(m3_GetMemory(runtime, 0, 0));
M5.Lcd.pushImage(40, 0, 240, 240, vram, true);

WebAssembly interpreter Wasm3 on M5Stack 編

ESP32/M5Stack で、AssemblyScript/Wasm3 にてフィボナッチ数列の計算と仮想 VRAM の転送による LCD 描画のサンプルを作成してみました。プログラムやビルドの方法などは以下のリンクを参照ください。

https://github.com/h1romas4/m5stack-wasm3-testing

ESP32 特有な部分としては、Wasm3 の関数が高速に動作する IRAM 上に配置されるように Wasm3 のコンパイルオプションを構成しています。

component.mk

CFLAGS += -DESP32
# CFLAGS += -DM3_IN_IRAM
CFLAGS += -Dd_m3LogOutput=true
CFLAGS += -Dd_m3VerboseLogs=true
CFLAGS += -O3
CFLAGS += -freorder-blocks
# CFLAGS += -Dd_m3FixedHeap=96000
# CFLAGS += -Dd_m3MaxFunctionStackHeight=128
# CFLAGS += -Dd_m3CodePageAlignSize=1024
# CFLAGS += -Dd_m3EnableOptimizations=1

# COMPONENT_ADD_LDFRAGMENTS += linker.lf

本来 linker.lf の指定で IRAM 上に載るはずなのですが、指定の仕方が悪いのかうまく効かなかったため M3_IN_IRAM を無効にして関数に IRAM_ATTR を付けています。

なお、フィボナッチ数列のサンプルについては、fib(19) くらいまでいくとおそらく再起が深くなりすぎメモリーが足りなくなります。@wasm3_engine さんより ESP32 の動作は今後さらに改善されるというコメントをいただいています。このような深い再起のない通常のプログラムであれば問題なく動作します。

VRAM 転送で円を描画しているサンプルは、240x240x8bit の領域を前述の memoryBase コンパイルオプションを使って AssemblyScript で確保しています。

M5Stack は本来 320×240 解像度ですが残念ながらそのサイズを指定すると malloc に失敗してしまいました。おそらく memoryBase ではなくて C側で malloc してポインターを受け渡せばいける気がしますが、方法について調査中です。

なお、描画速度ですが、このサンプルは LCD SPI に対して VRAM を最適化なしに M5.Lcd.pushImage 関数で単純に送信しているため速くありません。DMA などを活用すれば改善しそうです。

WebAssembly interpreter Wasm3 on Maixduino 編

Maixduino(K210) 上で M5Stack と同じ VRAM テストと、AssemblyScript の公式サンプルにありました Conway’s Game of Life を移植してみました。プログラムやビルドの方法などは以下のリンクを参照ください。

https://github.com/h1romas4/maixduino-wasm3-testing

Conway’s Game of Life デモは、AssemblyScript の Math.random() 関数を使っており、前述の通り Standard library の Math を使うためには binding を実装しなくてはなりませんが、使っているのが random 関数だけでしたので、ちょっとずるをしてその部分だけ実装して使うようにしています。

Math.ts

export declare function random(): f32;

index.ts

import * as Math from "./Math";

  for (let y = 0; y < h; ++y) {
    for (let x = 0; x < w; ++x) {
      set(x, y, Math.random() > 0.1 ? BGR_DEAD & 0x00ffffff : BGR_ALIVE | 0xff000000);
    }
  }

main.c

m3ApiRawFunction(math_randome) {
    m3ApiReturnType (float_t)
    m3ApiReturn     ((float_t)rand() / RAND_MAX);
}

M3Result LinkFunction(IM3Runtime runtime) {
    IM3Module module = runtime->modules;
    const char* math = "Math";
    m3_LinkRawFunction(module, math, "random", "i()",  &math_randome);
    return m3Err_none;
}

なお M5Stack と同様に、マイコンからの VRAM の LCD 転送については最適化しておらず、Conway’s Game of Life に関してはウェブブラウザーの canvas 形式である 32bit ARGB 形式を 16bit RGB に単純にループで変換していますので、かなり速度改善の余地があると思います。


Wasm3 公式サイトの Wasm3 vs other languages によりますと、Wasm3 の実行速度はマイコンでよく用いられる Micropython よりも 20倍以上高速な結果がでています。

                                             fib(40)
-----------------------------------------------------------------------------------------
LuaJIT             jit                         1.15s
Node v10.15        jit                         2.97s ▲ faster
Wasm3              interp                      3.83s
Lua 5.1            interp                     16.65s ▼ slower
Python 2.7         interp                     34.08s
Python 3.4         interp                     35.67s
Micropython v1.11  interp                     85,00s
Espruino 2v04      interp                       >20m

自分の所感でも非常にコンパクトで速いことが確認でき、マイコン上のユーザーインターフェース構築やルール定義など、柔軟性がありかつ安全でなければいけない領域で大いに活用できるのはないかと感じました。

引き続きチャレンジしたいと思います!

関連

今年 WebAssembly でつくった3つのアプリ

WebAssembly Advent Calendar 2019 の 11日目の記事です。

WebAssembly の登場で C/C++/Rust など JavaScript 以外の言語のエコシステムをウェブブラウザーに持ち込むことができるようになり嬉しいな〜ということで、どのくらい動くのかという検証もかねて、3つほどアプリをつくって動作させてみました。

Sapporo.CSS (SaCSS) vol 110 LT 資料(2020/01/25 追加)

Emscripten 編

C言語でかかれたゲーム機メガドライブのエミュレーター Genesis-Plus-GX に WebAssembly 用のインターフェースを追加し、Emscripten でコンパイルして動作させてみました。

かなり重めのサウンドコアエミュレーションを有効にしてコンパイルしているのですが、iOS Safari を含め非常に高速に動作しています。(ベアナックル2が最後までプレイできました 🙂

主に Firefox と iOS Safari で確認しています。 Firefox は7段キーボード世代の ThinkPad(いつのだ…)で動作させていますが 60 fps 維持できています。

ソースコードを以下で公開しています。cmake/make と webpack でビルドできるようになっています。

Genesis-Plus-GX WebAssembly porting

https://github.com/h1romas4/wasm-genplus

ROM を吸い出す環境がない方は、homebrew の ROM が動作するかもしれません。.wasm はコンパイル済みのバイナリをコミットしていますので node だけあれば遊べると思います。

ゲームパッドのアサインは手持ちの XBOX ONE 用になっていますので適当に修正ください。。ちなみに、iOS 13 から PS4/XBOX ONE コントローラーサポートが入りましたが、Safari の GamePad API からも接続できました。

Emscripten 環境で少し詰まったのは次のポイントでした。

Emscripten を webpack からモジュールとして import する方法:

リンカオプションで MODULARIZE=1 を指定。

add_compile_flags(LD
    "-s MODULARIZE=1"
)

JavaScirpt で import して module を取得

import wasm from './genplus.js';

wasm().then(function(module) {
    gens = module;
});

Wasm 側で malloc したメモリーポインタの取得する方法:

モジュールの module.HEAPU8.buffer など HEAP* ビューで取得。

wasm().then(function(module) {
    gens = module;
    // memory allocate
    gens._init();
    // load rom
    fetch(ROM_PATH).then(response => response.arrayBuffer())
    .then(bytes => {
        // create buffer from wasm
        romdata = new Uint8Array(gens.HEAPU8.buffer, gens._get_rom_buffer_ref(bytes.byteLength), bytes.byteLength);
        romdata.set(new Uint8Array(bytes));
        message("TOUCH HERE!");
        initialized = true;
    });
});

大きめの static のアロケートに失敗した場合:

リンカオプションで初期メモリーサイズを指定。

add_compile_flags(LD
    "-s ALLOW_MEMORY_GROWTH=1"
    "-s TOTAL_MEMORY=32MB"
)

Rust / wasm-pack 編 (1)

Rust/wasm-pack で最初につくったアプリです。

Wasm 側でアロケートしたメモリーを仮想 VRAM として、Rust で何も考えずにむちゃ描きしたらどれくらいの速度になるだろうということで試したものになります。

デモサイトから実際に動作するところが見れます。

https://github.com/h1romas4/wasm-canvas-bitblt

sin/cos で画像回転させながらラスタースクロール的な動きをさせていますが、思うままにプログラムをかいているため RGBA の 4Byte 転送を全画素で何度も回していたりします。

ちょっと興味があったのが、速くなるかなと Rust の unsafe のブロック転送を使い、

unsafe {
    ptr::copy_nonoverlapping(
        [color.0, color.1, color.2, 0xff].as_ptr(),
        self.vram.as_mut_ptr().offset(pos),
        4,
    );
}

のようにしてみたのですが、これは Wasm 的には単純なループで展開されてコンパイルされていました。これは今後 Bulk memory operations が入りコンパイラが対応することで改善するかもしれません。

WebAssembly/bulk-memory-operations

Some people have mentioned that memcpy and memmove functions are hot when profiling some WebAssembly benchmarks.

なお、このプログラムは前述の古い ThinkPad T420s では 45fps そこそこでしたが、iPhone X では余裕で 60fps でていました。速い。。

Rust / wasm-pack 編 (2)

最初の Emscripten メガドライブエミュレーターから、ゲーム機の音源部分(FM音源・PSG)を取り出しプログラムを C言語から Rust に移植したものです。

エミュレーターから音源 LSI に発行するコマンドを横取りして保存した、ゲームミュージックを楽しむ VGM という形式のファイルがありますが、それを再生するプレイヤーになっています。

YM2612/SN76489 VGM player by Rust

こちらもデモサイトから動作を見ることができます。自分がつくったサンプル VGM をひとつ入れています。しょぼいですがクリックで鳴ります。本当はもっとすごい楽曲が再生できます。。

.vgm を準備できる方はドラッグアンドドロップしてみてください。(なお全て WebAssembly で処理してますので、サーバーにファイルアップロードはされません。安心してお試しください)

ソースは以下から参照できます。

https://github.com/h1romas4/rust-synth-emulation

デバッグ手法:

プロジェクトを pure Rust 部分と、Wasm 部分に分けて構成しています。現在 WebAssemby のデバッグ環境はまだ整っていませんので、複雑な処理はネイティブで実行できる環境で行うと良さそうです。

Wasm のデバッグ環境については Chrome が DRAWF に対応しつつあるとのことで(まだステップ実行のみ)、今後整っていくのではないかと思います。

Improved WebAssembly debugging in Chrome DevTools

As a first step, DevTools now supports native source mapping using this information, so you can start debugging Wasm modules produced by any of these compilers without resorting to the disassembled format or having to use any custom scripts.

ライブラリの活用:

本プレイヤーアプリですが、.vgz と呼ばれる .gz 圧縮された .vgm ファイルの再生にも対応させています。

WebAssembly/Rust は stdlib でコンパイルできますので、pure Rust の Gzip, and Zlib ライブラリーである flate2 を dependencies に追加してコンパイルして、ファイル展開させてみたところ問題なく動作しました。

[dependencies]
flate2 = "1.0"

この辺は各言語のエコシステムを活用できる Wasm の強みだなと感じます。

Runtime Error: Index out of bounds.:

移植中 Rust のオブジェクトを JavaScript から new した際に、Index out of bounds. が発生してオブジェクトがつくられない事象が発生しました。ぱっと原因が分からなかったため、ソースを削る方向で試していくと、[0; 50000] ほどの配列の初期化の部分で発生していました。

Make stack size configurable

Currently the stack-size for local variables of the generated wasm code is preconfigured to be 1048576 bytes. It is easy to reach this limit,

どうやら stack-size の初期値が小さいということで、.cargo/config に次の記述をして回避しています。

[target.wasm32-unknown-unknown]
rustflags = [
  "-C", "link-args=-z stack-size=32000000",
]

WebAssembly 登場にてウェブブラウザーで好きな言語で、好きなプログラムを動かせるようになって嬉しいです。

今後も継続してウォッチしていきたいと思います。

関連記事