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 の推奨される環境設定を確認することを強くお勧めします。It is also highly advisable that developers review the recommended environment settings for Unity article. この記事には、パフォーマンスに優れた Mixed Reality アプリを構築するための最も重要なシーン構成を含むコンテンツが含まれています。This 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 のフレームデバッガーは、利用するための非常に強力な洞察ツールでもあります。Further, Unity's Frame Debugger is also a very powerful and insight tool to utilize.

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

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

注意

Unity プロファイラーが接続され、GPU プロファイラーを追加した後 (「Profiler を右上隅に追加する」を参照してください)、プロファイラーの途中で 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

初期化時に、関連するすべてのコンポーネントとオブジェクトへの参照をキャッシュすることをお勧めします。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. これは、普段使用しているカメラにも適用されます。 main.This also applies to to the very regularly used Camera.main. Camera. mainでは、その下に Findexpensively オブジェクト withtag () が使用されます。これにより、シーングラフで "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.
正常コンポーネント GetComponent (型の型)(Good) Component GetComponent(Type type)
正常T GetComponent<T > ()(Good) T GetComponent<T>()
不良コンポーネント GetComponent (文字列) >(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. ほとんどの場合、シーングラフ全体を検索して、一致するオブジェクトの一覧を検索します。Most of these involve searching your entire scene graph for some matching list of GameObjects. これらの操作は、通常、参照をキャッシュするか、問題のあるオブジェクトのマネージャーコンポーネントを実装して実行時に参照を追跡することで回避できます。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. したがって、1000のオブジェクトが割り当てられ、その結果、すべてのフレームが割り当て解除されるため、パフォーマンスが大幅に低下します。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 更新) 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 コールバックを持つコンポーネントを含む100のユーザーオブジェクトがある場合に特に問題になります。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 ()、Behaviour ()、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 は、多くの Holographic アプリで一般的な操作です。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) 一般に、1つのシーンに対して 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. もちろん、アプリケーションによっては、異なるオリジンまたは異なるレイヤーマスクの raycasts が必要になる場合があります。Of course, some applications may require raycasts from different origins or against different LayerMasks.

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

    b) Start () または起動 () で参照をキャッシュすることで、Update () のような Unity コールバックを繰り返し実行する場合は、getcomponent () 操作を避けてください。b) Avoid GetComponent() operations in repeated Unity callbacks like Update() by caching references in Start() or Awake()

     UnityEngine.Object.GetComponent()
    

    c) 初期化時にすべてのオブジェクトをインスタンス化し、オブジェクトプーリングを使用して、アプリケーションの実行時にユーザーオブジェクトをリサイクルして再利用することをお勧めします。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. 多くの場合、フレームごとに何回呼び出されるか、またはフレームごとに1回 (UpdateUI() メソッドなど) に呼び出される、高頻度のコードパスを特に注意する必要があります。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 の colliders の種類には、さまざまなパフォーマンス特性があります。b) The type of colliders in Unity have widely different performance characteristics. 次の順序では、最もパフォーマンスの高い colliders を左から右へと最もパフォーマンスの低い colliders に一覧表示します。The order below lists the most performant colliders to least performant colliders from left to right. メッシュの Colliders を回避することが最も重要です。これは、プリミティブ Colliders よりはるかにコストが高くなります。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

    アニメーターコンポーネントを無効にして、アイドル状態のアニメーションを無効にします (game オブジェクトを無効にしても効果はありません)。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. パフォーマンスを向上させるには、描画呼び出しが戦略的に再構築 ) 、最適な結果を得るために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. プレーヤーの XR の設定を開きます ( [編集] > プロジェクトの設定 > player > XR 設定)Open Player XR Settings (go to Edit > Project Settings > Player > XR Settings)
  2. [ステレオレンダリングメソッド] ドロップダウンメニューから [シングルパスインスタンス] を選択します ( [Virtual Reality がサポートさ] れる チェックボックスをオンにする必要があります)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. この機能を有効にした後、開発者は、一部のオブジェクトが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. 静的バッチ処理は、Unity では 、1) 同じ素材を共有し、 2) すべてが静的としてマークされている (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とマークされたユーザーオブジェクトは、アプリケーションのランタイム全体で移動できません。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 開発ではオブジェクトを静的としてマークするのは問題なので、動的バッチ処理は、このような機能がないことを補うために優れたツールとなります。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. ただし、Unity での動的バッチ処理を有効にすることは困難です。これは、1つのオブジェクトがである必要があります ) は同じ素材を共有し、b は他の条件の長い一覧を満たすためです。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の頂点を持つことができないため、ほとんどの場合、このようなオブジェクトは、動的にバッチ化されるように無効になります。Most commonly, GameObjects become invalid to be batched dynamically, because the associated mesh data can be no more than 300 vertices.

その他の方法Other techniques

バッチ処理は、複数のオブジェクトが同じ素材を共有できる場合にのみ発生します。Batching can only occur if multiple GameObjects are able to share the same material. 通常、これはブロックされます。これは、作成オブジェクトがそれぞれのマテリアルに対して一意のテクスチャを持つ必要があるためです。Typically, this will be blocked by the need for GameObjects to have a unique texture for their respective Material. テクスチャを1つの大きなテクスチャ (テクスチャの atlasing呼ばれるメソッド) にまとめるのが一般的です。It is common to combine Textures into one big Texture, a method known as Texture Atlasing.

さらに、一般に、可能であれば、メッシュを1つのオブジェクトに結合することをお勧めします。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.

注意

実行時にレンダラーのプロパティを変更すると、マテリアルのコピーが作成されるため、バッチ処理が中断される可能性があります。Modifying properties of Renderer.material at runtime will create a copy of the Material and thus potentially break batching. オブジェクト間で共有マテリアルのプロパティを変更するには、Renderer を使用します。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

通常は、プレーヤーの XR 設定深度バッファーの共有を有効にして、ホログラムの安定性を最適化することをお勧めします。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

深度範囲の忠実性を低くすると、 z の戦いが、16ビットの24ビットで発生する可能性が高くなります。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 の既定の1000m ではなく、50m の遠くのクリッププレーンによって、一般に 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 ビットステンシルバッファー] (https://docs.unity3d.com/Manual/SL-Stencil.html) (デバイスに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. ウィンドウ > 使用してすべての 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.

> プロジェクトの設定編集し、 [品質] カテゴリを選択 > UWP プラットフォームの [低品質] を選択します。Edit > Project Settings, then select the Quality category > Select Low Quality for the UWP Platform. また、 shadowsプロパティを設定して、影を無効にすることもできます。One can also just set the Shadows property to Disable Shadows.

Poly 数を減らすReduce poly count

通常、Polygon カウントは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) システムを実装して、同じ geometry の低いポリゴンバージョンで遠く離れたオブジェクトをレンダリングする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 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. シェーダー資産を選択した状態で、インスペクター ウィンドウの下にある コードのコンパイルと表示 ボタンをクリックします。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 のイメージの場合、フラグメントシェーダーは 2000 * 2000 = 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には、mixed reality プロジェクト用に最適化されたmrtk standard shaderが用意されています。The Mixed Reality Toolkit provides the MRTK standard shader that has been optimized for mixed reality projects.

Unity では、Unity 標準シェーダーと比較して、unlit、頂点の lit、拡散、およびその他の単純化されたシェーダーオプションも提供されます。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. 特に、シェーダーのプリロードは、ランタイムシェーダーのコンパイルによって hitches が表示されないことを意味します。In particular, shader preloading means you won't see any hitches due to runtime shader compilation.

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

Unity では、シーンビューの左上隅にある [描画モード] メニューを切り替えて、 [オーバードロー] を選択すると、シーンのオーバードローを表示できます。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

過剰なメモリ割り当て & 解放操作は、holographic アプリケーションに悪影響を及ぼす可能性があり、その結果、パフォーマンスが低下したり、フレームが固定されたり、その他の有害な動作が発生したりします。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

Holographic アプリは、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 () または起動中 () にキャプチャし、Update () や Behaviour () などの後の関数で再利用する必要があります。Any references should be captured during Start() or Awake() and re-used in later functions such as Update() or LateUpdate().

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

  • StringBuilder C#クラスを使用して、実行時に複雑な文字列を動的に構築するUse the StringBuilder C# class to dynamically build complex strings at runtime
  • アプリケーションのすべてのビルドバージョンで実行されるため、不要になったときにデバッグログ () への呼び出しを削除します。Remove calls to Debug.Log() when no longer needed, as they still execute in all build versions of an app
  • Holographic アプリで一般的に多くのメモリが必要な場合は、読み込み中または移行画面を表示するときなど、読み込みフェーズ中に、 System. GC () を呼び出すことを検討してください。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 を使用してシーンの残りの部分を読み込むことを検討してください。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 プロパティを "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.

スタートアップシーンの読み込み中は、holographic スプラッシュ画面がユーザーに表示されることに注意してください。Remember that while the startup scene is loading, the holographic splash screen will be displayed to the user.

関連項目See also