RustでWebAssembly 2022年2月版
日本語の資料が古いのでどハマりだよ。英語版の資料を見よう。過去とWebAssemblyの開発環境が大きく変わっているので、作り直した。コードをCからrustに書き換えないと。
セットアップ
npmはLatestでないとエラーが出る可能性があるので、ディストリビューションのNodeJSではなく最新版のLTSをインストールすること。wasmだけを使うなら入れなくても大丈夫かも知れない。
以下はUbuntu(Ubuntu 20.04 on WSL)の場合
# Install Rust
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
source $HOME/.cargo/envrustup update
# Install Wasm pack
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
#Install pkg-config because cargo-generate require it.
sudo apt install pkg-config -y
# Install Cargo generate
cargo install cargo-generate
# npm with nodejs LTS (Ubuntu) see https://github.com/nodesource/distributions
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodej
sudo npm install npm@latest -g
テンプレートを作成
今回は、小さめなプロジェクトでカスタマイズしやすいwasm-pack-templateを導入。プロジェクト名を聞いてくるので、プロジェクト名を入力すると同名のフォルダを作成し、そこにテンプレートを作成する(Hello rust!だけど)
ここからどハマり……。
cargo generate --git https://github.com/rustwasm/wasm-pack-template.git
#Input <procject name>
cd <project name>
ビルド
プロジェクトをビルドする。ターゲットをWebにしないとブラウザがロード出来ない(エラーで弾かれる。ここで一時間ぐらい嵌まった)
wasm-pack build -t web
ローダーを作成する。日本語版にあったのは古いバージョンの方法で上手くロードできなかった。ロード方法に変更があったらしいので、英語のURLを参照した方が良い(そもそもWebAssemblyが動かない環境は、モジュール使えないので問題無い)
なお、実行ファイルはpkgの下に出来る。
テスト
index.htmlを作成。hello_wasm.jsをプロジェクト名.jsに変える様に。
https://developer.mozilla.org/en-US/docs/WebAssembly/Rust_to_wasm
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>hello-wasm example</title>
</head>
<body>
<script type="module">
// hello_wasm.jsをプロジェクト名.js に変える
import init, {greet} from "./pkg/hello_wasm.js";
init()
.then(() => {
greet();
});
</script>
</body>
</html>
テストWeb Serverはpythonで十分(3.8以降なら大丈夫らしい)
python3 -m http.server 8000
ブラウザで表示させて、alertが表示されれば環境構築終わり(表示されない場合は、エラーが出ているので開発者ツールでチェック)
ここまで出来たらコーディング。rust思い出さないと……。
コーディング
ここから先はどハマりの巻。rust使ったの大分前なのでほとんど忘れた……。クレートの作り方とか完全に忘れている。
このプログラム四角を描くだけだけど、ここから派生できるから。
一番のポイントは共有メモリのセッティング。Vecを使うのが良いらしい。しかし、Vecはサイズに合わせてメモリの破棄と取得を繰り返すため、最初に最大値を取得して変更しては行けない(大きさを変えるとポインタが変わるはず)
let buffer = (0..buffersize) .map(|_| {0}) .collect();
どこぞからパクってきた初期化方法を採用。0から始まるbuffersizeのRangeを作成し、そこに0を埋めて、型変換するらしい(暗黙のVec型)
何も表示されないのでサンプルを見ながら修正。どうもimportの設定がおかしかったらしい。なんかサンプルと違うけどPromiseで返り値wasmをgetして、wasm.memory.bufferを見ているところか……。動いているけどwasm.memory.bufferを見ないと……。動作は間違っていないや。
苦節三時間、共有メモリーが動いた。それでも表示がおかしい……。縦に潰れている。offsetを計算するときwidthを4倍するの忘れていた。
最新のサンプルを見るとrust内でマルチスレッドコーディング出来るらしい。タイマー管理や画像更新もrustでwrapできるってことかな。
完成品