Version: 2020.1
言語: 日本語
WebGL のパフォーマンスについて
WebGL ビルドのデバッグとトラブルシューティング

WebGL のメモリ

Unity WebGLではメモリの制約により、実行できるコンテンツの複雑さが制限されることがあります。

WebGL コンテンツはブラウザー上で動作します。ブラウザーは、アプリケーションがコンテンツを実行するために必要なメモリを、そのメモリスペースに確保します。 利用可能なメモリの量は、以下の条件によって異なります。

  • 使用するデバイス
  • 使用する OS
  • 使用するブラウザーと、それが 32 プロセッサと 64 プロセッサーのどちらで動作するか
  • ブラウザーの JavaScript エンジンがコードを解析するのに必要なメモリ量
  • ブラウザーがタブごとに別々のプロセスを使用するかどうか、コンテンツが他のすべての開いているタブとメモリスペースを共有する必要があるかどうか。

Unity WebGL コンテンツがブラウザーに著しく大きなメモリの割り当てを必要とするエリアが、複数あります。

Unity のヒープ

Unity はメモリヒープを使用して、すべての Unity エンジンランタイムオブジェクトを格納します。これには、マネージオブジェクト、ネイティブオブジェクト、ロードされたアセット、シーン、シェーダーなどが含まれます。これは、他のプラットフォームで Unity プレイヤーが使用するメモリのようなものです。

Unity のヒープは、割り当てられたメモリの連続したブロックです。Unity は、アプリケーションのニーズに合わせて、ヒープの自動サイズ変更をサポートしています。ヒープサイズは、アプリケーションが実行すると拡張され、最大 2GB まで拡張できます。Unity はこのメモリヒープを Memory オブジェクト として作成します。Memory オブジェクトの buffer プロパティはサイズ変更可能な ArrayBuffer で、WebAssembly コードがアクセスするメモリの生のバイトを保持します。

ヒープの自動リサイズは、ブラウザーがアドレス空間の連続したメモリブロックの割り当てに失敗すると、アプリケーションがクラッシュする原因となります。このような理由から Unity のヒープサイズをできるだけ小さくしておくことが重要です。したがって、アプリケーションのメモリ使用量を計画する際には注意が必要です。Unity のヒープサイズをテストしたい場合は、プロファイラー を使って、メモリブロックの内容をプロファイルすることができます。

アセットデータ

Unity WebGL ビルドを作成すると、Unity は .data ファイルを生成します。このファイルには、アプリケーションの起動に必要なシーンとアセットがすべて含まれています。Unity WebGL は実際のファイルシステムにアクセスできないため、仮想メモリのファイルシステムを作成し、ブラウザーはこの .data ファイルをここで解凍します。Emscipten フレームワーク (JavaScript) は、このメモリファイルシステムをブラウザーのメモリ空間に確保します。コンテンツが実行されている間、ブラウザーのメモリは解凍されたデータを維持します。ダウンロード時間とメモリ使用量の両方を低く抑えるために、この圧縮されていないデータをできるだけ小さくするようにしてください。

メモリ使用量を削減するために、アセットデータを AssetBundle (アセットバンドル) にまとめることができます。アセットバンドルを使うと、アセットのダウンロードを完全に制御することが可能になります。つまり、アプリケーションがアセットをダウンロードするタイミングや、ランタイムがアセットをアンロードするタイミングを制御できます。未使用のアセットをアンロードすると、メモリが解放されます。

AssetBundles は、Unity のヒープに直接ダウンロードします。そのため、ブラウザーが余分な割り当てをすることはありません。

データキャッシング を有効にして、ユーザーのマシンにコンテンツのアセットデータをキャッシュします。これにより、後の実行時にそのデータを再ダウンロードする必要がなくなります。Unity WebGL ローダーは、IndexedDB API を使用してデータキャッシングを実装しています。このオプションにより、ブラウザーがネイティブにキャッシュできないサイズのファイルをキャッシュすることができます。

データキャッシングは、ブラウザーがアプリケーションデータをユーザーのマシンに保存することを可能にします。ブラウザーでは、しばしば、キャッシュに保存できる量やキャッシュできる最大ファイルサイズが制限されます。これでは、アプリケーションがスムーズに動作するためには十分ではありません。そこで Unity WebGL ローダーでは、IndexedDB API を用いてデータキャッシングを実装しています。ブラウザーのキャッシュにコンテンツを保存するのではなく、Unity は IndexedDB にデータを保存します。

データキャッシングオプションを有効にするには、File > Build Settings > Player Settings > Publishing Settings の順に移動します。

Large-Allocation Http ヘッダー

サーバーはコンテンツの Large-Allocation http ヘッダーを生成することがあります。これはサポートされているブラウザー (現在 Mozilla のみ) にメモリが必要としているものを伝えます。この情報によって、対応するブラウザーが分割されていないメモリスペースで新しい処理を発生させたり、大きなメモリ割り当てが成功するように雑多な作業が可能になります。 こうすることにより、特に 32 ビットブラウザーで Unity ヒープを割り当てようとするときに、ブラウザーがメモリ不足になる問題を解決できます。

ガベージコレクションへの配慮

ガベージコレクションとは、使われていないメモリを探し出して解放するプロセスです。ガベージコレクターは、未使用のメモリを収集すると、Unity のヒープ内で再割り当てします。

For an overview on how Unity garbage collection works, see Automatic Memory Management. WebGL garbage collection runs when the stack is empty. The stack is a part of the Unity heap but not the heap itself. This usually occurs after every frame. This is different from the garbage collection process on other platforms, where the collector pauses all running threads so it can inspect the stack. This is not possible in JavaScript. You can debug the garbage collection process using the Unity Profiler.

他のほとんどのプラットフォームでは、ガベージコレクションは、実行中のすべてのスレッドを一時停止します。ガベージコレクターは、それらのスレッドのスタックを検査し、ロードされたオブジェクト参照を登録します。これは、現在のところ、JavaScript では不可能です。このため、WebGL では、スタックが空になった場合にのみ、ガベージコレクタが実行されます。これは現在、各フレームの後に一度だけ発生します。

このため、下のコードは WebGL での実行に失敗します。これは、ループの繰り返しの間に、ガベージコレクターを実行する機会がないためです。これは、ガベージコレクタが中間文字列オブジェクトが使用するメモリを解放できないことを意味し、Unity のヒープのメモリを使い果たしてしまう可能性があります。

string hugeString = "";
 
for (int i = 0; i < 100000; i++)
 
{
 
  hugeString += "foo";
 
}

2020.1 で導入された WebGL ローダーの更新

WebGL のパフォーマンスについて
WebGL ビルドのデバッグとトラブルシューティング