言語学習課題としてRustをここ最近勉強しているのですが、そろそろ何かしら大きめの実装をしてみたいなという思いからWebAssemblyに手を出しはじめました。
せっかくなので、WebAssemblyが活躍しそうなアニメーション処理を題材にしています。 まずは、実装の感触をつかんでみようという思いから、ボールが四方に飛び交うかんたんなアニメーションを実装しました。
導入
Rust
Rustは2015年に1.0版がリリースされた、比較的新しいプログラミング言語です。
C, C++言語の代替となるシステムプログラミング言語を目指して開発された言語で、C, C++で問題視されていたメモリの安全性(ヌルポインタ参照、多重フリー等)をコンパイルタイムで確保することができるメモリ管理の仕組みを独自に採用しています。
ガベージコレクションを使用せずにメモリの管理ができ、C,C++に匹敵する実行速度を実現しつつ、平行プログラミングも安全にできるらしいです。(やったことはないけれど)
Microsoft社がOS開発にRustを採用していたり、stackoverflowの調査で最も愛されている言語とされているなど、注目を集めている言語です。(“Most loved, dreaded, and wanted” の項目)
WebAssembly
WebAssemblyはブラウザ上で実行可能かつ、ネイティブに近いパフォーマンスで動作するバイナリ形式のアセンブリ風言語です。
モダンブラウザでは結構サポートされているようなので、WEBアプリケーション開発の際に採用を検討できそうですね。
基本的には、C, C++, Rust, Goなどの言語をコンパイルすることでWebAssemblyのコードを作成し、それをJavascriptから実行することで動作させます。
ちなみに、導入にあたっては全てのJSコードをWebAssemblyで置換する必要はなく、今回の実装のように部分的に採用することも可能です。
フロントエンド開発において必須のJavascriptですが、インタプリタ言語で動的型付けなので実行速度等でパフォーマンスが悪いことが懸念されます。 そこで、処理が重くなる実装の一部をWebAssemblyで置換することにより、高速で動作することが期待されます。
Rust + WebAssembly
wasm-packというパッケージを使用することで、RustのコードからWebAssemblyのコードを生成することができます。
wasm-pack build
を実行してRustコードをビルドすると .wasm という拡張子のバイナリファイルが作成されるので、JSからはそれを実行するようになります。
また、バイナリと同階層にTypescript対応の型定義ファイルも生成されるため、フロントエンド実装時も型のサポートを受けながらコーディングすることが可能となっています。
ちなみに、まだまだ発展途中ではありそうですが、Rustでフロントエンドをすべて書いてしまおうというプロジェクトも存在するようです。
WebGL
概要
WebGLはHTML5で追加された<canvas>
要素から使用可能なJavascriptAPIです。
WEBページ上でOpenGL ES(OpenGL for Embedded Systems)相当の画面描画処理を実行することができ、2/3次元のグラフィックをレンダリングすることができます。
既存のJavaScriptのライブラリでも、Three.jsやBabylon.jsなどWebGLを手軽に利用することができるようになるものが提供されています。
シェーダー
WebGLのコードは、JavaScriptで制御するコードとGPUで実行するためのシェーダーコードで構成されます。
シェーダーには「頂点シェーダー」と「フラグメントシェーダー」の2種類があり、各シェーダーをコンパイルしてWebGLの処理の際に利用します。
開発環境
ディレクトリ構成
デフォルトのViteの構成の中にwasm-packで生成された構成が入り込んでいる感じです。
(wasm以下がwasm-packで生成されたファイル群)
プロジェクトの構築
今回使用している開発環境の構築手順です。 実行環境としては以下を想定しております。
バージョン | |
cargo | cargo 1.61.0 |
cargo | v16.13.2 |
wasm-pack | wasm-pack 0.10.2 |
ViteでReactプロジェクトの作成
yarn create vite
cd {プロジェクト名}
yarn (install)
wasm-packでWebAssembly用プロジェクトの作成
cargo generate --git https://github.com/rustwasm/wasm-pack-template
ビルド
wasm-pack build --target web
yarn dev
コードのハイライト
JsValue
JsValueというのはJavaScriptのオブジェクトをRustで表現するための型です。
以下の画像では、init_gl
関数でJavaScriptのオブジェクトを引数として受け取り、serde
パッケージを利用してデシリアライズして使用しています。
利用する側(JavaScriptのアプリケーション側)はこんな感じでインスタンス化、メソッドの実行などをおこなえます。
WebGLContextの取り扱い
wasm-packを利用すると、DOMの操作がRustから実行できるようになります。
WebGLContextの扱い、DOM操作系の処理いずれも、JavaScriptとインターフェースがそろっているのでJavaScript↔置換も難しくない印象でした。
Rust版 シェーダーのコンパイル処理
JS版 シェーダーのコンパイル処理
Rust版 DOM操作系処理
まとめ
- Rust+WebAssemblyを使用することでJSでは実現できなかった処理の高速化・効率化が可能になるかも?
- JavaScript→Rustへのコード置換だけであれば以外とハードルは低い。
今回はRust+WebAssembly+WebGLを使用して、シンプルなアニメーション処理を実装しました。
個人的には、弾幕ゲームのシミュレーションみたいのを作ってみたいと思ったのですが、まだまだ勉強が必要そうです🤔