Slide 1

Slide 1 text

WebAssemblyインタプリタを書く ~Component Modelを添えて~ るっちょ @ruccho_vector

Slide 2

Slide 2 text

作ったもの ● Unity (.NET) 上で動く、C#製WebAssembly?インタプリタ ● Component Model?に対応 ● ゲームにおける会話イベントやカットシーンなどをWasmで動かす

Slide 3

Slide 3 text

ゲーム開発における「スクリプト」 ● ゲーム開発はエンジンの上に軽量なスクリプト環境を作りがち ● アプリケーション寄りのロジックを書く ● 動的なロードや実行、ホットリロードなどをサポート ● 非エンジニアが書けるようにする目的も エンジン (C++) スクリプト (C#, Lua, …)

Slide 4

Slide 4 text

Unityの場合 ● エンジンはC++、ユーザーコードとしてC#がサポートされている ● しかし、C#はスクリプトとして難点あり ○ 一部プラットフォームではAOT制約がある (JITができない) ○ ホットリロード機能はあるが、 よく壊れるので実質機能していない ○ コンパイル時間が長い ● 新たなスクリプトレイヤーが欲しい! C++ C# スクリプト (WebAssembly)

Slide 5

Slide 5 text

WebAssembly (Wasm) ● 特定のOSやCPUに依存しない仮想の命令セットとバイナリ形式 ● Rust, C/C++, Java, Go, C#など様々な言語をWasmにコンパイルできる 👉言語非依存なスクリプト環境をUnity上に作れるのではないか

Slide 6

Slide 6 text

余談:ほかの選択肢 ● Lua ○ 組み込みが容易で、昔からゲーム向けスクリプト環境として人気 ○ 動的型付け、エディタ補完の弱さから敬遠 ● ビジュアルスクリプティング ○ これを作る前に3回ビジュアルスクリプティング環境を自作した 結論、なんだかんだでテキストは強い

Slide 7

Slide 7 text

WebAssemblyインタプリタを書く

Slide 8

Slide 8 text

① モジュールのデコード ● ヘッダ+セクションのシンプルな構成 ● LEB128 (整数の可変長バイナリ表現) が多用される ● 仕様見ながら地道に書く ヘッダ Type Section Import Section Function Section Table Section Memory Section Global Section Export Section Start Section Code Section Data Section

Slide 9

Slide 9 text

② 命令のデコード・挙動の実装 ● 数が多いのでデコーダは自動生成で実装 ● とりあえずWasm 1.0まで実装 ○ 2.0にはSIMDなど含まれる https://pengowray.github.io/wasm-ops/

Slide 10

Slide 10 text

③ VM ● ハーバードアーキテクチャ ○ 関数にはインデックスベースで飛ぶ ● スタックマシン ● ブロック単位の遷移 ● 型はi32, i64, f32, f64の4種類 ○ ※Wasm 1.0の場合 ● 独自機能としてプリエンプティブなグリーンスレッドをサポート ○ C#側の非同期関数をWasmから同期的に呼べる ○ 会話イベントなど作るうえで便利

Slide 11

Slide 11 text

④テスト ● 公式テストスイートがある

Slide 12

Slide 12 text

動いた!しかし…… ● 素のWebAssemblyはFFIがたいへんやりにくい! ● FFIでの引数・戻り値の型はi32, i64, f32, f64の4種類のみ ○ それ以外のデータは線形メモリを介して渡す必要がある ● 線形メモリを介した任意長データの渡し方が十分に定義されていない ○ 線形メモリ上のデータレイアウト、確保・解放の方法やタイミングなど ○ つまり標準的なABIがない ● 実用的には、FFI用のグルーコード生成が必要になる

Slide 13

Slide 13 text

その悩み、 WebAssembly Component Modelが 解決します

Slide 14

Slide 14 text

WebAssembly Component Model ● Wasmのハイレベルなinteroperabilityを実現するための仕様 ● 文字列、配列、構造体など複雑な値を扱うためのCanonical ABIがある ○ 引数や戻り値はCanonical ABIに従ってやりとりされる ● WASI preview 2以降はComponent Modelに則って定義される ● 将来的にはWasmコンポーネントをレジストリに公開して依存解決する、 みたいなのも目指している ○ wa.devというパッケージレジストリが既にあったりする

Slide 15

Slide 15 text

利用者から見たComponent Model ● WITという専用のDSLを使う ○ Wasmコンポーネントが公開する関数のシグネチャや、そこで使われる データ型を定義するもの

Slide 16

Slide 16 text

利用者から見たComponent Model ● WITから各言語向けのバインディングを自動生成する ● コンパイルしたWasmモジュールにメタデータを付与してコンポーネントに 変換する WIT ゲストソース (Rust, Go, …) バインディング 素のWasm Wasm Component

Slide 17

Slide 17 text

ランタイムから見たComponent Model ● Wasmコンポーネントは独自のバイナリ仕様を定義している ○ 素のWasmバイナリに様々なメタデータを付与する ○ つまり、新たにデコーダが必要 ● FFIでCanonical ABIを実装する必要がある ○ 配列や構造体、複雑な値をWasmの線形メモリにシリアライズしたり、 逆にデシリアライズしたり

Slide 18

Slide 18 text

Component Modelに対応できた! ● 文字列とかカジュアルに渡せるようになった ○ ゲームの会話イベントで必須なメッセージ本文の受け渡しなど活用範囲は広い ● Component Model 対応の詳しい話は記事に書いています ○ Component Model な Wasm ランタイムを作った https://zenn.dev/ruccho/articles/7aad0b660377ae

Slide 19

Slide 19 text

👈 Unity (C++) の上で動く C# の上で 動く Rust (Wasm)

Slide 20

Slide 20 text

Component Modelの普及状況 ● ゲスト言語向けのツーリングは充実しつつある ○ Rust, Go, C/C++, C#あたりは動く ○ moonbit (Wasmをメインターゲットとした新興言語) も注目 ● 実行環境のサポートはまだまだ ○ wasmtime ○ ブラウザはまだ非対応

Slide 21

Slide 21 text

WebAssemblyインタプリタを書く ~Component Modelを添えて~ るっちょ @ruccho_vector