Unity を検索

WebAssembly が WebGL ビルドのスタンダードに!

2018年8月15日 カテゴリ: Engine & platform | 8 分 で読めます
取り上げているトピック
シェア

Is this article helpful for you?

Thank you for your feedback!

Unity WebGL ビルドターゲットの出力形式をWebAssembly(Wasm)に切り替える計画を本ブログで発表してから、かなりの年月が経ちました。この変更が Unity 2018.2 で実装されます。そこで本記事では、これまでの経緯と、この変更がインタラクティブウェブコンテンツを制作する皆様にとって何を意味するのかをご説明したいと思います。

Wasm 導入に至った経緯

WebAssembly への対応は Unity 5.6 で試験的に実装されました。これは WebAssembly が主要な 4 種のデスクトップブラウザーで利用可能になったのとほぼ同時期です。以来 Unity およびこれらのブラウザーに各種改良とバグ修正が加えられると同時に、これを導入するユーザーが増加し、多くの肯定的なフィードバックが寄せられました。これを受け、次のステップとして、満を持して正式対応に踏み切ることになりました ― Unity 2018.1 では WebAssembly は実験的な機能ではなくなり、Wasm 単独でのビルド作成機能が実装されました。

そしてバージョン 2018.2 でついにデフォルトの Linker Target が asm.js から Wasm に切り替わりました。

したがって、Unity 2018 で Unity WebGL ビルドターゲット用にパブリッシュする場合の LTS ストリームのデフォルトは Wasm となります。

これは、Unity が長期にわたって目標として来た重要なマイルストーンです。これを行うに当たっては、いくつかの条件を満たす必要がありました。具体的には Unity の実装とブラウザー対応を安定させることと、内部でのテストカバレッジに Wasm を含めることで、そのためには Emscripten のアップグレードとコード中のいくつかの問題の修正も必要でした。

今日では Wasm のバリエーションがすべてのテストスイート用に揃っているので、メインラインにマージされる変更に関してはすべて WebAssembly に対するテストが行われています。

(注)asm.js テストスイートも引き続き維持および実行されますが、メインラインに反映されるすべての変更は Wasm に対してテストされるようになっています。

WebAssembly と asm.js

ここで、WebAssembly に関する詳細と、asm.js との主な相違点を見ていきましょう。

asm.js と比較すると Wasm のほうが速く、サイズが小さく、メモリ効率が高くなります。これらはすべて Unity WebGL エクスポートにおいて課題となる要素です。Wasm で既知の問題がすべて解決されるとは限りませんが、プラットフォームを全面的に向上させることは確かです。ただし、パフォーマンスはブラウザーの実装によって変わりますのでご注意ください。主要なブラウザーのベンダーがすべて、WebAssembly への対応と改善を計画していることは、朗報だと思われます。

Wasm ファイルを開くと、これが(テキストである asm.js とは異なり)バイナリファイルであることがすぐ分かるでしょう。コードの配信方法としては、こちらのほうがよりコンパクトである一方、デバッグ用に読み出したり変更を加えたりすることは不可能となります。

Wasm が独自の命令セットを持つのに対し、asm.js は「高度に最適化可能な」JavaScript のサブセットです。WebAssembly は、開発ビルドではより精密な算術演算のエラー検知を提供し、ゼロ除算や大きな浮動小数点数から整数への四捨五入などの例外を投げることができます。開発ビルドでない場合、このような算術エラーの検知はマスクされるので、ユーザー体験には影響を及ぼしません。

コードサイズ

Unity では WebAssembly の生成用に(IL2CPPemscriptenbinaryen に基づく)複雑なツールチェインを提供しており、これが C/C++ および C# コードを WebAssembly に変換します。これによりバイナリファイル(.wasm.code.unityweb)が作成され、結果としてコードサイズが asm.js より小さくなります。

これに対し、開発ビルドの場合、コードサイズは数十 MB 小さくなり、開発ビルドでない場合は数百 KB 小さくなります。大まかな基準としては、空のプロジェクトのコードサイズは最大 12%(3D 物理演算が含まれる場合は最大 18%)小さくなります。

(注)これは、不要なパッケージやシェーダー内でビルドされた要素をすべて除いて、圧縮形式は Brotli を用いて計測されています。

すべての改良(パフォーマンス、メモリ、ロード時間)について言えることですが、効果の度合いはプロジェクトによって異なります。

メモリ

asm.js を使用した場合に生じる制約のひとつは、Unity ヒープ のサイズ制限でした。Unity ヒープのサイズはビルド時に指定する必要があり、変更は不可能でした。WebAssembly では Unity ヒープのサイズがランタイムで増加可能であるため、Unity コンテンツの使用メモリが、開始時に指定されたヒープサイズを超過できます。

つまり、以前は不可能だった「小さなヒープ(例えば 32MB)でコンテンツを開始し、必要に応じてサイズを増加させる」ということが可能となりました。

memorySize の値が、コンテンツ開始時の初期サイズであると考えてください。これはバージョン 2018.2 で搭載された機能ですので現在利用可能です。ただし asm.js もターゲットにしている場合は、ヒープのサイズを変更できないため、このアプローチは不可能です。

ヒープのサイズが増加し過ぎるとブラウザーがメモリ不足になる可能性があることにご注意ください。この限界はブラウザーによって異なります。すべてのブラウザーで挙動を一貫させるには、Unity ヒープの最大サイズを設定してください。これは、エディタースクリプト内で Emscripten に引数 "-s WASM_MEM_MAX=" を設定することで行えます(以下の例をご覧ください)。

PlayerSettings.WebGL.emscriptenArgs = "-s WASM_MEM_MAX=512MB";

最大メモリサイズは 2032 で、これを超えるとブラウザーでランタイムエラーが発生します。

また、ロード時のメモリ効率は Wasm のほうが高くなります。したがって、多くのユーザーが asm.js で(特に 32 ビットブラウザーで)直面するメモリ不足の問題が削減されます。

Unity WebGL でメモリが機能する仕組みについてはこちらのブログ記事をご覧ください。

パフォーマンス

Wasm と asm.js のパフォーマンスの差はブラウザーによって異なります。バイナリ形式である Wasm は、JavaScript テキストファイルとして解析される asm.js より格段に速く読み込みを行える可能性を持っています。

これに加え、すでにコンパイル済の wasm コードモジュールは IndexedDB キャッシュ内に保管することができるので、同じコンテンツをリロードする場合は、起動が非常に速くなります。Wasm キャッシングを利用するには、Data Caching のオプションを有効にしてください。

起動後の実行速度は、JavaScript エンジン内ですでに asm.js スタイルのコード用に最適化してあるブラウザー上で asm.js を実行した場合に匹敵します。これまで asm.js を認識していなかったブラウザー上で Wasm を実行する場合は格段に速度が上がるはずです。

コードによっては、64 ビット整数演算などの(asm.js が固有の命令を持たない)一部の命令は、Wasm のほうが速くなる場合があります。

マルチスレッディング

WebAssembly マルチスレッディングへの対応は、恐らく最も待ち望まれていた機能であり、パフォーマンスを最も向上させるものです。これは今年、より早い段階でブラウザーに提供される予定でしたが、この実現に必要な要素のひとつである SharedArrayBuffer への対応を(Spectre と Meltdown に起因した安全性に関わる懸念のために)無効化する必要がありました。ありがたいことに、SAB の再有効化を可能にするためのブラウザーの安全対策がこの数か月間で整いつつあり、将来のバージョンでは提供できる兆しが見えています。

Unity 側ではその時に備えて準備を整えておきたいので Wasm マルチスレッディング対応に積極的に取り組んでいます。これは、まずこの数か月のうちに試験的機能として内部的なネイティブスレッド(C# スレッドは未対応)限定で公開されます。「内部的な」というのは、スキニング、アニメーション、カリング、AI 経路探索やその他のサブシステム用のジョブスレッドのことを意味しています。これらは最初のうちはすべて有効にはならないかもしれませんが、長期的には、出来る限りマルチスレッディングの利点を活用することを目標としています。

デバッグ

デバッグは常に asm.js の課題でした。残念なことに、これは WebAssembly でもまだ改善されていません。各種ブラウザーはデベロッパーツールスイートで WebAssembly デバッグの提供を開始していますが、それらのデバッガーはまだ Unity3D のコンテンツのサイズにうまく適合しません。幸い、Wasm は「オープンでデバッグ可能な」形に設計されているため、将来的には各種ブラウザーから、これを可能にするより良いツールが提供されることが期待できます。差し当たっては、他のデバッギング技術をご使用ください(以下参照)。

  • 往々にして、Unity WebGL ビルドの問題は、ビルドされたゲームがブラウザー API とインタラクトするレイヤー内に発生します。このインタラクションサーフェスは UnityLoader.js と .asm/wasm.framework.unityweb の中にあります。ここには、容易に読み出し可能でブラウザー内蔵のデベロッパーツールで直ちにデバッグできる JavaScript コードが含まれています。
  • C# コードのデバッグ用には Debug.Log() が唯一の選択肢であることが多いので、可能な場合は他のプラットフォームでデバッグを行うことを強くお勧めします。
  • 高度なデバッグの場合は、生成された asm.js コンテンツを console.log() でアノテートできるようにするために、asm.js へのエクスポートをお試しください。

また、バージョン 2018.2 では IL2CPP によるマネージドコードデバッグへの対応が追加されました。WebAssembly マルチスレッディングの対応が実装され次第、このテストを開始する予定です。

今後の予定

ブラウザーのベンダーは WebAssembly への対応をより強化していく計画です。各ベンダーは MVP(実用最小限の製品・Minimum Viable Product) の公開以来、起動時間とパフォーマンスを向上させる新機能の開発と最適化に取り組んできました。以下はそのいくつかの例です。

  • Wasm の非同期インスタンス化(Unity で対応済み)
  • 基準と階層コンパイル ― インスタンス化を高速化します(Unity コンテンツの実行時に自動的に対応)
  • ストリーミングによるインスタンス化 ― Wasm コードをダウンロード中にコンパイルします。(Unity での対応を検討中)
  • マルチスレッディング(Unity で対応のための作業中)

(注)ブラウザーによっては上記の機能の一部はすでに実装済みです。将来実装予定の各機能とその現状はこちらのページ(英語)でご確認いただけます。

私達は WebAssembly を非常に信頼していますので、デベロッパーの皆様には WebAssembly をデフォルトとして使用されることをお勧めします。必要であれば古いブラウザー用に asm.js をラインタイムのフォールバックとして保持することも可能です。これを行うには WebGL のプレイヤー設定で WebGLLinkerTarget.Both を選択してください。

asm.js はバージョン 2018.3 をもって廃止する予定となっています。したがって今後 asm.js には、Wasm 向けの改良(マルチスレッディング、SIMD など)は一切追加されません。ただし、バージョン 2018 の 長期サポート(LTS)ストリーム では引き続き利用可能です。これは今年末のリリース日から 2 年間正式対応となります。

特定のブラウザーが WebAssembly に対応しているかどうかを確認したい場合はこちらをご覧ください。

次回のブログ記事では、様々な側面から各種ブラウザーを比較していきます。本記事に関する皆様からのフィードバックもお待ちしております。

2018年8月15日 カテゴリ: Engine & platform | 8 分 で読めます

Is this article helpful for you?

Thank you for your feedback!

取り上げているトピック