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できるってことかな。

 完成品

https://mith-mmk.github.io/wasm/sample.html

#プログラミング #Rust #WebAssembly

この記事が気に入ったらサポートをしてみませんか?