読者です 読者をやめる 読者になる 読者になる

【WebAssembly】MODULARIZEオプションを使う

目次

  1. 関連記事
  2. MODULARIZEオプション
  3. HTMLからwasmを読み込む方法
  4. HTMLからwasmを読み込むサンプル
  5. まとめ

 

f:id:shogonir:20170514025514p:plain

1. 関連記事

 

 

2. MODULARIZEオプション

emccコマンドにMODULARIZEオプションをつけると生成されるファイルが変わります。
下記の記事(MODULARIZEオプション無し)と生成されるファイルを比較してみます。

【WebAssembly】Getting Started をDocker で試す - shogonir blog

 

GirHubにソースをあげました。

wasm-sample/01-wasm-modularize at master · shogonir/wasm-sample · GitHub

2.1. MODULARIZEオプション無しの時の成果物

emcc hello.c -s WASM=1 -o hello.html

生成されるファイルは xxx.wasm, xxx.js, xxx.html の三つです。

xxx.jsファイルの形式は下記の通り。

 

var Module;
if (!Module) Module = (typeof Module !== 'undefined' ? Module : null) || {};
...

 

2.2. MODULARIZEオプション有り

emcc hello.c -s WASM=1 -s "MODULARIZE=1" -o hello.js

生成されるファイルは xxx.wasm, xxx.js の二つです。(HTMLはありません)

xxx.js ファイルの形式は下記の通り。

 

var Module = function(Module) {
  Module = Module || {};
  var Module = Module;
  ...
  return Module;
};

 

2.3. オプションの有無による違い

まずHTMLが生成されるかが大きく違います。
wasmを使って何かを作る場合、自動生成されるHTMLを使う理由はほぼないでしょう。

 

また、生成されるJSファイルも少し異なります。
どちらもModuleという名前のオブジェクトに色々付け加えてwasmとのI/Fとする点は共通しています。
異なるのはMODULARIZEオプション有りの方はJSファイル全体が関数になっていることです。
これにより、wasmの読み込みをより明示的に行えるようになっています。

 

以上の二点から、個人的にはMODULARIZEオプションはほぼ必須であると思います。
さて、ではHTMLからどのようにwasm読み込みを行う方法を説明します。

 

 

3. HTMLからwasmを読み込む方法

まず自動生成されたJSファイルを読み込む必要があるので、下記のようなHTMLになります。

 

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8"/>
    <title>modularize</title>
  </head>
  <body>
    <script src="hello.js"></script>
  </body>
</html>

 

hello.jsを読み込んだ以降で、このJS内で実装されているModuleという名前の関数を呼び出すことで、wasmとのI/Fとなるオブジェクトを得ることができます。
このメソッドの引数は一つのオブジェクトで、このオブジェクトにいろんな情報を詰めることでwasm読み込みに干渉することができます。
本記事では二つの情報を渡してwasmを読み込みたいと思います。

 

3.1. wasmBinary

生成されたwasmファイルをUint8Arrayに変換して渡すことで、そのwasmを読み込んだModuleオブジェクトを得ることができます。

 

3.2. onRuntimeInitialized

wasmの読み込みが完了した際のコールバックを設定することができます。
wasmのmain()が実行される直前にこのコールバックが実行されます。
Moduleオブジェクトを通じてwasmにアクセスする際は、このコールバックもしくはmain()が実行された後である必要があります。
もしこの前にwasmにアクセスしようとすると下記のエラーが発生します。

 

Uncaught (in promise) abort("Assertion failed: you need to wait for the runtime to be ready (e.g. wait for main() to be called)") at Error

 

では上記を踏まえてwasmの読み込みを行うHTMLを書いてみましょう。

 

 

4. HTMLからwasmを読み込むサンプル

下記のようになります。

 

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8"/>
    <title>modularize</title>
  </head>
  <body>
    <script src="hello.js"></script>
    <script>

fetch('hello.wasm')
  .then(response => response.arrayBuffer())
  .then(buffer => new Uint8Array(buffer))
  .then(binary => {
    var moduleArgs = {
      wasmBinary: binary,
      onRuntimeInitialized: function () {
        console.log('on runtime initialized');
      }
    };
    var module = Module(moduleArgs);
  });

    </script>
  </body>
</html>

 

実行するとコンソールに2行出力されるはずです。

f:id:shogonir:20170516003604p:plain

 

5. まとめ

emccのMODULARIZEオプションを使ったサンプルを紹介しました。
これで独自のHTMLでもwasmを使えるようになりました。
注意として、Moduleオブジェクトを通じてwasmにアクセスする際には、wasmファイルの取得とその読み込みを待つ必要があることを説明しました。

 

また、このMODULARIZEを用いるとbabel, webpackなどとあわせてwasmを使うこともできます。
具体的には生成されたJSの末尾にexport default Module;を追記するのですが、詳しくは別の記事で解説したいと思います。