Rust+Wasm+WebGLはじめました 環境構築=>Hello World =>画面クリアまで(Windows10)

投稿日:

Rustというプログラム言語が面白そうなので使い始めてみました。Rustから直接WebAssemblyコードを出力できるので、実行環境はWebブラウザに、グラフィック処理はWebGLを使用します。Webブラウザ上で3Dゲームが作成できるようになることが目標です。それとWebプログラム初心者なので、npm、wasm-bindgenなどのツールやバインダーは使用せず、基本的に手作業で作成していきます。というか使い方がよくわかってないだけですが…… 

今回行うこと Rustの環境作成&ビルド、ブラウザ上で実行、Rust⇔JavaScript間の関数呼び出し、WebGL導入まで

 

インストール 環境構築

Windows10でのインストール手順になります。

rustup-init.exe

Windows用のインストーラーです。

https://rustup.rs/ ここからrustup-init.exeをダウンロード

実行して表示される内容(英語)に従ってインストールを行います。そのまま  1) Proceed with installation (default)  を選択すると%USERPROFILE%(C:\Users\ユーザー名)にインストールされます。詳しいことは、https://github.com/rust-lang/rustup.rsに書かれています。Windows10の場合 default host triple: x86_64-pc-windows-msvc になっていると思うので、VS2013以降または、Visual C++ Build Tools 2019のインストールが必要になります。※wasmのコンパイルのみであればインストール不要かもしれない

指定した場所にインストールしたい場合は、インストール前に環境変数(RUSTUP_HOMEとCARGO_HOME)を登録することで可能になります。環境変数がない場合 C:\Users\ユーザー名 に.cargoと.rustupというフォルダが作成されそこにインストールされます。

CARGO_HOME 実行ファイルのインストール場所
RUSTUP_HOME rustup関連のファイル
WebAssemblyターゲットの追加

コマンドプロンプトまたはPowerShellで

> rustup target add wasm32-unknown-unknown

を実行するとRustからWebAssemblyのバイナリーコードに変換できるようになります。


インストールされる主なツール

Rust関連ツールの利用はPowerShell、またはコマンドプロンプトから行います。

rustup

新しいツールのインストールや既存ツールのアップデート、アンインストールを行うツールです。

> rustup --version         バージョン確認

> rustup update アップデート

> rustup self uninstall アンインストール
rustc

Rustコンパイラですが直接使用することはあまりないと思います。

cargo

Rustのビルドシステム兼パッケージマネージャです。これでプロジェクトの作成、管理、ビルドを行います。crates.ioで公開されているライブラリ(Rustではクレート) は、設定ファイル(Cargo.toml)に記入すると、自動的にダウンロード&ビルドしてくれます。そのほかにもローカルファイルのパス指定、gitリポジトリ指定もできます。

使用例> cargo --version
> cargo update
> cargo new project_name
> cargo build --target ターゲット名 --release

Visual Stusio Codeのインストール

※必須ではありません。ソースコードエディタとWebサーバーの一例です。

詳しい説明は不要だと思います。ソースコードの編集、PowerShellターミナルでビルドの実行や作業の自動化などができます。

Rust(rls)とLive Server

Rustソースコードの色付け、補間、事前のエラー表示をしてくれる追加機能が便利です。Extention:Marketplaceでrustを検索すると色々出てきます。その中のRust(rls)を使用しています。
それと簡易ローカルサーバーを起動できるLive Serverが便利です。ボタン1つでWebサーバーが立ち上がり、ファイル更新を監視してブラウザを自動的に更新してくれます。


Hello World

準備が整ったので、実際にコーディングしていきます。

プロジェクト作成

プロジェクトを作成したい場所でcargoの新規作成コマンドを実行します。実行ファイルを作成するプロジェクトではないので–libを指定。

> cargo new hello_world --lib

hello_worldフォルダが作成され、いくつかの初期ファイルが生成されます。それとGitリポジトリも作成されます。

新規作成時のファイル構成hello_world
| .gitignore
| Cargo.toml
|
+---.git
| | Gitリポジトリ 中身は省略
|
\---src
lib.rs
build.cmd、index.htmlとindex.jsの追加

cargoにはビルド後にファイルコピーなどの処理ができない様なのでバッチコマンドでcargo実行とファイルコピーを行います。それとwwwフォルダにブラウザに表示させるためのhtmlとJavaScriptを追加します。これであとはコードを書いてビルドすれば完了です。

最終的なファイル構成 Git関連は省略hello_world
| Cargo.toml
| build.cmd
|
+---src
| lib.rs
|
\---www
index.html
index.js

コーディング

用意したファイルに書き込んでいきます。

cargo設定ファイル

wasm実行バイナリーを出力するために[lib]の項目を追加しています。その他はプロジェクト作成時に生成されたものです。

Cargo.toml
[package] name = "hello_world" version = "0.1.0" authors = ["namae"] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] # *.rlib ではなく *.wasmを出力する crate-type = ["cdylib"] [dependencies] # メモ # ローカルファイルのクレート指定 # mycreate = { path = "../lib/mylib" } # gitリポジトリ (たぶん、まだ試してない) # mycreate = { git = 'リポジトリの場所' }
ビルド用バッチファイル

少し調べたのですが、cargoだけではビルド後出力された実行ファイルを別の場所にコピーできない様なので、バッチコマンドで対応します。hello_world.wasmがビルドによって出力されるwasmバイナリーです。copyコマンドでWebサーバーのルートとなるwwwフォルダへコピーします。–releaseを消すとdebugビルド。

build.cmdcargo build --target=wasm32-unknown-unknown --release
copy target\wasm32-unknown-unknown\release\hello_world.wasm www\

html

ブラウザに表示させるためのindex.htmlです。JavaScriptの指定とWebGL用のキャンバスを定義します。

www/index.html<!DOCTYPE html>
<html>
  <head>
    <title>Hello Rust + wasm + WebGL!</title>
  </head>
  <body>
    <canvas id="canvas" width="320" height="240"></canvas>
    <script src="./index.js" type="module"></script>
  </body>
</html>
Rust

JavaScriptから呼ばれる関数を定義します。呼ばれた関数はJavaScript側の関数を呼び出して終了です。

src/lib.rs// JavaScript側の関数
extern "C" {
    fn hello_js();
}

//JavaScriptから呼ばれる関数
#[no_mangle]
fn hello_rust() {
    //JavaScriptの関数呼び出しは危険を伴うのでunsafe
    unsafe {    
        hello_js();
    }
}
JavaScript
最後にJavaScriptです。fetch命令を使ってwasmバイナリーの読み込んでいます。wasmの準備が終了したらrust側のhello_rust()を呼び出し、rust側ではhello_js()を呼び出しているので、WebGLで画面のクリアとアラート表示が実行されます。
www/index.js// rust(wasm)から呼ばれる関数
// WebAssembly.instantiate()へ渡す
const imports = {
    env: {
        hello_js : hello_js,   //rust側の関数名 : jsの関数
    },
}

// wasm読み込み
fetch('./hello_world.wasm')
    .then(response => response.arrayBuffer())
    .then(bytes => WebAssembly.instantiate(bytes, imports))
    .then(main);

function main(results)
{
    var wasm = results.instance;

    // Rust(wasm)の関数呼び出し
    wasm.exports.hello_rust();   
}

function hello_js()
{
    var canvas = document.getElementById('canvas');
    var gl = canvas.getContext('webgl');

    gl.clearColor(0.6, 0.8, 0.9, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.finish();

    alert("Hello World!");

    // 終了処理 16回リロードで発生するwarning対策 for Firefox
    //  リロードしたときにWebGLContextが破棄されない?
    window.addEventListener('beforeunload', function(e){
        gl.getExtension('WEBGL_lose_context').loseContext();
    });    
}

ビルド&実行

準備が整ったのでビルドを行います。build.cmdを実行してビルドが成功すると以下のような文字が出力されます。

// hello_world(build.cmdとCargo.tomlがある)フォルダで
> ./build

hello_world>cargo build --target=wasm32-unknown-unknown --release 
   Compiling hello_world v0.1.0 (hello_world)
    Finished release [optimized] target(s) in 7.04s 

hello_world>copy target\wasm32-unknown-unknown\release\hello_world.wasm www\ 
        1 個のファイルをコピーしました。
>
Webサーバー起動(Visual Studio Code)

hello_world/wwwフォルダをVSCodeで開き、Live Serverがインストールされていれば右下に◎GoLiveが表示され、それをクリックするとWebサーバーが立ち上がります。ブラウザも同時に起動します。デフォルトのアドレスはhttp://127.0.0.1:5500/、ポート番号はGo Liveがあったところに表示されています。

ブラウザでキャンバスの部分の色が変わり、Hello Worldのアラートが表示されれば成功です。ブラウザやサーバーによってアラート表示の後にクリアされます。

ここで実行結果を確認できます。(hello_world/www/)
□hello-rust-wasm-webgl

動作確認
Firefox 68.02(64bit)
Google Chrome 76.0.3809.132(Official Build) (64 ビット)
Microsoft Edge 44.18362.267.0 Microsoft EdgeHTML 18.18362


wasmコード削減

環境によって変わるかもしれませんが、出力されたwasmファイルは、デバッグ情報などが含まれているため簡単なコードにかかわらず1.4MBほどになります。大きいです。いくつかコード削減ツールがありますが、その中のwasm-gcを紹介します。

ツールのインストール

cargoのinstallコマンドを使います。

 > cargo install --git 'https://github.com/alexcrichton/wasm-gc'
使い方
 > wasm-gc wasmファイル -o 出力ファイル

build.cmdのcopyコマンドの代わりにwasm-gcを使用。1.4MBから242byteに削減されました。

build.cmd> cargo build --target=wasm32-unknown-unknown --release
> wasm-gc  target\wasm32-unknown-unknown\release\hello_world.wasm -o www\hello_world.wasm

まとめ

必要最低限のツールでのRust、Wasm、WebGLプログラムの開発環境を整えてみました。必須なのはRustツールチェインのみです。ここから必要に応じてパッケージングツールやWebAPIのバインダーの利用を考えます。しばらくは勉強のために、できるだけ手作業で進めていきます。

今回作成したWebページ(hello_world/www/フォルダ)
□hello-rust-wasm-webgl

>> Rust(wasm)とJavaScript(WebGL)のデータ受け渡し Rust+WebGLでポリゴン描画 (1/2)

コメントを残す

メールアドレスが公開されることはありません。

認証:数字を入力してください(必須) * Time limit is exhausted. Please reload CAPTCHA.