AssemblyScript で WebAssembly を知る

毎年恒例 ゆるWeb勉強会@札幌 Advent Calendar 2022 への投稿です…!

札幌で開催しているWeb系勉強会、「ゆるWeb勉強会@札幌」の2022年アドベントカレンダーです。

過去発表内容のまとめ、発表する機会の無かったネタ、勉強会に参加したいけどできなかったので、などなど、Webに関するキーワードを中心としつつ自由に書いていってください!

毎年 WebAssembly 関連を投稿させていただいておりますが、今年は WebAssembly が理解の手助けとなるような、入門記事を書いてみたいと思います。

AssemblyScript 言語

最初に WebAssembly のおおよそですが、 プログラムソースコードをビルドすることにより Wasm バイナリー(.wasm)を出力し、このファイルを WebAssembly ランタイムに読ませることでプログラムを動作させる技術です。

この記事で取り上げる AssemblyScript 言語は、TypeScript Like なソースコードを Wasm バイナリーにビルドできるコンパイラーです。npm で簡単にツールチェインが導入でき、また WebAssembly 専用の言語となっているため、端的に理解しやすく、入門にも最適だと感じます。

The AssemblyScript Book

AssemblyScript compiles a variant of TypeScript (opens new window) (a typed superset of JavaScript) to WebAssembly (opens new window) using Binaryen (opens new window), looking like:

WebAssembly ランタイムを持つ主要ユーザのひとつはウェブブラウザーで、モバイルを含む主要 4 ウェブブラウザーで .wasm を実行可能です。また、ウェブブラウザーから JavaScript ランタイムが node.js として取り出されたように、ウェブブラウザー外で .wasm を動かす動きも近年活発です。

なお、.wasm のビルドに対応しているプログラム言語は、大きく 2種類のパターンがあり、直接 .wasm にビルドするものと、言語のインタープリタ(ランタイム)を .wasm にして、ソースコード(もしくは中間コード)をそのまま与えて動作するものに分けられます。

前者の代表は、Rust、C/C++、TinyGo などなど、そして AssemblyScript もこの仲間です。

後者は、Python(Pyodide)、Ruby、.NET/C# などが該当します。つまり、これらのインタープリタやランタイムが C/C++ であるということに立脚すると、前者の方式で Wasm ビルドできればスクリプトファイルが実行できる、というイメージです。

というわけで、本記事では WebAssembly 自身の理解の手助けとなるよう、直接プログラムが .wasm となり、ランタイムとのインターフェースもシンプルで分かりやすい AssemblyScript を使って解説を進めていきます。

サンプルプログラム

AssemblyScript/WebAssembly を使って時計を描画するプログラムをつくってみました。

次のような要素が含まれています。

  • WebAssembly から直接画面描画をすることができないので、WebAssembly ランタイムから import した、指定した位置に「点」を描画する関数を呼び出し時計を描いている。
  • 上記のような特性を逆に利用すると、ランタイム環境に依存する処理が差し替えられるため、同一 WebAssembly プログラムを、ウェブブラウザーとマイコン(M5Stack) で動作させるマルチプラットフォームの例ともなっています。

ソースコード:

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

M5Stack Core2 With Wasm3/AssemblyScript Demo

WebAssembly プログラムとランタイムのインターフェース

WebAssembly の上のプログラムはよく「計算しかできない」と言われます。これは Wasm がサンドボックスで動作し、その外側のリソースにアクセスできないためです。

外界との接続との唯一のインターフェースは、それぞれの関数の export/import と、メモリーのポインターです。

JavaScript からみた、.wasm は引数の型が不自由な関数が追加されているように見えます。

:ランタイムに関数を公開する AssemblyScript のソースコード:

– 引数の型が u32 という見慣れない型になっている。

export function clock(x: u32, y: u32, r: u32): void {
    analogClock = new AnalogClock(x, y, r);
}

export function tick(): void {
    if(analogClock != null) {
        analogClock.tick();
    }
}

:export function が入った AssemblyScript を .wasm にビルドしたアセンブリーが出力(抜粋):

– 引数が i32 という WebAssembly の型になっている。

  (export "clock" (func 16))
  (export "tick" (func 28))

  (func (;16;) (type 1) (param i32 i32 i32)
  (func (;28;) (type 2)

そしてこの関数は JavaScript から次のように呼び出すことができます。

.wasm をロードする JavaScript のソースコード:

instance.exports.wasm に定義された関数を取得できる。

async function loadWasm() {
    const response = await fetch(new URL('../dist/app.wasm', import.meta.url));
    const responseArrayBuffer = new Uint8Array(await response.arrayBuffer());
    const wasm_bytes = new Uint8Array(responseArrayBuffer).buffer;
    let module = await WebAssembly.compile(wasm_bytes);
    const instance = await WebAssembly.instantiate(module, {
        ...createImports()
    });
    wasmExports = instance.exports;
};

.wasm でエクスポートされた関数を JavaScript からコールするソースコード:

instance.exports にエクスポートされた関数の引数に number 型を渡している。

(async function() {
    await loadWasm();
    wasmExports.clock(160, 120, 120);
    setInterval(() => {
        wasmExports.tick();
        wasmExports.__collect() // clean up all garbage
    }, 500);
})();

ポイントは .wasm 関数の引数には i32, i64, f32, f64 といった WebAssembly で使える(数値)型しかとれないことです。 JavaScript のオブジェクトのようなものや、文字列、もちろん DOM などの WebIDL の型も(現在のところ)そのまま渡して処理はできません。

AssemblyScript inherits WebAssembly’s more specific integer, floating point and reference types:

逆もしかりで、.wasm 側から JavaScript の関数を呼び出す際もこれらの引数型しかとることができません。

:JavaScript の関数を import する AssemblyScript のコード:

export declare function draw_pixel(x: i32, y: i32, color: i32): void;

export declare function をビルドした .wasm のアセンブリー(抜粋):

  (type (;1;) (func (param i32 i32 i32)))
  (import "c3dev" "draw_pixel" (func (;0;) (type 1)))

:AssemblyScript に関数を公開する JavaScript のコード:

async function loadWasm() {
    // ..snip...
    const instance = await WebAssembly.instantiate(module, {
        ...createImports()
    });
    // ..snip...
};

function createImports() {
    let imports = [];
    // ..snip...
    imports['c3dev'] = {
        'draw_pixel': (x, y, color) => {
            canvasContext.fillStyle = convertRGB565toStyle(color);
            canvasContext.fillRect(x, y, 1, 1);
        },
    // ..snip...
}

.wasm 側で import した i32 という数値型 を持つ draw_pixel 関数を呼び出し、最終的に ウェブブラウザーの Canvas に指定座標にひとつ点を描いています。マイコン動作ではこの部分が LCD に点をひとつ描く C の関数が呼び出すようにしています。

(ちなみに、点が描ければ円も線も計算で描けるということで、サンプルプログラムの時計は AssemblyScript の演算処理により全て点だけで描画を行っています)

インターフェースをつくる

以上のように、WebAssembly でプログラムをかく場合は、WebAssembly ランタイムをホストしている環境との連携インターフェースの設計が肝で、特性を考慮して、どのような処理単位にすると効率的に処理できるのかを考えていきます。

また、今回の例では用いていませんが、メモリーのポインター値を共有することで、VRAM や音声波形など大きな演算結果(配列)をインターフェースすることもでき、Wasm 内の演算結果を外部に持ち出すことができます。

このようなホストへのインターフェースの標準化の実装のひとつとして WASI があり、システム時間や乱数シード、ファイルシステムやネットワークへのアクセスが規定されています。(ただしウェブブラウザーには WASI API は実装されていません)

WASI も WebAssembly 型の関数が沢山あるだけですので、覗いてみると理解しやすいかと思います。

また、Rust の wasm-pack(wasm-bindgen) や Emscripten(C/C++) など他の WebAssembly ツールチェインは、Wasm 型との引数合わせやラップ、JavaScript の呼び出しをソースコードジェネレートなどを、自動でやってくれるものとして捉えると考えやすいです。

WebAssembly のインターフェースを知ることで周辺技術が何をしているのかが見えてきそうだな、ということで本記事は書かれました。

処理を速くしたい部分や、JavaScript 外で便利なライブラリー、、圧縮展開、画像動画音声処理、言語解析器などなどを見つけたら、「WebAssembly(型) なインターフェース」を追加して呼び出す。まずは WebAssembly の実用的なユースケースのひとつと思いますので、ぜひお試しください。

関連

Xbox Series S|X コントローラのファームウェアをアップデートする

Xbox Series S|X のリリースノートで「Xbox コントローラーのファームウェア アップデート」とある場合に、手動でコントローラのファームウェアをアップデートする方法がぱっと分からなかったのでメモしておきます。

Xbox コントローラーのファームウェア アップデート

コントローラーを最新のソフトウェアにアップデートしておくと、コントローラーの機能と互換性を最大限に活かすことができます。10 月のアップデートでは、Xbox アダプティブ コントローラーに接続する USB フライト スティックの修正と、以下の Xbox コントローラーの修正を含むファームウェアのアップデートを行っています。

Xbox Series S|X の USB にコントローラを有線接続して次の操作をします。(未確認ですがもしかすると無線でも可能かもしれません)

しいたけボタンから設定画面へ。「デバイスと接続」から「アクセサリ」。

コントローラを選んで「…」ボタン。(ここがむずいw

ファームウェアバージョンを確認しつつ「今すぐ更新」(アップデートがある場合にこのボタンになります)

注意事項をしっかり読んで「続ける」

じっとがまんの子(2分くらい)

おめでとうございます。

「アップデートはありません」おうけいぼす。

以上です。PC 使わずにできて タスカルラスカル。

関連記事

Xbox Series S|X の使い方と小技

Xbox Series S|X 良いです…!(3回目)

2022/10 現在 Xbox Series S が在庫もあり購入しやすくなっています。もしかすると初 Xbox という方もいらっしゃるかもしれません。ようこそ Jump In…!

というわけで、Xbox Series S|X の使い方と小技などを書いてみたいと思います。

以下、現時点の情報でアップデートで構成が変わったり翻訳が変更になるかもしれません。

ホーム画面

Xbox を起動すると最初にでる画面です。デフォルトでたくさんサブメニュー(コントローラの下を押すと切り替わる)があるので圧倒されるかもしれません。

画面一番上にはよく使うもののショートカットが自動に配置されます。

その下からがサブメニューになりますが、いったん使うものだけに絞ると分かりやすくなります。画面右下「カスタマイズ」に割り当てられているコントローラのボタンを押します。

Game Pass と Microsoft Store がゲームの導入で活躍しますので、ここではこれだけ残してほかは表示しないようにしておきます。(慣れてきたらいろいろ追加すると良いでしょう)

Xbox Game Pass

Xbox Game Pass な月額サブスクリプションサービスのメニューです。

初回月 100円なのでかなりお得です。「全てのゲームを見る」から現在導入可能なゲームが一覧できます。加入している人は要チェック…!

Microsoft Store

ゲームが買えるストアです。Xbox Series S の場合は光学メディアスロットがありませんので、Microsoft Store からゲームをダウンロード購入します。通年的に何かしらの「セール」をしていますので、覗いてみましょう。

検索文字列の入力がコントローラで大変という場合は、何かしらの USB キーボードを接続すると良いと思います。普通に動作します。

ゲームの購入は Xbox 本体だけでなく、ウェブ上の Microsoft Store (Xbox と同じ Microsoft アカウントでログインのこと)からも可能です。ただ検索機能がいまいちなので、、目的が見つからない場合は Xbox 本体から検索してみるといいかもです。。

https://www.xbox.com/ja-JP/games/all-games

なお、Xbox 本体で対応していないと思われる(例えば) PayPal 決済がウェブ上からだと通るといった謎の仕様も現在あるようです。(そのうち修正されるかも…)

ウェブ上で買ったゲームは Xbox 本体の Microsoft Store でみると「インストール可能」になりますので、ぽちっとします。

ぼくのゲームはどこ?

所有しているゲームを一覧したい場合は、コントローラの Xbox ボタン(以下しいたけボタン)を押して「マイコレクション」「全て表示」を押します。

本体に導入済みのゲームが一覧されます。

また、本体から削除しているけれど所有権を持っているゲームは「フルライブラリ」「所有しているゲーム」から見ることができます。

所有しているゲームはこの画面からいつでも本体に導入可能です。

ホーム画面にゲームを並べる

ゲームを名前付けしたグループに入れて整理することができます。

グループを新規作成し、ゲームにチェックして「追加」します。「追加」ボタンが右にあるのでご注意を。(よく忘れて前の画面に戻ってしまう)

作成したグループはホーム画面に追加できます。

ホームのカスタマイズ画面から「ホームにもっと追加する」

下を押していくと作成したグループが見えますので追加します。(なお、「ピン留め」はデフォルトで作成済みのグループです。また Quick Resume はシステムの特別なグループ扱いです)

追加…!

ホーム画面に追加した「DEAD OR ALIVE」 グループの例。

ホーム画面に追加した「アーケード」グループの例

便利ですね…!(眺めて悦に浸りましょう)

また、ホーム画面にはグループだけでなく、ゲームそのものも配置可能なものもあります。この場合は右にニュースなどがでますので、継続プレイのゲームは配置しておくと良いでしょう。

本体設定

本体設定は、しいたけボタンを押してでてくるサイドメニューの「プロフィールと設定」の設定から遷移するのが簡単です。

電源関連

起動した Xbox のシャットダウンを行うには、しいたけボタンを長押しします。

電源オフ時の挙動は設定「全般」「電源オプション」から指定できます。

電源オフ中にゲームのダウンロードを走らせたい場合は、「Xbox がオフの時、データ保存機能をオフにする」のチェックを外します。

なお、電源をオフにする方法で「シャットダウン」「スリープ」ともにダウンロードを走らせることができるとのこと。(自分はスリープにしているためシャットダウン時の動きは未検証)

ゲーム中にしいたけボタン

ゲーム中にしいたけボタンを押すと、いつでもサイドメニューが登場します。

別のゲームを起動するもよし、Microsoft Edge で攻略を検索するもよし。

また動画キャプチャーもここからできます。(スクショはコントローラのアップロードマークみたいな中央ボタンでいつでもできます)

別媒体への画像アップロードなどは「すべての共有オプションを表示」から。

ゲームを終わりたい

特に操作はありません。セーブされたことを確認したら、気持ち、しいたけボタンでホームに戻って電源をオフにすればいいかもです。またクイックレジューム対応のゲームは、再び起動すると元のシーンに戻ります。(完全に戻らないこともあるので、セーブは大切に…)

コントローラの電池の蓋が固くて全く開かない

どうやら少数の個体のようですが、電池の蓋が固くて全く開かない場合がありますw Xbox One のはすぐ開くのですが。。

Xbox ワイヤレス コントローラーで乾電池を使用する

コントローラーに電池を取り付けるには、片手でコントローラーを上下逆さまに持ちます。 反対の親指で、矢印記号のバッテリー ドアを押し、矢印が指す方向にドアを動かします。 ドアを取り外したら、電池をコンパートメント内に示されている方向に取り外すか挿入します。

見た目通りの開き方をしますが、中央にフックがありますので、「中央(矢印記号のバッテリードア)を “思いっきり” 内側に押し込みながら」「上にスライド」、、

上(スライド)方向に力を加える前に、いったん内側の電池方向にぐいぐい押し込んでフックを外してから上へスライドするのがポイントす。

ゴム手袋装着などで力を集めるのも良いかも?。。グッドラック。

不具合かな?

Xbox ステータスサイトを見ると吉。

https://support.xbox.com/ja-JP/xbox-live-status

ゲームも含めてコンポーネントごとの詳細な不具合情報が見れます。

個別の不具合の場合は、以下からチャット or 電話サポートを受けられます。ちなみに、オンライン決済などの部分で何度かお世話になっていますが、非常に丁寧に対応していただきました。

https://support.xbox.com/ja-JP/contact-us

故障かな?

Microsoft アカウントにデバイスとして Xbox がひも付きます。xbox.com などでサインインして自分のアカウントが見れます。

「デバイスを修理に出す」というのがありますのでここから修理のオーダーができそうです。(未経験)

ゲームを買ってポイントをもらう

Xbox でお使いの Microsoft アカウントで Microsoft Rewards にサインアップしておくと、Microsoft Store でのゲーム購入でポイントが付きます。

https://www.microsoft.com/ja-jp/rewards

ポイントを獲得できる手段が豊富 オンラインで検索、ショッピング、ゲームをするだけでポイントが貯まります

特に Xbox Live Gold = Xbox Game Pass に加入していると LEVEL 2+ ステージ(最大)扱いでゲーム購入時のポイントが大幅にあがりますのでお得かもです。溜まったポイントは Microsoft Store や Amazon のギフトカードに交換できます。

update 2024-10-28: カスタム指定で引き換えられるようになっていました。便利。

関連