次の方法で共有


Unity のパフォーマンスに関する推奨事項

この記事は、Mixed Reality のパフォーマンスに関する推奨事項の内容が基になっていますが、Unity 固有の改善に焦点を当てています。

Microsoft は最近、HoloLens 2 アプリのパフォーマンス、設計、環境に関する一般的な問題と解決策を網羅した Quality Fundamentals というアプリケーションをリリースしました。 このアプリは、次の内容に関する優れたビジュアル デモとなっています。

Unity で Mixed Reality アプリのパフォーマンスを最適化する際に最も重要な初めのステップは、推奨される Unity の環境設定を使用しているかどうかを確認することです。 この記事には、パフォーマンスに優れた Mixed Reality アプリを構築するための最も重要なシーン構成に関するコンテンツが含まれています。 これらの推奨設定の一部は、以下でも強調して示されています。

Unity でプロファイルする方法

Unity に組み込まれている Unity プロファイラーは、特定のアプリに関する貴重なパフォーマンスの分析情報を収集するための優れたリソースです。 エディターでプロファイラーを実行できますが、これらのメトリックは実際のランタイム環境を表していないため、結果は慎重に使用する必要があります。 最も正確で実用的な分析情報を得るには、デバイスでアプリケーションを実行しながらリモートでアプリケーションをプロファイルすることをお勧めします。

Unity では、次のことに関する優れたドキュメントが提供されています。

  1. Unity プロファイラーを UWP アプリケーションにリモートで接続する方法
  2. Unity プロファイラーでパフォーマンスの問題を効果的に診断する方法

GPU プロファイル

Unity プロフィール

Unity プロファイラーを接続し、GPU プロファイラーを追加した後 (右上隅の「プロファイラーの追加」を参照)、プロファイラーの中央で CPU と GPU それぞれで費やされている時間を確認できます。 これにより、開発者は簡単に、アプリケーションが CPU バウンドか GPU バウンドかをだいたい確認できます。

Unity の CPU と GPU

Note

GPU プロファイリングを使用するには、Unity Player 設定グラフィックス ジョブを無効にする必要があります。 詳細については、Unity の GPU 使用状況プロファイラー モジュール を参照してください。

Unity のフレーム デバッガー

さらに、Unity の Frame デバッガー も、利用すると強力な分析情報ツールです。 これは、GPU が各フレームを実行している内容の概要を示します。 注意すべき点は、追加のレンダリング ターゲットと、それらの間でコピーする blit コマンドで、これらはHoloLensで非常に高価だからです。 HoloLensでは、画面外のレンダー ターゲットを使用しないのが理想的です。 これらは通常、高価なレンダリング機能 (MSAA、HDR、ブルームなどの全画面表示効果など) を有効にする場合に追加されますが、これは避ける必要があります。

HoloLensフレーム レート オーバーレイ

デバイス ポータル の [システム パフォーマンス] ページには、デバイスの CPU と GPU のパフォーマンスの詳細な概要が示されています。 [ヘッドセットの フレーム レート カウンターの表示][ヘッドセットのフレーム レート グラフの表示] を有効にすることができます。 これらのオプションを使用すると、FPS カウンターとグラフがそれぞれ有効になり、デバイスで実行中のアプリケーションですぐにフィードバックが得られます。

PIX

PIX は、Unity アプリケーションのプロファイルにも使用できます。 また、HoloLens 2の PIXの使用方法とインストール方法についても詳しく説明します。 開発ビルドでは、Unity のフレーム デバッガー に表示されるのと同じ検索範囲が PIX にも表示され、詳細に検査およびプロファイリングできます。

Note

Unity では、XRSettings.renderViewportScale プロパティを使用することで、ランタイムにアプリケーションのレンダー ターゲットの解像度を簡単に変更することができます。 デバイスに表示される最終的な画像の解像度は、固定されています。 プラットフォームでは、低解像度の出力がサンプリングされ、ディスプレイにレンダリングするための高解像度の画像が構築されます。

UnityEngine.XR.XRSettings.renderViewportScale = 0.7f;

CPU のパフォーマンスに関する推奨事項

以下では、特に Unity と C# の開発でのパフォーマンスに関する慣例についてさらに詳しく説明します。

参照をキャッシュする

初期化時に、関連するすべてのコンポーネントと GameObject への参照をキャッシュすることをお勧めします。これは、GetComponent<T>()Camera.main などの関数呼び出しを繰り返すことは、ポインターを格納するためのメモリ コストと比べてコストが高くなるためです。 . Camera.main では基になっている FindGameObjectsWithTag() が使用されるだけであり、そこで行われるシーン グラフでの "MainCamera" タグの付いたカメラ オブジェクトの検索にはコストがかかります。

using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour
{
    private Camera cam;
    private CustomComponent comp;

    void Start() 
    {
        cam = Camera.main;
        comp = GetComponent<CustomComponent>();
    }

    void Update()
    {
        // Good
        this.transform.position = cam.transform.position + cam.transform.forward * 10.0f;

        // Bad
        this.transform.position = Camera.main.transform.position + Camera.main.transform.forward * 10.0f;

        // Good
        comp.DoSomethingAwesome();

        // Bad
        GetComponent<CustomComponent>().DoSomethingAwesome();
    }
}

Note

GetComponent(string) を避ける
GetComponent() を使用と、いくつかの異なるオーバーロードが発生します。 常に、型ベースの実装を使用し、文字列ベースの検索オーバーロードは使用しないことが重要です。 シーンでの文字列による検索は、型での検索よりかなりコストがかかります。
(良い)Component GetComponent(Type 型)
(良い)T GetComponent<T>()
(正しくない) Component GetComponent(string)>

コストのかかる操作を避ける

  1. LINQ を使わないようにする

    LINQ はクリーンで、読み取りと書き込みが簡単である可能性はありますが、一般的にアルゴリズムを手動で記述した場合よりも多くの計算とメモリが必要になります。

    // Example Code
    using System.Linq;
    
    List<int> data = new List<int>();
    data.Any(x => x > 10);
    
    var result = from x in data
                 where x > 10
                 select x;
    
  2. 一般的な Unity API

    特定の Unity API は便利ですが、実行にかかるコストが高くなることがあります。 それらの多くには、シーン グラフ全体での、一致する GameObject のリストの検索が含まれます。 これらの操作は、一般に、参照をキャッシュするか、その GameObject に対するマネージャー コンポーネントを実装して実行時に参照を追跡することで回避できます。

        GameObject.SendMessage()
        GameObject.BroadcastMessage()
        UnityEngine.Object.Find()
        UnityEngine.Object.FindWithTag()
        UnityEngine.Object.FindObjectOfType()
        UnityEngine.Object.FindObjectsOfType()
        UnityEngine.Object.FindGameObjectsWithTag()
        UnityEngine.Object.FindGameObjectsWithTag()
    

Note

SendMessage()BroadcastMessage() は、絶対に除去する必要があります。 これらの関数は、関数を直接呼び出すより 1,000 倍も遅くなる可能性があります。

  1. ボックス化に注意する

    ボックス化は、C# 言語とランタイムの中核となる概念です。 これは、charintbool などの値型変数を参照型変数の中にラップするプロセスです。 値型変数が "ボックス化" されると、マネージド ヒープに格納される System.Object の内部にラップされます。 メモリが割り当てられ、最終的に破棄されるときは、ガベージ コレクターによって処理される必要があります。 これらの割り当てと割り当て解除は、パフォーマンス コストの原因になり、多くのシナリオでは、不要であるか、低コストの代替手段で簡単に置き換えることができます。

    ボックス化を回避するには、数値型と構造体 (Nullable<T> を含む) が格納される変数、フィールド、プロパティを、オブジェクトを使用するのではなく、intfloat?MyStruct などの特定の型として厳密に型指定します。 これらのオブジェクトをリストに配置する場合は、List<object>ArrayList ではなく、List<int> などの厳密に型指定されたリストを使用します。

    C# でのボックス化の例

    // boolean value type is boxed into object boxedMyVar on the heap
    bool myVar = true;
    object boxedMyVar = myVar;
    

コード パスの繰り返し

1 秒あたりまたはフレームごと、またはその両方で何回も実行される Unity コールバック関数 (つまり、Update) を繰り返す場合は、慎重に記述する必要があります。 ここでコストのかかる操作を行うと、パフォーマンスが常に大きな影響を受けるようになります。

  1. 空のコールバック関数

    以下のコードはアプリケーション内に残しても害がないように見えますが、特にすべての Unity スクリプトが Update メソッドで自動的に初期化されるため、これらの空のコールバックは高コストになる可能性があります。 Unity では、UnityEngine コードとアプリケーション コードの間で、アンマネージド コードとマネージド コードの間を行き来する動作が発生します。 実行するものがない場合であっても、このブリッジを越えるコンテキストの切り替えには大きなコストがかかります。 繰り返し呼び出される空の Unity コールバックを持つコンポーネントが含まれる GameObject がアプリに何百個もある場合、これは特に問題になります。

    void Update()
    {
    }
    

Note

Update() は、このパフォーマンスの問題の最も一般的な症状ですが、次のような他の繰り返し Unity コールバックも同様に悪い場合があります。FixedUpdate()、LateUpdate()、OnPostRender"、OnPreRender()、OnRenderImage()などです。

  1. フレームごとに 1 回実行したくなる操作

    次の Unity API は、多くのホログラフィック アプリで一般的な操作です。 常に可能であるとは限りませんが、これらの関数の結果は、1 回計算すれば、アプリケーション全体で特定のフレームに対して再利用できることがよくあります。

    a) 同じ Raycast 操作をコンポーネントごとに繰り返し行うのではなく、シーンに対する視線 Raycast を処理する専用のシングルトン クラスまたはサービスを用意し、他のすべてのシーン コンポーネントではこの結果を再利用することがよい方法です。 アプリケーションによっては、異なるオリジンからの Raycast、または異なる LayerMask に対する Raycast が必要になる場合があります。

        UnityEngine.Physics.Raycast()
        UnityEngine.Physics.RaycastAll()
    

    b) Update() などの繰り返される Unity コールバックでの GetComponent() 操作は、Start() または Awake() で参照をキャッシュすることによって回避します

        UnityEngine.Object.GetComponent()
    

    c) 可能であれば、初期化時にすべてのオブジェクトをインスタンス化し、オブジェクト プーリングを使用して、アプリケーションの実行時全体を通して GameObject を再利用するのがよい方法です

        UnityEngine.Object.Instantiate()
    
  2. インターフェイスと仮想コンストラクトを避ける

    直接オブジェクトではなくインターフェイスを介した関数の呼び出し、または仮想関数の呼び出しは、直接コンストラクトまたは直接関数呼び出しを使用するより、はるかにコストがかかることがよくあります。 仮想関数またはインターフェイスが不要な場合は、削除する必要があります。 ただし、開発の共同作業、コードの読みやすさ、コードの保守性を容易にするためにこれらの方法を使用する場合は、これらの方法によるパフォーマンスへの影響とトレードオフする価値があります。

    一般に、このメンバーを上書きする必要があると明確に予想される場合を除き、フィールドや関数を仮想としてマークしないことをお勧めします。 UpdateUI() メソッドなど、フレームごとに何回も呼び出される高頻度のコード パスは (フレームごとに 1 回であっても)、特に注意して使用する必要があります。

  3. 構造体を値で渡さないようにする

    クラスとは異なり、構造体は値型であり、関数に直接渡すと、その内容が新しく作成されるインスタンスにコピーされます。 このコピーにより、CPU コストが発生し、スタックにメモリが追加されます。 小さい構造体であれば、影響は最小限であるため、許容されます。 しかし、すべてのフレームで繰り返し呼び出される関数や、大きな構造体を受け取る関数の場合は、可能であれば、関数定義を変更して参照渡しにします。 詳細については、こちらを参照してください

その他

  1. 物理計算

    a) 一般に、物理計算を改善する最も簡単な方法は、物理計算にかける時間または 1 秒あたりの反復回数を制限することです。 これにより、シミュレーションの精度は低下します。 Unity の TimeManager を参照してください

    b) Unity でのコライダーの種類には、さまざまなパフォーマンス特性があります。 次に示すのは、コライダーをパフォーマンスが最もよいもの (左端) から最も悪いもの (右端) まで順に並べたものです。 メッシュ コライダーは、プリミティブ コライダーよりはるかにコストが高くなるため、避けることが重要です。

    スフィア < カプセル < ボックス <<< メッシュ (凸) < メッシュ (凸なし)

    詳細については、Unity の物理計算のベスト プラクティスに関するページを参照してください

  2. アニメーション

    Animator コンポーネントを無効にして、アイドル状態のアニメーションを無効にします (ゲーム オブジェクトを無効にしても同じ効果は得られません)。 同じものに値を設定するループ内にアニメーターを配置する設計パターンは避けます。 この方法にはかなりのオーバーヘッドがありますが、アプリケーションへの効果はありません。 こちらを参照してください。

  3. 複雑なアルゴリズム

    アプリケーションで、逆キネマティクスやパス ファインディングなどの複雑なアルゴリズムを使用している場合は、より簡単なアプローチを見つけたり、パフォーマンスに関連する設定を調整したりします

CPU と GPU のパフォーマンスに関する推奨事項

一般に、CPU と GPU のパフォーマンスは、グラフィックスカードに送信される描画呼び出しに行き着きます。 パフォーマンスを向上させるには、最適な結果が得られるように描画呼び出しを戦略的に a) 減らすか、または b) 再構築する必要があります。 描画呼び出し自体がリソースを集中的に使用するため、呼び出しを減らすと必要な作業全体が減ります。 さらに、描画呼び出し間の状態変化には、グラフィックス ドライバーでコストのかかる検証と変換のステップが必要であるため、アプリケーションの描画呼び出しを再構築して状態の変化を制限すると (つまり、異なる素材など) パフォーマンスが向上する可能性があります。

Unity には、それらのプラットフォームに対する描画呼び出しのバッチ処理に関する概要と詳細がわかる優れた記事があります。

単一パス インスタンス化レンダリング

Unity の単一パス インスタンス化レンダリングを使用すると、各視点に対する描画呼び出しを 1 つのインスタンス化描画呼び出しに減らすことができます。 2 つの描画呼び出し間のキャッシュの一貫性により、GPU のパフォーマンスもある程度向上します。

Unity プロジェクトでこの機能を有効にするには

  1. OpenXR 設定を開きます ([編集]>[プロジェクト設定]>[XR プラグイン管理]>[OpenXR])。
  2. [レンダリング モード] ドロップダウン メニューから [単一パス インスタンス化] を選択します。

このレンダリング方法の詳細については、Unity の次の記事をお読みください。

Note

単一パス インスタンス化レンダリングに関する一般的な問題の 1 つは、開発者がインスタンス化対応に作成されていない既存のカスタム シェーダーを持っている場合に発生します。 この機能を有効にした後、開発者は、一部の GameObject が 1 つの視線でしかレンダリングしないことに気付く場合があります。 これは、関連付けられたカスタム シェーダーに、インスタンス化のための適切なプロパティがないためです。

静的バッチ処理

Unity では、多くの静的オブジェクトをバッチ処理して、GPU への描画呼び出しを減らすことができます。 静的バッチ処理は、1) 同じ素材を共有し2) Static とマークされている、Unity のほとんどの Renderer オブジェクトで動作します (Unity でオブジェクトを選択し、インスペクターの右上にあるチェック ボックスをオンにします)。 Static とマークされている GameObject は、アプリケーションの実行時を通して移動できません。 そのため、すべてのオブジェクトを配置、移動、スケーリングする必要がある HoloLens では、静的バッチ処理を利用することが困難になる場合があります。イマーシブ ヘッドセットの場合、静的バッチ処理によって描画呼び出しが大幅に削減され、パフォーマンスが向上します。

詳細については、Unity の描画呼び出しのバッチ処理に関する記事の「静的バッチ処理」を参照してください。

動的バッチ処理

HoloLens の開発では Static としてオブジェクトをマークすることには問題であるため、動的バッチ処理が、この機能がないことを補う優れたツールになる可能性があります。 イマーシブ ヘッドセットでも同様に役立つことがあります。 ただし、GameObject が a) 同じ素材を共有しb) それ以外の条件の長い一覧を満たす必要があるため、Unity で動的バッチ処理を有効にすることは難しい場合があります。

詳細については、Unity の描画呼び出しのバッチ処理に関する記事の「動的バッチ処理」を参照してください。 ほとんどの場合、関連付けられたメッシュ データは 300 個より多くの頂点を持つことができないため、GameObject は動的バッチ処理が無効になります。

その他の手法

バッチ処理は、複数の GameObject が同じ素材を共有できる場合にのみ使用できます。 通常、これは、GameObject ではそれぞれの素材に対して固有のテクスチャを持つ必要があるため妨げられます。 複数のテクスチャを 1 つの大きなテクスチャに結合するのが一般的であり、これはテクスチャ アトラシングと呼ばれる手法です。

さらに、可能かつ妥当であれば、複数のメッシュを 1 つの GameObject に結合することをお勧めします。 Unity では、各レンダラーには関連する描画呼び出しがあり、結合されたメッシュは 1 つのレンダラーで送信されます。

Note

実行時に Renderer.material のプロパティを変更すると、素材のコピーが作成されるため、バッチ処理が損なわれる可能性があります。 GameObject 間で共有される素材のプロパティを変更するには、Renderer.sharedMaterial を使用します。

GPU のパフォーマンスに関する推奨事項

詳細については、Unity でのグラフィックス レンダリングの最適化に関する記事を参照してください

帯域幅とフィル レート

GPU 上でフレームをレンダリングする際、アプリケーションはメモリ帯域幅またはフィル レートのいずれかの制限を受けます。

  • メモリ帯域幅 は、GPU がメモリから実行できる読み取りと書き込みの速度です
    • Unity では、[Edit]\(編集\)>[Project Settings]\(プロジェクト設定\)>[Quality Settings]\(品質設定\)[Texture Quality]\(テクスチャ品質\) を変更できます。
  • フィル レート とは、GPU が 1 秒間に描画できるピクセル数のことです。

深度バッファーの共有を最適化する

深度バッファーの共有を有効にしてホログラムの安定性を最適化することをお勧めします。 この設定を使用して深度ベースの遅延ステージ再投影を有効にする場合は、24 ビット深度形式ではなく、16 ビット深度形式を選択することをお勧めします。 16 ビット深度バッファーを使用すると、深度バッファー トラフィックに関連付けられた帯域幅 (したがって電力) が大幅に減少します。 これにより、電力削減とパフォーマンスの両面で大きな改善が見込めます。 ただし、"16 ビット深度形式" を使用すると、2 つの悪影響が発生する可能性があります。

Z ファイティング

深度範囲の忠実性を低くすると、24 ビットより 16 ビットの方が Z ファイティングが発生しやすくなります。 これらのアーティファクトを回避するには、Unity カメラのニア/ファー クリップ プレーンを変更して、低い精度に対応します。 HoloLens ベースのアプリケーションでは、Unity の既定の 1000 m ではなく、50 m のファー クリップ プレーンにすることで、一般に Z ファイティングを除去できます。

ステンシル バッファーの無効化

Unity で 16 ビット深度のレンダリング テクスチャが作成されるときは、ステンシル バッファーは作成されません。 Unity のドキュメントによると、24 ビット深度形式を選択した場合は、24 ビットの z バッファーとともに 8 ビットのステンシル バッファーが作成されます (デバイスで 32 ビットが適用されている場合 (HoloLens など)、これが一般的です)。

全画面効果を避ける

全画面に対して動作する手法は、すべてのフレームで数百万の操作が行われるため、コストが高くなる可能性があります。 アンチエイリアシングやブルームなどのポストプロセッシング エフェクトを回避することをお勧めします。

最適な照明設定

Unity でリアルタイム グローバル イルミネーションを使用すると、極めて優れたビジュアル結果を得ることができますが、高コストのライティングの計算が含まれます。 [Window]\(ウィンドウ\)>[Rendering]\(レンダリング\)>[Lighting Settings]\(ライティング設定\)[Real-time Global Illumination]\(リアルタイム グローバル イルミネーション\) をオフにすることで、Unity のすべてのシーン ファイルでリアルタイム グローバル イルミネーションを無効にすることをお勧めします。

また、シャドウ キャストも、Unity のシーンに高コストの GPU パスが追加されるため、すべて無効にすることをお勧めします。 シャドウはライトごとに無効にできますが、品質設定を使用することでまとめて制御することもできます。

[Edit]\(編集\)>[Project Settings]\(プロジェクト設定\)[Quality]\(品質\) カテゴリを選択し、UWP プラットフォームに対して [Low Quality]\(低品質\) を選択します。 また、[Shadows]\(シャドウ\) プロパティを [Disable Shadows]\(シャドウを無効にする\) に設定するだけでもかまいません。

Unity のモデルでは、ベイク済みライティングを使用することをお勧めします。

多角形の数を減らす

ポリゴン数は次のようにすると減ります

  1. シーンからのオブジェクトの削除
  2. アセットを間引く (特定のメッシュのポリゴン数が減ります)
  3. 詳細レベル (LOD) システムをアプリケーションに実装する (遠く離れたオブジェクトが同じジオメトリのローポリ バージョンでレンダリングされます)

Unity でのシェーダーについて

パフォーマンスでのシェーダーの比較を近似する簡単な方法は、実行時に各操作が実行される平均操作数を特定することです。 Unity ではこれを簡単に行うことができます。

  1. シェーダー アセットをまたはマテリアルを選択した後、インスペクター ウィンドウの右上隅にある歯車アイコンを選択して、[Select Shader]\(シェーダーの選択\) を選択します

    Unity でシェーダーを選択する

  2. シェーダー アセットを選択した状態で、インスペクター ウィンドウの下の [Compile and show code]\(コードのコンパイルと表示\) ボタンをクリックします

    Unity でシェーダー コードをコンパイルする

  3. コンパイル後、頂点シェーダーとピクセル シェーダーの両方について、異なる操作の数に関する結果の統計セクションを確認します (注: ピクセル シェーダーはフラグメント シェーダーとも呼ばれます)

    Unity での標準シェーダー操作

ピクセル シェーダーを最適化する

上記の方法を使用してコンパイル済みの統計結果を調べると、平均してフラグメント シェーダーでは一般に頂点シェーダーより多くの操作が実行されます。 ピクセル シェーダーとも呼ばれるフラグメント シェーダーは、画面出力のピクセルごとに実行されます。一方、頂点シェーダーは、画面に描画されるすべてのメッシュの頂点ごとにだけ実行されます。

したがって、すべての照明計算のために、フラグメント シェーダーには頂点シェーダーより多くの命令があるだけでなく、ほとんどの場合、フラグメント シェーダーは大きなデータセットに対して実行されます。 たとえば、画面出力が 2k x 2k の画像の場合、フラグメント シェーダーは 2,000 * 2,000 = 400 万回実行される可能性があります。 2 つの視線をレンダリングする場合、2 つの画面があるため、この数は 2 倍になります。 複合現実アプリケーションに複数のパス、全画面の後処理効果、または同じピクセルへの複数のメッシュのレンダリングがある場合は、この数が大幅に増加します。

したがって、フラグメント シェーダー内の操作の数を減らすと、通常、頂点シェーダーでの最適化より、はるかに大幅にパフォーマンスが向上します。

Unity 標準シェーダーの代替手段

物理ベースのレンダリング (PBR) や他の高品質シェーダーを使用する代わりに、より高パフォーマンスで低コストのシェーダーを利用することを検討します。 Mixed Reality Toolkit では、複合現実プロジェクト用に最適化された MRTK 標準シェーダーが提供されています。

Unity には、Unity 標準シェーダーより高速な、unlit、vertex lit、diffuse、およびその他単純化されたシェーダー オプションも用意されています。 詳細については、「組み込みシェーダーの使用とパフォーマンス」を参照してください。

シェーダーのプリロード

シェーダーの読み込み時間を最適化するには、シェーダーのプリロードや他のテクニックを使用します。 特に、シェーダーのプリロードは、シェーダーの実行時コンパイルによる遅延が発生しないことを意味します。

オーバードローを制限する

Unity では、シーン ビューの左上隅にある描画モード メニューを切り替えて [Overdraw]\(オーバードロー\) を選択することにより、シーンのオーバードローを表示できます。

一般に、GPU に送られる前にオブジェクトを事前にカリングすることで、オーバードローを軽減できます。 Unity では、エンジンにオクルージョン カリングを実装する詳しい方法が提供されています。

メモリに関する推奨事項

過剰なメモリ割り当て操作と割り当て解除操作により、ホログラフィック アプリケーションに悪影響があり、その結果、一定しないパフォーマンス、フレームの凍結、その他の有害な動作が発生する可能性があります。 メモリ管理はガベージ コレクターによって制御されるため、Unity で開発するときは、メモリに関する考慮事項を理解することが特に重要です。

ガベージ コレクション

ホログラフィック アプリでは、ガベージ コレクター (GC) がアクティブ化されて、実行中にスコープ外になったオブジェクトを分析し、そのメモリを解放して再利用できるようにする必要がある場合、GC のために処理計算時間が失われます。 通常、継続的に割り当てと割り当て解除を行うと、より頻繁にガベージ コレクターを実行する必要があるため、パフォーマンスとユーザー エクスペリエンスが低下します。

Unity では、ガベージ コレクターの詳細なしくみと、メモリ管理に関してより効率的なコードを記述するためのヒントが説明された、優れたページが提供されています。

ガベージ コレクションが過剰になる最も一般的な状況の 1 つは、Unity の開発においてコンポーネントとクラスへの参照をキャッシュしないことです。 Start() または Awake() の間にすべての参照をキャプチャし、後の Update() や LateUpdate() などの関数で再利用する必要があります。

その他のクイックヒント:

  • C# の StringBuilder クラスを使用して、実行時に複雑な文字列を動的に構築します
  • アプリのすべてのビルド バージョンで実行されるため、Debug.Log() の呼び出しは不要になったら削除します
  • ホログラフィック アプリで一般に多くのメモリが必要な場合は、読み込み中や移行中の画面を表示するときなど、読み込みフェーズの間に、System.GC.Collect() を呼び出すことを検討します

オブジェクトプール

オブジェクト プーリングは、継続的なオブジェクトの割り当てと割り当て解除のコストを削減するためによく使用される手法です。 これを行うには、同一のオブジェクトの大規模なプールを割り当て、時間の経過と共にオブジェクトを絶えず生成して破棄するのではなく、このプールの非アクティブで使用可能なインスタンスを再利用します。 オブジェクト プールは、アプリの間の有効期間が一定ではない再使用可能なコンポーネントに最適です。

起動時のパフォーマンス

比較的小さなシーンでアプリを開始してから、SceneManager.LoadSceneAsync を使用して残りのシーンを読み込むことを検討してください。 これにより、可能な限り速くアプリを対話状態にすることができます。 新しいシーンをアクティブ化するときは大きな CPU スパイクが発生する可能性があること、およびレンダリングされるコンテンツが途切れたり停止したりする場合があります。 これを回避する方法の 1 つは、読み込まれるシーンで AsyncOperation.allowSceneActivation プロパティを "false" に設定し、シーンが読み込まれるまで待った後、画面を黒にクリアしてから、"true" に戻してシーンのアクティブ化を完了することです。

開始シーンが読み込まれている間、ユーザーにはホログラフィック スプラッシュ画面が表示されることに注意してください。

関連項目