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

この記事は、Mixed Reality のパフォーマンスに関する推奨事項についての記事の内容が基になっていますが、Unity エンジン環境に固有の学習に焦点を当てています。This article builds on the discussion outlined in performance recommendations for mixed reality but focuses on learnings specific to the Unity engine environment.

Unity で Mixed Reality アプリのパフォーマンスを最適化する際に最も重要な初めのステップは、推奨される Unity の環境設定を使用しているかどうかを確認することです。The most important first step when optimizing performance of mixed reality apps in Unity is to be sure you are using the recommended environment settings for Unity. この記事には、パフォーマンスに優れた Mixed Reality アプリを構築するための最も重要なシーン構成に関するコンテンツが含まれています。That article contains content with some of the most important scene configurations for building performant Mixed Reality apps. これらの推奨設定の一部は、以下でも強調して示されています。Some of these recommended settings are highlighted below, as well.

Unity でプロファイルする方法How to profile with Unity

Unity に組み込まれている Unity プロファイラー は、特定のアプリに関する貴重なパフォーマンスの分析情報を収集するための優れたリソースです。Unity provides the Unity Profiler built-in, which is a great resource to gather valuable performance insights for your particular app. エディターでプロファイラーを実行できますが、これらのメトリックは実際のランタイム環境を表していないため、その結果は慎重に使用する必要があります。Although one can run the profiler in-editor, these metrics do not represent the true runtime environment and thus, results from this should be used cautiously. 最も正確で実用的な分析情報を得るには、デバイスでアプリケーションを実行しながらリモートでプロファイルすることをお勧めします。It is recommended to remotely profile your application while running on device for most accurate and actionable insights. さらに、Unity の Frame Debugger も、利用すると非常に強力な分析情報ツールです。Further, Unity's Frame Debugger is also a very powerful and insight tool to utilize.

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

  1. Unity プロファイラーを UWP アプリケーションにリモートで接続する方法How to connect the Unity profiler to UWP applications remotely
  2. Unity プロファイラーでパフォーマンスの問題を効果的に診断する方法How to effectively diagnose performance problems with the Unity Profiler

注意

Unity プロファイラーを接続し、GPU プロファイラーを追加した後 (右上隅の「 プロファイラーの追加 」を参照)、プロファイラーの中央で CPU と GPU それぞれで費やされている時間を確認できます。With the Unity Profiler connected and after adding the GPU profiler (see Add Profiler in top right corner), one can see how much time is being spent on the CPU & GPU respectively in the middle of the profiler. これにより、開発者は簡単に、アプリケーションが CPU バウンドか GPU バウンドかをだいたい確認できます。This allows the developer to get a quick approximation if their application is CPU or GPU bounded.

Unity の CPU と GPU

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

以下では、特に Unity と C# の開発でのパフォーマンスに関する慣例についてさらに詳しく説明します。The content below covers more in-depth performance practices, especially targeted for Unity & C# development.

参照をキャッシュするCache references

初期化時に、関連するすべてのコンポーネントと GameObject への参照をキャッシュするのがベスト プラクティスです。It is best practice to cache references to all relevant components and GameObjects at initialization. これは、 GetComponent<T>() のような関数呼び出しを繰り返すと、ポインターを格納するためのコストの方がメモリ コストよりかなり大きくなるためです。This is because repeating function calls such as GetComponent<T>() are significantly more expensive relative to the memory cost to store a pointer. これはまた、非常によく使用される Camera.main にも当てはまります。This also applies to to the very regularly used Camera.main. Camera.main では実際には基になっている FindGameObjectsWithTag() が使用されるだけであり、そこで行われるシーン グラフでの "MainCamera" タグの付いたカメラ オブジェクトの検索にはコストがかかります。Camera.main actually just uses FindGameObjectsWithTag() underneath, which expensively searches your scene graph for a camera object with the "MainCamera" tag.

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();
    }
}

注意

GetComponent(string) を使用しないAvoid GetComponent(string)
GetComponent() を使用と、いくつかの異なるオーバーロードが発生します。When using GetComponent() , there are a handful of different overloads. 常に、型ベースの実装を使用し、文字列ベースの検索オーバーロードは使用しないことが重要です。It is important to always use the Type-based implementations and never the string-based searching overload. シーンでの文字列による検索は、型での検索よりかなりコストがかかります。Searching by string in your scene is significantly more costly than searching by Type.
(正しい) Component GetComponent(Type type)(Good) Component GetComponent(Type type)
(正しい) T GetComponent<T>()(Good) T GetComponent<T>()
(正しくない) Component GetComponent(string)>(Bad) Component GetComponent(string)>

コストのかかる操作を避けるAvoid expensive operations

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

    LINQ は非常にすっきりしていて、読みことも書くことも簡単な場合がありますが、一般に、アルゴリズムを手作業で記述するより、計算と、特にメモリの割り当てが、はるかに多く必要になります。Although LINQ can be very clean and easy to read and write, it generally requires much more computation and particularly more memory allocation than writing the algorithm out manually.

    // 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 APICommon Unity APIs

    特定の Unity API は便利ですが、実行コストが非常に大きくなることがあります。Certain Unity APIs, although useful, can be very expensive to execute. それらの多くには、シーン グラフ全体での、一致する GameObject のリストの検索が含まれます。Most of these involve searching your entire scene graph for some matching list of GameObjects. これらの操作は、一般に、参照をキャッシュするか、問題のある GameObject に対するマネージャー コンポーネントを実装して実行時に参照を追跡することで回避できます。These operations can generally be avoided by caching references or implementing a manager component for the GameObjects in question to track the references at runtime.

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

注意

SendMessage()BroadcastMessage() は、絶対に除去する必要があります。SendMessage() and BroadcastMessage() should be eliminated at all costs. これらの関数は、関数を直接呼び出すより 1,000 倍も遅くなる可能性があります。These functions can be on the order of 1000x slower than direct function calls.

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

    ボックス化は、C# 言語とランタイムの中核となる概念です。Boxing is a core concept of the C# language and runtime. これは、char、int、bool などの値型変数を参照型変数の中にラップするプロセスです。It is the process of wrapping value-typed variables such as char, int, bool, etc. into reference-typed variables. 値型変数が "ボックス化" されると、マネージド ヒープに格納される System.Object の内部にラップされます。When a value-typed variable is "boxed", it is wrapped inside of a System.Object which is stored on the managed heap. したがって、メモリが割り当てられ、最終的に破棄されるときは、ガベージ コレクターによって処理される必要があります。Thus, memory is allocated and eventually when disposed must be processed by the garbage collector. これらの割り当てと割り当て解除は、パフォーマンス コストの原因になり、多くのシナリオでは、不要であるか、低コストの代替手段で簡単に置き換えることができます。These allocations and deallocations incur a performance cost and in many scenarios are unnecessary or can be easily replaced by a less expensive alternative.

    開発でのボックス化の最も一般的な形式の 1 つは、null 許容値型の使用です。One of the most common forms of boxing in development is the use of nullable value types. 関数で値型に対して null を返すことができるようにするのはよくあることであり、値を取得しようとして操作が失敗する可能性がある場合は特にそうです。It is common to want to be able to return null for a value type in a function, especially when the operation may fail trying to get the value. この方法で発生する可能性のある問題は、ヒープで割り当てを行ったため、後でガベージ コレクションが必要になることです。The potential problem with this approach is that allocation now occurs on the heap and consequently needs to be garbage collected later.

    C# でのボックス化の例Example of boxing in C#

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

    null 許容値型を使用したことで問題のあるボックス化の例Example of problematic boxing via nullable value types

    このコードは、Unity プロジェクトで作成する可能性のあるダミー パーティクル クラスを示したものです。This code demonstrates a dummy particle class that one may create in a Unity project. TryGetSpeed() の呼び出しにより、後でガベージ コレクションが必要になるオブジェクトの割り当てがヒープで行われます。A call to TryGetSpeed() will cause object allocation on the heap which will need to be garbage collected at a later point in time. この例の場合、1000 個またはそれよりずっと多くのパーティクルがシーン内にあり、それぞれに対して現在の速度が要求されるため、特に問題になります。This example is particularly problematic as there may be 1000+ or many more particles in a scene, each being asked for their current speed. したがって、何千個ものオブジェクトが割り当てられ、結果としてすべてのフレームが割り当て解除されるため、パフォーマンスが大幅に低下します。Thus, 1000's of objects would be allocated and consequently de-allocated every frame, which would greatly diminish performance. エラーが発生したことを示す -1 などの負の値を返すように関数を書き直すと、この問題は回避され、メモリがスタックに保持されます。Re-writing the function to return a negative value such as -1 to indicate a failure would avoid this issue and keep memory on the stack.

        public class MyParticle
        {
            // Example of function returning nullable value type
            public int? TryGetSpeed()
            {
                // Returns current speed int value or null if fails
            }
        }
    

コード パスの繰り返しRepeating code paths

繰り返し実行される Unity コールバック関数 (Any repeating Unity callback functions (i.e Update など) で、1 秒間またはフレームごとの実行回数多いものは、非常に慎重に記述する必要があります。Update) that are executed many times per second and/or frame should be written very carefully. ここでコストのかかる操作を行うと、パフォーマンスが常に大きな影響を受けるようになります。Any expensive operations here will have huge and consistent impact on performance.

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

    次のようなコードはアプリケーション内に残しても害がないように見えますが、特にすべての Unity スクリプトがこのコード ブロックで自動的に初期化するため、これらの空のコールバックは実際には非常に高コストになる可能性があります。Although the code below may seem innocent to leave in your application, especially since every Unity script auto-initializes with this code block, these empty callbacks can actually become very expensive. Unity では、UnityEngine コードとアプリケーション コードの間で、アンマネージド コードとマネージド コードの境界を越えて行き来する動作が発生します。Unity operates back and forth over an unmanaged/managed code boundary, between UnityEngine code and your application code. 実行するものがない場合であっても、このブリッジを越えるコンテキストの切り替えには大きなコストがかかります。Context switching over this bridge is fairly expensive, even if there is nothing to execute. 繰り返し呼び出される空の Unity コールバックを持つコンポーネントが含まれる GameObject がアプリに何百個もある場合、これは特に問題になります。This becomes especially problematic if your app has 100's of GameObjects with components that have empty repeating Unity callbacks.

    void Update()
    {
    }
    

注意

このようなパフォーマンスの問題の原因として最も一般的なものは Update() ですが、次のような他の反復的な Unity コールバックも、さらに悪くはないにしても、同程度に悪影響を及ぼす可能性があります: FixedUpdate()、LateUpdate()、OnPostRender()、OnPreRender()、OnRenderImage() など。Update() is the most common manifestation of this performance issue but other repeating Unity callbacks, such as the following can be equally as bad, if not worse: FixedUpdate(), LateUpdate(), OnPostRender", OnPreRender(), OnRenderImage(), etc.

  1. フレームごとに 1 回実行したくなる操作Operations to favor running once per frame

    次の Unity API は、多くのホログラフィック アプリで一般的な操作です。The following Unity APIs are common operations for many Holographic Apps. 常に可能であるとは限りませんが、これらの関数の結果は、1 回計算すれば、アプリケーション全体で特定のフレームに対して再利用できることが非常によくあります。Although not always possible, the results from these functions can very commonly be computed once and the results re-utilized across the application for a given frame.

    a) 一般に、基本的に同じ Raycast 操作をコンポーネントごとに繰り返し行うのではなく、シーンに対する視線 Raycast を処理する専用のシングルトン クラスまたはサービスを用意し、他のすべてのシーン コンポーネントではこの結果を再利用することがよい方法です。a) Generally it is good practice to have a dedicated Singleton class or service to handle your gaze Raycast into the scene and then re-use this result in all other scene components, instead of making repeated and essentially identical Raycast operations by each component. もちろん、アプリケーションによっては、異なるオリジンからの Raycast、または異なる LayerMask に対する Raycast が必要になる場合があります。Of course, some applications may require raycasts from different origins or against different LayerMasks.

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

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

        UnityEngine.Object.GetComponent()
    

    c) 可能であれば、初期化時にすべてのオブジェクトをインスタンス化し、オブジェクト プーリングを使用して、アプリケーションの実行時全体を通して GameObject を再利用するのがよい方法ですc) It is good practice to instantiate all objects, if possible, at initialization and use object pooling to recycle and re-use GameObjects throughout runtime of your application

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

    直接オブジェクトではなくインターフェイスを介した関数の呼び出し、または仮想関数の呼び出しは、直接コンストラクトまたは直接関数呼び出しを利用するより、はるかにコストがかかることがよくあります。Invoking function calls through interfaces vs direct objects or calling virtual functions can often times be much more expensive than utilizing direct constructs or direct function calls. 仮想関数またはインターフェイスが不要な場合は、削除する必要があります。If the virtual function or interface is unnecessary, then it should be removed. ただし、開発の共同作業、コードの読みやすさ、コードの保守性を容易にするためにこれらの方法を利用する場合は、一般に、これらの方法によるパフォーマンスへの影響とトレードオフする価値があります。However, the performance hit for these approaches are generally worth the trade-off if utilizing them simplifies development collaboration, code readability, and code maintainability.

    一般に、このメンバーを上書きする必要があると明確に予想される場合を除き、フィールドや関数を仮想としてマークしないことをお勧めします。Generally, the recommendation is to not mark fields and functions as virtual unless there is a clear expectation that this member needs to be overwritten. UpdateUI() メソッドなど、フレームごとに何回も呼び出される高頻度のコード パスは (フレームごとに 1 回であっても)、特に注意して使用する必要があります。One should be especially careful around high-frequency code paths that are called many times per frame or even once per frame such as an UpdateUI() method.

  3. 構造体を値で渡さないようにするAvoid passing structs by value

    クラスとは異なり、構造体は値型であり、関数に直接渡すと、その内容が新しく作成されるインスタンスにコピーされます。Unlike classes, structs are value-types and when passed directly to a function, their contents are copied into a newly created instance. このコピーにより、CPU コストが発生し、スタックにメモリが追加されます。This copy adds CPU cost, as well as additional memory on the stack. 小さい構造体であれば、通常、影響は最小限であるため、許容されます。For small structs, the effect is usually very minimal and thus acceptable. しかし、すべてのフレームで繰り返し呼び出される関数や、大きな構造体を受け取る関数の場合は、可能であれば、関数定義を変更して参照渡しにします。However, for functions repeatedly invoked every frame as well as functions taking large structs, if possible modify the function definition to pass by reference. 詳細については、こちらを参照してくださいLearn more here

その他Miscellaneous

  1. 物理計算Physics

    a) 一般に、物理計算を改善する最も簡単な方法は、物理計算にかける時間または 1 秒あたりの反復回数を制限することです。a) Generally, the easiest way to improve physics is to limit the amount of time spent on Physics or the number of iterations per second. もちろん、これによりシミュレーションの精度は低下します。Of course, this will reduce simulation accuracy. Unity の TimeManager を参照してくださいSee TimeManager in Unity

    b) Unity でのコライダーの種類には、さまざまなパフォーマンス特性があります。b) The type of colliders in Unity have widely different performance characteristics. 次に示すのは、コライダーをパフォーマンスが最もよいもの (左端) から最も悪いもの (右端) まで順に並べたものです。The order below lists the most performant colliders to least performant colliders from left to right. メッシュ コライダーを避けることが最も重要です。メッシュ コライダーは、プリミティブ コライダーよりはるかにコストが高くなります。It is most important to avoid Mesh Colliders, which are substantially more expensive than the primitive colliders.

    スフィア < カプセル < ボックス <<< メッシュ (凸) < メッシュ (凸なし)Sphere < Capsule < Box <<< Mesh (Convex) < Mesh (non-Convex)

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

  2. アニメーションAnimations

    Animator コンポーネントを無効にして、アイドル状態のアニメーションを無効にします (ゲーム オブジェクトを無効にしても同じ効果は得られません)。Disable idle animations by disabling the Animator component (disabling the game object won't have the same effect). 同じものに値を設定するループ内にアニメーターを配置する設計パターンは避けます。Avoid design patterns where an animator sits in a loop setting a value to the same thing. この方法にはかなりのオーバーヘッドがありますが、アプリケーションへの効果はありません。There is considerable overhead for this technique, with no effect on the application. 詳細については、こちらを参照してください。Learn more here.

  3. 複雑なアルゴリズムComplex algorithms

    アプリケーションで、逆キネマティクスやパス ファインディングなどの複雑なアルゴリズムを使用している場合は、より簡単なアプローチを見つけたり、パフォーマンスに関連する設定を調整したりしますIf your application is using complex algorithms such as inverse kinematics, path finding, etc, look to find a simpler approach or adjust relevant settings for their performance

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

一般に、CPU と GPU のパフォーマンスは、グラフィックスカードに送信される 描画呼び出し に行き着きます。Generally, CPU-to-GPU performance comes down to the draw calls submitted to the graphics card. パフォーマンスを向上させるには、最適な結果が得られるように描画呼び出しを戦略的に a) 減らす か、または b) 再構築する 必要があります。To improve performance, draw calls need to be strategically a) reduced or b) restructured for optimal results. 描画呼び出し自体がリソースを集中的に使用するため、呼び出しを減らすと必要な作業全体が減ります。Since draw calls themselves are resource-intensive, reducing them will reduce overall work required. さらに、描画呼び出し間の状態変化には、グラフィックス ドライバーでコストのかかる検証と変換のステップが必要であるため、アプリケーションの描画呼び出しを再構築して状態の変化を制限すると (つまり、Further, state changes between draw calls requires costly validation and translation steps in the graphics driver and thus, restructuring of your application's draw calls to limit state changes (i.e 異なる素材など) パフォーマンスが向上する可能性があります。different materials, etc) can boost performance.

Unity には、それらのプラットフォームに対する描画呼び出しのバッチ処理に関する概要と詳細がわかる優れた記事があります。Unity has a great article that gives an overview and dives into batching draw calls for their platform.

単一パス インスタンス化レンダリングSingle pass instanced rendering

Unity の単一パス インスタンス化レンダリングを使用すると、各視点に対する描画呼び出しを 1 つのインスタンス化描画呼び出しに減らすことができます。Single Pass Instanced Rendering in Unity allows for draw calls for each eye to be reduced down to one instanced draw call. 2 つの描画呼び出し間のキャッシュの一貫性により、GPU のパフォーマンスもある程度向上します。Due to cache coherency between two draw calls, there is also some performance improvement on the GPU as well.

Unity プロジェクトでこの機能を有効にするにはTo enable this feature in your Unity Project

  1. [Player XR Settings](プレーヤー XR 設定) を開きます ( [Edit](編集) > [Project Settings](プロジェクト設定) > [Player](プレーヤー) > [XR Settings](XR 設定) )Open Player XR Settings (go to Edit > Project Settings > Player > XR Settings )
  2. [Stereo Rendering Method](ステレオ レンダリング方法) ドロップダウン メニューから [Single Pass Instanced](単一パス インスタンス化) を選択します ( [Virtual Reality Supported](仮想現実のサポート) チェック ボックスをオンにする必要があります)Select Single Pass Instanced from the Stereo Rendering Method drop-down menu ( Virtual Reality Supported checkbox must be checked)

このレンダリング方法の詳細については、Unity の次の記事をお読みください。Read the following articles from Unity for details with this rendering approach.

注意

単一パス インスタンス化レンダリングに関する一般的な問題の 1 つは、開発者がインスタンス化対応に作成されていない既存のカスタム シェーダーを持っている場合に発生します。One common issue with Single Pass Instanced Rendering occurs if developers already have existing custom shaders not written for instancing. この機能を有効にした後、開発者は、一部の GameObject が 1 つの視線でしかレンダリングしないことに気付く場合があります。After enabling this feature, developers may notice some GameObjects only render in one eye. これは、関連付けられたカスタム シェーダーに、インスタンス化のための適切なプロパティがないためです。This is because the associated custom shaders do not have the appropriate properties for instancing.

この問題への対処方法については、Unity から提供されている HoloLens 用の単一パス ステレオ レンダリングに関する記事を参照してくださいSee Single Pass Stereo Rendering for HoloLens from Unity for how to address this problem

静的バッチ処理Static batching

Unity では、多くの静的オブジェクトをバッチ処理して、GPU への描画呼び出しを減らすことができます。Unity is able to batch many static objects to reduce draw calls to the GPU. 静的バッチ処理は、 1) 同じ素材を共有し2) Static とマークされている 、Unity のほとんどの Renderer オブジェクトで動作します (Unity でオブジェクトを選択し、インスペクターの右上にあるチェック ボックスをオンにします)。Static Batching works for most Renderer objects in Unity that 1) share the same material and 2) are all marked as Static (Select an object in Unity and click the checkbox in the top right of the inspector). Static とマークされている GameObject は、アプリケーションの実行時を通して移動できません。GameObjects marked as Static cannot be moved throughout your application's runtime. そのため、事実上すべてのオブジェクトの配置、移動、スケーリングなどを行う必要がある HoloLens では、静的バッチ処理を利用することが困難な場合があります。イマーシブ ヘッドセットの場合、静的バッチ処理によって描画呼び出しが大幅に減少し、パフォーマンスが向上する可能性があります。Thus, static batching can be difficult to leverage on HoloLens where virtually every object needs to be placed, moved, scaled, etc. For immersive headsets, static batching can dramatically reduce draw calls and thus improve performance.

詳細については、 Unity の描画呼び出しのバッチ処理に関する記事の「 静的バッチ処理 」を参照してください。Read Static Batching under Draw Call Batching in Unity for more details.

動的バッチ処理Dynamic batching

HoloLens の開発では Static としてオブジェクトをマークすることには問題であるため、動的バッチ処理が、この機能がないことを補う優れたツールになる可能性があります。Since it is problematic to mark objects as Static for HoloLens development, dynamic batching can be a great tool to compensate for this lacking feature. もちろん、イマーシブ ヘッドセットにも役立つことがあります。Of course, it can also be useful on immersive headsets, as well. ただし、GameObject が a) 同じ素材を共有しb) それ以外の条件の長い一覧を満たす 必要があるため、Unity で動的バッチ処理を有効にすることは難しい場合があります。However, dynamic batching in Unity can be difficult to enable because GameObjects must a) share the same Material and b) meet a long list of other criteria .

詳細については、 Unity の描画呼び出しのバッチ処理に関する記事の「 動的バッチ処理 」を参照してください。Read Dynamic Batching under Draw Call Batching in Unity for the full list. ほとんどの場合、関連付けられたメッシュ データは 300 個より多くの頂点を持つことができないため、GameObject は動的バッチ処理が無効になります。Most commonly, GameObjects become invalid to be batched dynamically, because the associated mesh data can be no more than 300 vertices.

その他の手法Other techniques

バッチ処理は、複数の GameObject が同じ素材を共有できる場合にのみ使用できます。Batching can only occur if multiple GameObjects are able to share the same material. 通常、これは、GameObject ではそれぞれの素材に対して固有のテクスチャを持つ必要があるため妨げられます。Typically, this will be blocked by the need for GameObjects to have a unique texture for their respective Material. 複数のテクスチャを 1 つの大きなテクスチャに結合するのが一般的であり、これはテクスチャ アトラシングと呼ばれる手法です。It is common to combine Textures into one big Texture, a method known as Texture Atlasing.

さらに、一般に、可能かつ妥当であれば、複数のメッシュを 1 つの GameObject に結合することをお勧めします。Furthermore, it is generally preferable to combine meshes into one GameObject where possible and reasonable. Unity では、各レンダラーには関連する描画呼び出しがあり、結合されたメッシュは 1 つのレンダラーで送信されます。Each Renderer in Unity will have its associated draw call(s) versus submitting a combined mesh under one Renderer.

注意

実行時に Renderer.material のプロパティを変更すると、素材のコピーが作成されるため、バッチ処理が損なわれる可能性があります。Modifying properties of Renderer.material at runtime will create a copy of the Material and thus potentially break batching. GameObject 間で共有される素材のプロパティを変更するには、Renderer.sharedMaterial を使用します。Use Renderer.sharedMaterial to modify shared material properties across GameObjects.

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

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

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

一般に、 ホログラム安定性に対して最適化するには、 [Player XR Settings](プレーヤー XR 設定)[Depth buffer sharing](深度バッファー共有) を有効にすることをお勧めします。It is generally recommended to enable Depth buffer sharing under Player XR Settings to optimize for hologram stability. ただし、この設定を使用して深度ベースの遅延ステージ再投影を有効にする場合は、 24 ビット深度形式 ではなく、 16 ビット深度形式 を選択することをお勧めします。When enabling depth-based late-stage reprojection with this setting however, it is recommended to select 16-bit depth format instead of 24-bit depth format . 16 ビット深度バッファーを使用すると、深度バッファー トラフィックに関連付けられた帯域幅 (したがって電力) が大幅に減少します。The 16-bit depth buffers will drastically reduce the bandwidth (and thus power) associated with depth buffer traffic. これは、電力の削減とパフォーマンスの向上の両方に大きなメリットがあります。This can be a big win both in power reduction and performance improvement. ただし、" 16 ビット深度形式 " を使用すると、2 つの悪影響が発生する可能性があります。However, there are two possible negative outcomes by using 16-bit depth format .

Z ファイティングZ-Fighting

深度範囲の忠実性を低くすると、24 ビットより 16 ビットの方が Z ファイティングが発生しやすくなります。The reduced depth range fidelity makes z-fighting more likely to occur with 16-bit than 24-bit. これらのアーティファクトを回避するには、Unity カメラのニア/ファー クリップ プレーンを変更して、低い精度に対応します。To avoid these artifacts, modify the near/far clip planes of the Unity camera to account for the lower precision. HoloLens ベースのアプリケーションでは、Unity の既定の1000 m ではなく、50 m のファー クリップ プレーンにすることで、一般に Z ファイティングを除去できます。For HoloLens-based applications, a far clip plane of 50m instead of the Unity default 1000m can generally eliminate any z-fighting.

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

Unity で 16 ビット深度のレンダリング テクスチャが作成されるときは、ステンシル バッファーは作成されません。When Unity creates a Render Texture with 16-bit depth, there is no stencil buffer created. Unity のドキュメントによると、24 ビット深度形式を選択した場合は、24 ビットの z バッファーと共に 8 ビットのステンシル バッファー が作成されます (デバイスで 32 ビットが適用されている場合。HoloLens などではこれが一般的です)。Selecting 24-bit depth format, per Unity documentation, will create a 24-bit z-buffer, as well as an [8-bit stencil buffer] (https://docs.unity3d.com/Manual/SL-Stencil.html) (if 32-bit is applicable on a device, which is generally the case such as HoloLens).

全画面効果を避けるAvoid full-screen effects

全画面に対して動作する手法は、すべてのフレームで数百万の操作が行われるため、非常にコストが高くなる可能性があります。Techniques that operate on the full screen can be quite expensive since their order of magnitude is millions of operations every frame. したがって、アンチエイリアシングやブルームなどの後処理効果を回避することをお勧めします。Thus, it is recommended to avoid post-processing effects such as anti-aliasing, bloom, and more.

最適な照明設定Optimal lighting settings

Unity でリアルタイム全体照明を使用すると、極めて優れたビジュアル結果を得ることができますが、非常に高コストの照明計算が含まれます。Real-time Global Illumination in Unity can provide outstanding visual results but involves quite expensive lighting calculations. [Window](ウィンドウ) > [Rendering](レンダリング) > [Lighting Settings](証明設定)[Real-time Global Illumination](リアルタイム全体照明) をオフにすることで、すべての Unity シーン ファイルでリアルタイム全体照明を無効にすることをお勧めします。It is recommended to disable Realtime Global Illumination for every Unity scene file via Window > Rendering > Lighting Settings > Uncheck Real-time Global Illumination .

また、シャドウ キャストも、Unity シーンに高コストの GPU パスが追加されるため、すべて無効にすることをお勧めします。Furthermore, it is recommended to disable all shadow casting as these also add expensive GPU passes onto a Unity scene. シャドウはライトごとに無効にできますが、品質設定を使用することでまとめて制御することもできます。Shadows can be disable per light but can also be controlled holistically via Quality settings.

[Edit](編集) > [Project Settings](プロジェクト設定)[Quality](品質) カテゴリを選択し、UWP プラットフォームに対して [Low Quality](低品質) を選択します。Edit > Project Settings , then select the Quality category > Select Low Quality for the UWP Platform. また、 [Shadows](シャドウ) プロパティを [Disable Shadows](シャドウを無効にする) に設定するだけでもかまいません。One can also just set the Shadows property to Disable Shadows .

Unity のモデルでは、ベイク済みライティングを使用することをお勧めします。It is recommended that you use baked lighting with your models in Unity.

多角形の数を減らすReduce poly count

通常、多角形の数は次のようにすると減りますPolygon count is usually reduced by either

  1. シーンからのオブジェクトの削除Removing objects from a scene
  2. 特定のメッシュに対する多角形の数が減る資産の間引きAsset decimation which reduces the number of polygons for a given mesh
  3. 遠く離れたオブジェクトが同じジオメトリのローポリ バージョンでレンダリングされる詳細度 (LOD) システムのアプリケーションへの実装Implementing a Level of Detail (LOD) System into your application which renders far away objects with lower-polygon version of the same geometry

Unity でのシェーダーについてUnderstanding shaders in Unity

パフォーマンスでのシェーダーの比較を近似する簡単な方法は、実行時に各操作が実行される平均操作数を特定することです。An easy approximation to compare shaders in performance is to identify the average number of operations each executes at runtime. Unity ではこれを簡単に行うことができます。This can be done easily in Unity.

  1. シェーダー資産をまたは素材を選択した後、インスペクター ウィンドウの右上隅にある歯車アイコンを選択して、 [Select Shader](シェーダーの選択) を選択しますSelect your shader asset or select a material, then in the top right corner of the inspector window, select the gear icon followed by "Select Shader"

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

  2. シェーダー資産を選択した状態で、インスペクター ウィンドウの下の [Compile and show code](コードのコンパイルと表示) ボタンをクリックしますWith the shader asset selected, click the "Compile and show code" button under the inspector window

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

  3. コンパイル後、頂点シェーダーとピクセル シェーダーの両方について、異なる操作の数に関する結果の統計セクションを確認します (注: ピクセル シェーダーはフラグメント シェーダーとも呼ばれます)After compiling, look for the statistics section in the results with the number of different operations for both the vertex and pixel shader (Note: pixel shaders are often also called fragment shaders)

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

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

上記の方法を使用してコンパイル済みの統計結果を調べると、平均してフラグメント シェーダーでは一般に頂点シェーダーより多くの操作が実行されます。Looking at the compiled statistic results using the method above, the fragment shader will generally execute more operations than the vertex shader, on average. ピクセル シェーダーとも呼ばれるフラグメント シェーダーは、画面出力のピクセルごとに実行されます。一方、頂点シェーダーは、画面に描画されるすべてのメッシュの頂点ごとにだけ実行されます。The fragment shader, also known as the pixel shader, is executed per pixel on the screen output while the vertex shader is only executed per-vertex of all meshes being drawn to the screen.

したがって、すべての照明計算のために、フラグメント シェーダーには頂点シェーダーより多くの命令があるだけでなく、ほとんどの場合、フラグメント シェーダーは大きなデータセットに対して実行されます。Thus, not only do fragment shaders have more instructions than vertex shaders because of all the lighting calculations, fragment shaders are almost always executed on a larger dataset. たとえば、画面出力が 2k x 2k の画像の場合、フラグメント シェーダーは 2,000 * 2,000 = 400 万回実行される可能性があります。For example, if the screen output is a 2k by 2k image, then the fragment shader can get executed 2,000*2,000 = 4,000,000 times. 2 つの視線をレンダリングする場合、2 つの画面があるため、この数は 2 倍になります。If rendering two eyes, this number doubles since there are two screens. 複合現実アプリケーションに複数のパス、全画面の後処理効果、または同じピクセルへの複数のメッシュのレンダリングがある場合は、この数が大幅に増加します。If a mixed reality application has multiple passes, full-screen post-processing effects, or rendering multiple meshes to the same pixel, this number will increase dramatically.

したがって、フラグメント シェーダー内の操作の数を減らすと、通常、頂点シェーダーでの最適化より、はるかに大幅にパフォーマンスが向上します。Therefore, reducing the number of operations in the fragment shader can generally give far greater performance gains over optimizations in the vertex shader.

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

物理ベースのレンダリング (PBR) や他の高品質シェーダーを使用する代わりに、より高パフォーマンスで低コストのシェーダーを利用することを検討します。Instead of using a physically based rendering (PBR) or another high-quality shader, look at utilizing a more performant and cheaper shader. Mixed Reality Toolkit では、複合現実プロジェクト用に最適化された MRTK 標準シェーダーが提供されています。The Mixed Reality Toolkit provides the MRTK standard shader that has been optimized for mixed reality projects.

Unity では、unlit、vertex lit、および Unity 標準シェーダーより大幅に高速の他の単純化されたシェーダー オプションも提供されています。Unity also provides an unlit, vertex lit, diffuse, and other simplified shader options that are significantly faster compared to the Unity Standard shader. 詳細については、「組み込みシェーダーの使用とパフォーマンス」を参照してください。See Usage and Performance of Built-in Shaders for more detailed information.

シェーダーのプリロードShader preloading

シェーダーの読み込み時間を最適化するには、 シェーダーのプリロード や他のテクニックを使用します。Use Shader preloading and other tricks to optimize shader load time. 特に、シェーダーのプリロードは、シェーダーの実行時コンパイルによる遅延が発生しないことを意味します。In particular, shader preloading means you won't see any hitches due to runtime shader compilation.

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

Unity では、 シーン ビュー の左上隅にある 描画モード メニューを切り替えて [Overdraw](オーバードロー) を選択することにより、シーンのオーバードローを表示できます。In Unity, one can display overdraw for their scene, by toggling the draw mode menu in the top-left corner of the Scene view and selecting Overdraw .

一般に、GPU に送られる前にオブジェクトを事前にカリングすることで、オーバードローを軽減できます。Generally, overdraw can be mitigated by culling objects ahead of time before they are sent to the GPU. Unity では、エンジンにオクルージョン カリングを実装する詳しい方法が提供されています。Unity provides details on implementing Occlusion Culling for their engine.

メモリに関する推奨事項Memory recommendations

過剰なメモリ割り当て操作と割り当て解除操作により、ホログラフィック アプリケーションに悪影響があり、その結果、一定しないパフォーマンス、フレームの凍結、その他の有害な動作が発生する可能性があります。Excessive memory allocation & deallocation operations can have adverse effects on your holographic application, resulting in inconsistent performance, frozen frames, and other detrimental behavior. メモリ管理はガベージ コレクターによって制御されるため、Unity で開発するときは、メモリに関する考慮事項を理解することが特に重要です。It is especially important to understand memory considerations when developing in Unity since memory management is controlled by the garbage collector.

ガベージ コレクションGarbage collection

ホログラフィック アプリでは、ガベージ コレクター (GC) がアクティブ化されて、実行中にスコープ外になったオブジェクトを分析し、そのメモリを解放して再利用できるようにする必要がある場合、GC のために処理計算時間が失われます。Holographic apps will lose processing compute time to the garbage collector (GC) when the GC is activated to analyze objects that are no longer in scope during execution and their memory needs to be released, so it can be made available for re-use. 通常、継続的に割り当てと割り当て解除を行うと、より頻繁にガベージ コレクターを実行する必要があるため、パフォーマンスとユーザー エクスペリエンスが低下します。Constant allocations and de-allocations will generally require the garbage collector to run more frequently, thus hurting performance and user experience.

Unity では、ガベージ コレクターの詳細なしくみと、メモリ管理に関してより効率的なコードを記述するためのヒントが説明された、優れたページが提供されています。Unity has provided an excellent page that explains in detail how the garbage collector works and tips to write more efficient code in regards to memory management.

ガベージ コレクションが過剰になる最も一般的な状況の 1 つは、Unity の開発においてコンポーネントとクラスへの参照をキャッシュしないことです。One of the most common practices that leads to excessive garbage collection is not caching references to components and classes in Unity development. Start() または Awake() の間にすべての参照をキャプチャし、後の Update() や LateUpdate() などの関数で再利用する必要があります。Any references should be captured during Start() or Awake() and re-used in later functions such as Update() or LateUpdate().

その他のクイックヒント:Other quick tips:

  • C# の StringBuilder クラスを使用して、実行時に複雑な文字列を動的に構築しますUse the StringBuilder C# class to dynamically build complex strings at runtime
  • アプリのすべてのビルド バージョンで実行されるため、Debug.Log() の呼び出しは不要になったら削除しますRemove calls to Debug.Log() when no longer needed, as they still execute in all build versions of an app
  • ホログラフィック アプリで一般に多くのメモリが必要な場合は、読み込み中や移行中の画面を表示するときなど、読み込みフェーズの間に、 System.GC.Collect() を呼び出すことを検討しますIf your holographic app generally requires lots of memory, consider calling System.GC.Collect() during loading phases such as when presenting a loading or transition screen

オブジェクト プーリングObject pooling

オブジェクト プーリングは、オブジェクトの継続的な割り当てと割り当て解除のコストを削減するための一般的な手法です。Object pooling is a popular technique to reduce the cost of continuous allocations & deallocations of objects. これを行うには、同一のオブジェクトの大規模なプールを割り当て、時間の経過と共にオブジェクトを絶えず生成して破棄するのではなく、このプールの非アクティブで使用可能なインスタンスを再利用します。This is done by allocating a large pool of identical objects and re-using inactive, available instances from this pool instead of constantly spawning and destroying objects over time. オブジェクト プールは、アプリの間の有効期間が一定ではない再使用可能なコンポーネントに最適です。Object pools are great for re-useable components that have variable lifetime during an app.

開始時のパフォーマンスStartup performance

比較的小さなシーンでアプリを開始してから、 SceneManager.LoadSceneAsync を使用して残りのシーンを読み込むことを検討する必要があります。You should consider starting your app with a smaller scene, then using SceneManager.LoadSceneAsync to load the rest of the scene. これにより、可能な限り速くアプリを対話状態にすることができます。This allows your app to get to an interactive state as fast as possible. 新しいシーンをアクティブ化するときは大きな CPU スパイクが発生する可能性があること、およびレンダリングされるコンテンツが途切れたり停止したりする場合があることに注意してください。Be aware that there may be a large CPU spike while the new scene is being activated and that any rendered content might stutter or hitch. これを回避する方法の 1 つは、読み込まれるシーンで AsyncOperation.allowSceneActivation プロパティを "false" に設定し、シーンが読み込まれるまで待った後、画面を黒にクリアしてから、"true" に戻してシーンのアクティブ化を完了することです。One way to work around this is to set the AsyncOperation.allowSceneActivation property to "false" on the scene being loaded, wait for the scene to load, clear the screen to black, and then set it back to "true" to complete the scene activation.

開始シーンが読み込まれている間、ユーザーにはホログラフィック スプラッシュ画面が表示されることに注意してください。Remember that while the startup scene is loading, the holographic splash screen will be displayed to the user.

関連項目See also