Version: 2021.1
言語: 日本語
例 - ビルボード平面の作成
テクスチャ

テクスチャとメッシュデータのロード

Unity は、同期と非同期の 2 種類の方法で、テクスチャやメッシュのデータをディスクから読み込み GPU にアップロードします。この 2 つの処理は “同期アップロードパイプライン” と “非同期アップロードパイプライン” と呼ばれます。

Unity が同期型アップロードパイプラインを使用すると、データをロードしてアップロードしている間は、他のタスクを実行できません。これにより、アプリケーションに目に見える一時停止が発生することがあります。Unity が非同期アップロードパイプラインを使用すると、バックグラウンドでデータのロードとアップロードを行いながら、他のタスクを実行できます。

テクスチャやメッシュに対して非同期アップロードパイプラインを使用できる場合、Unity は自動的に非同期アップロードパイプラインを使用します。テクスチャやメッシュに非同期アップロードパイプラインを使用できない場合、Unity は自動的に同期アップロードパイプラインを使用します。

仕組み

同期アップロードパイプラインと非同期アップロードパイプラインの主な違いは、ビルド時に Unity がデータを保存する場所で、それがランタイムに Unity がデータを読み込む方法に影響します。

同期アップロードパイプラインでは、Unity は、1 つのフレームで、テクスチャやメッシュのメタデータ (ヘッダーデータ) と、テクセル/頂点データ (バイナリデータ) の両方を読み込む必要があります。非同期アップロードパイプラインでは、Unity は 1 つのフレームでヘッダーデータのみをロードする必要があり、その後のフレームでバイナリデータを GPU にストリーミングすることができます。

同期型アップロードパイプラインでは、以下が行われます。

  • ビルド時に、Unity はメッシュやテクスチャのヘッダーとバイナリデータの両方を 1 つの .res ファイルに書き込みます。
  • ランタイムに、アプリケーションがテクスチャやメッシュを必要とするとき、Unity はそのテクスチャやメッシュのヘッダーデータとバイナリデータの両方を .res ファイルからメモリにロードします。すべてのデータがメモリに入ると、Unity はバイナリデータをメモリから GPU にアップロードします。ロードとアップロードの作業は、主にメインスレッドで、1 フレーム内で行われます。

非同期のアップロードパイプラインでは、以下が行われます。

  • ビルド時に、Unity はヘッダーデータを .res ファイルに、バイナリデータを別の .res ファイルに書き込みます。
  • ランタイムに、アプリケーションがテクスチャやメッシュを必要とすると、Unity は .res ファイルからヘッダーをメモリにロードします。ヘッダーがメモリに入ったら、Unity は、サイズを固定したリングバッファを使って、.res ファイルから GPU にバイナリデータをストリーミングします。Unity は、複数のスレッドを使って数フレームに渡って、バイナリデータをストリーミングします。なお、Unity がすでに GPU ハードウェアを知っている一部のコンソールプラットフォームでは、リングバッファをスキップして直接 GPU メモリにロードします。

テクスチャとメッシュデータの適格性

テクスチャは、以下の条件を満たすと、非同期アップロードパイプラインの対象となります。

  • テクスチャは読み取り/書き込み可能ではない。
  • テクスチャは Resources フォルダーに入っていない。
  • ビルドターゲットが Android の場合、プロジェクトのビルド設定で LZ4 圧縮が有効になっている。

なお、LoadImage(byte[] data) を使用してテクスチャをロードする場合、上記の条件が満たされていても、強制的に同期アップロードパイプラインが使用されます。

メッシュは、以下の条件を満たすと、非同期アップロードパイプラインの対象となります。

  • メッシュは読み取り/書き込み可能ではない。
  • メッシュは Resources フォルダーに入っていない。
  • メッシュは BlendShape ではない。
  • メッシュが動的バッチ処理に不適格であるか、動的バッチ処理が無効になっているため、Unity がメッシュに動的バッチ処理を適用していない。動的バッチ処理の詳細については、ドローコールバッチング を参照してください。
  • メッシュの頂点/インデックスデータが、パーティクルシステム、Terrain (地形)、メッシュコライダーで必要とされていない。
  • メッシュに ボーンウェイト がない。
  • メッシュ トポロジークワッド ではない。
  • メッシュアセットの meshCompressionOff に設定されている。 ビルドターゲットが Android の場合、プロジェクトのビルド設定で LZ4 圧縮が有効になっている。

それ以外の状況では、Unity はテクスチャとメッシュを同期してロードします。

Unity が使用するアップロードパイプラインを特定する方法

Profiler または他のプロファイリングツールを使用して、スレッドのアクティビティとプロファイラーのマーカーを観察することによって、Unity が非同期アップロードパイプラインを使用していることを特定できます。

以下は、Unity がテクスチャやメッシュのアップロードに非同期アップロードパイプラインを使用していることを示しています。

  • AsyncUploadManager.ScheduleAsyncReadAsyncReadManager.ReadFileAsync.DirectTextureLoadBegin プロファイラーマーカー。
  • AsyncRead スレッドでのアクティビティ。

このアクティビティが表示されない場合、Unity は非同期アップロードパイプラインを使用していません。

なお、以下のプロファイラーマーカーは、Unity が非同期アップロードパイプラインを使用していることを示すものではありません。これらは、非同期アップロード作業が必要かどうかをチェックするために呼び出されます。

  • Initialization.AsyncUploadTimeSlicedUpdate
  • AsyncUploadManager.AsyncResourceUpload
  • AsyncUploadManager.ScheduleAsyncCommands

非同期アップロードパイプラインの設定

非同期アップロードパイプラインの設定を行うことができます。なお、同期アップロードパイプラインの設定はできません。

Quality 設定の Async Upload 設定
Quality 設定の Async Upload 設定

非同期アップロードバッファ

Unity では、1 つのリングバッファを再利用して、テクスチャやメッシュデータを GPU にストリーミングします。これにより、必要なメモリ割り当ての数を減らすことができます。

Async Upload Buffer は、このリングバッファのサイズをメガバイト単位で決定します。最小サイズは 2 で、最大サイズは 512 です。

Unity は、現在ロードしている最大のテクスチャやメッシュに合わせて、自動的にバッファのサイズを変更します。これは時間のかかる操作です。例えば、デフォルトのバッファサイズより大きいテクスチャを多数ロードしている場合など、Unity が複数回実行しなければならない場合は特に時間がかかります。Unity がバッファサイズを変更しなければならない回数を減らすために、この値を、ロードすると予想される最大の値に合わせて設定してください。これは通常、シーン内の最大のテクスチャの値です。

この値は、Quality settings ウィンドウか、QualitySettings.asyncUploadBufferSize API を使用して設定できます。

非同期アップロードタイムスライス

Async Upload Time Slice は、CPU が GPU にテクスチャやメッシュデータをアップロードするのに費やす時間を、フレームあたりのミリ秒で表したものです。

値が大きいほど、データが GPU 上ですぐに準備できることを意味しますが、CPU はそれらのフレームの間、アップロード操作に多くの時間を費やします。Unity がこの時間をアップロードに使うのは、GPU へのアップロードを待っているデータがバッファにある場合だけです。待機中のデータがなければ、Unity はこの時間を他の操作に使うことができます。

この値は、Quality settings ウィンドウで設定するか、QualitySettings.asyncUploadTimeSlice API を使用して設定できます。

その他の情報

テクスチャやメッシュデータの非同期アップロードの詳細については、Unity ブログの Async Upload Pipeline (AUP) でローディングのパフォーマンスを最適化する を参照してください。

例 - ビルボード平面の作成
テクスチャ