Version: 2022.3
言語: 日本語
Emscripten 用 WebGL ネイティブプラグイン
WebGLのキャッシュ動作

Unity WebGL のメモリ

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

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

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

ノート: WebGL のメモリに関するセキュリティリスクについては、セキュリティとメモリリソース を参照してください。

Unity WebGL のメモリ使用量

Unity WebGL コンテンツの以下の領域では、ブラウザーが大量のメモリを割り当てる必要があります。

Unity のヒープ

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

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

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

デフォルトのオプションは、すべてのデスクトップのユースケースでうまく機能するように設定されています。しかし、モバイルブラウザーの場合は、詳細なチューニングオプションを使用する必要があります。モバイルブラウザーの場合、アプリケーションの典型的なヒープ使用量に合わせて Initial Memory Size を設定することが推奨されます。

アセットデータ

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

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

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

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

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

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

ガベージコレクター

ガベージコレクションとは、使われていないメモリを探し出して解放するプロセスです。ガベージコレクターは、未使用のメモリを収集すると、Unity のヒープ内で再割り当てします。次に、ガベージコレクターはスタックを検査し、ロードされたオブジェクト参照を登録します。ガベージコレクターは、参照されなくなったオブジェクトを見つけると、そのオブジェクトによって使用されていたメモリを解放します。

Unity のガベージコレクションの仕組みの概要については、自動メモリ管理 を参照してください。WebGL のガベージコレクションは、スタックが空のときに実行されます。スタックは Unity のヒープの一部ですが、ヒープそのものではありません。JavaScript ではデバッグできないため、ガベージコレクターはスタックが空の場合に WebGL でのみ実行されます。これは現在、各フレーム後に 1 回発生します。

他のほとんどのプラットフォームでは、ガベージコレクション処理 が異なり、コレクターはスタックを検査できるように実行中のすべてのスレッドを一時停止します。Unity プロファイラー を使用してガベージコレクション処理をデバッグできます。

ガベージコレクターはメインスレッドで実行されます。つまり、実行時間が長いループがある場合、次のコードを WebGL で実行すると失敗する可能性があります。これは、コレクターがループの繰り返しの間にガベージコレクターを実行する機会がないためです。これは、ガベージコレクターが中間文字列オブジェクトが使用するメモリを解放できず、Unity ヒープ内のメモリが不足してしまうことを意味します。

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

その他の参考資料

Emscripten 用 WebGL ネイティブプラグイン
WebGLのキャッシュ動作