{"id":2056,"date":"2020-07-02T12:33:25","date_gmt":"2020-07-02T04:33:25","guid":{"rendered":"http:\/\/blog.coolcoding.cn\/?p=2056"},"modified":"2020-10-02T03:40:13","modified_gmt":"2020-10-01T19:40:13","slug":"performance-recommendations-for-unity","status":"publish","type":"post","link":"https:\/\/blog.coolcoding.cn\/?p=2056","title":{"rendered":"Performance recommendations for Unity"},"content":{"rendered":"\n<ul><li> <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/mixed-reality\/performance-recommendations-for-unity\">https:\/\/docs.microsoft.com\/en-us\/windows\/mixed-reality\/performance-recommendations-for-unity<\/a> <\/li><\/ul>\n\n\n\n<p>This article builds on the discussion outlined in&nbsp;<a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/mixed-reality\/understanding-performance-for-mixed-reality\">performance recommendations for mixed reality<\/a>&nbsp;but focuses on learnings specific to the Unity engine environment.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"use-recommended-unity-project-settings\">Use recommended Unity project settings<\/h2>\n\n\n\n<p>The most important first step when optimizing performance of mixed reality apps in Unity is to be sure you are using the&nbsp;<a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/mixed-reality\/recommended-settings-for-unity\">recommended environment settings for Unity<\/a>. 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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"how-to-profile-with-unity\">How to profile with Unity<\/h2>\n\n\n\n<p>Unity provides the&nbsp;<strong><a href=\"https:\/\/docs.unity3d.com\/Manual\/Profiler.html\">Unity Profiler<\/a><\/strong>&nbsp;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. Further, Unity&#8217;s&nbsp;<a href=\"https:\/\/docs.unity3d.com\/Manual\/FrameDebugger.html\">Frame Debugger<\/a>&nbsp;is also a very powerful and insight tool to utilize.<\/p>\n\n\n\n<p>Unity provides great documentation for:<\/p>\n\n\n\n<ol><li>How to connect the&nbsp;<a href=\"https:\/\/docs.unity3d.com\/Manual\/windowsstore-profiler.html\">Unity profiler to UWP applications remotely<\/a><\/li><li>How to effectively&nbsp;<a href=\"https:\/\/unity3d.com\/learn\/tutorials\/temas\/performance-optimization\/diagnosing-performance-problems-using-profiler-window\">diagnose performance problems with the Unity Profiler<\/a><\/li><\/ol>\n\n\n\n<p>&nbsp;Note<\/p>\n\n\n\n<p>With the Unity Profiler connected and after adding the GPU profiler (see&nbsp;<em>Add Profiler<\/em>&nbsp;in top right corner), one can see how much time is being spent on the CPU &amp; GPU respectively in the middle of the profiler. This allows the developer to get a quick approximation if their application is CPU or GPU bounded.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2020\/07\/unity-profiler-cpu-gpu.png\" alt=\"Unity CPU vs GPU\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"cpu-performance-recommendations\">CPU performance recommendations<\/h2>\n\n\n\n<p>The content below covers more in-depth performance practices, especially targeted for Unity &amp; C# development.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"cache-references\">Cache references<\/h4>\n\n\n\n<p>It is best practice to cache references to all relevant components and GameObjects at initialization. This is because repeating function calls such as&nbsp;<em><a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/GameObject.GetComponent.html\">GetComponent&lt;T&gt;()<\/a><\/em>&nbsp;are significantly more expensive relative to the memory cost to store a pointer. This also applies to to the very regularly used&nbsp;<a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/Camera-main.html\">Camera.main<\/a>.&nbsp;<em>Camera.main<\/em>&nbsp;actually just uses&nbsp;<em><a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/GameObject.FindGameObjectsWithTag.html\">FindGameObjectsWithTag()<\/a><\/em>&nbsp;underneath, which expensively searches your scene graph for a camera object with the&nbsp;<em>&#8220;MainCamera&#8221;<\/em>&nbsp;tag.CSCopy<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>using UnityEngine;\nusing System.Collections;\n\npublic class ExampleClass : MonoBehaviour\n{\n    private Camera cam;\n    private CustomComponent comp;\n\n    void Start() \n    {\n        cam = Camera.main;\n        comp = GetComponent&lt;CustomComponent>();\n    }\n\n    void Update()\n    {\n        \/\/ Good\n        this.transform.position = cam.transform.position + cam.transform.forward * 10.0f;\n\n        \/\/ Bad\n        this.transform.position = Camera.main.transform.position + Camera.main.transform.forward * 10.0f;\n\n        \/\/ Good\n        comp.DoSomethingAwesome();\n\n        \/\/ Bad\n        GetComponent&lt;CustomComponent>().DoSomethingAwesome();\n    }\n}\n<\/code><\/pre>\n\n\n\n<p>&nbsp;Note<\/p>\n\n\n\n<p>Avoid GetComponent(string)<br>When using&nbsp;<em><a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/GameObject.GetComponent.html\">GetComponent()<\/a><\/em>, 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.<br>(Good) Component GetComponent(Type type)<br>(Good) T GetComponent&lt;T&gt;()<br>(Bad) Component GetComponent(string)&gt;<br><\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"avoid-expensive-operations\">Avoid expensive operations<\/h4>\n\n\n\n<ol><li><strong>Avoid use of&nbsp;<a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/programming-guide\/concepts\/linq\/getting-started-with-linq\">LINQ<\/a><\/strong>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.CSCopy<code>\/\/ Example Code\nusing System.Linq;\n\nList&lt;int&gt; data = new List&lt;int&gt;();\ndata.Any(x =&gt; x &gt; 10);\n\nvar result = from x in data\n             where x &gt; 10\n             select x;\n<\/code><\/li><li><strong>Common Unity APIs<\/strong>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.Copy<code> GameObject.SendMessage()\n GameObject.BroadcastMessage()\n UnityEngine.Object.Find()\n UnityEngine.Object.FindWithTag()\n UnityEngine.Object.FindObjectOfType()\n UnityEngine.Object.FindObjectsOfType()\n UnityEngine.Object.FindGameObjectsWithTag()\n UnityEngine.Object.FindGameObjectsWithTag()\n<\/code><\/li><\/ol>\n\n\n\n<p>&nbsp;Note<\/p>\n\n\n\n<p><em><a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/GameObject.SendMessage.html\">SendMessage()<\/a><\/em>&nbsp;and&nbsp;<em><a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/GameObject.BroadcastMessage.html\">BroadcastMessage()<\/a><\/em>&nbsp;should be eliminated at all costs. These functions can be on the order of 1000x slower than direct function calls.<\/p>\n\n\n\n<ol><li><strong>Beware of boxing<\/strong><a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/programming-guide\/types\/boxing-and-unboxing\">Boxing<\/a>&nbsp;is a core concept of the C# language and runtime. It is the process of wrapping value-typed variables such as char, int, bool, etc. into reference-typed variables. When a value-typed variable is &#8220;boxed&#8221;, 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.One of the most common forms of boxing in development is the use of&nbsp;<a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/programming-guide\/nullable-types\/\">nullable value types<\/a>. 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.<strong>Example of boxing in C#<\/strong>C#Copy<code>\/\/ boolean value type is boxed into object boxedMyVar on the heap\nbool myVar = true;\nobject boxedMyVar = myVar;\n<\/code><strong>Example of problematic boxing via nullable value types<\/strong>This code demonstrates a dummy particle class that one may create in a Unity project. A call to&nbsp;<code>TryGetSpeed()<\/code>&nbsp;will cause object allocation on the heap which will need to be garbage collected at a later point in time. 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&#8217;s of objects would be allocated and consequently de-allocated every frame, which would greatly diminish performance. 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.C#Copy<code>    public class MyParticle\n    {\n        \/\/ Example of function returning nullable value type\n        public int? TryGetSpeed()\n        {\n            \/\/ Returns current speed int value or null if fails\n        }\n    }\n<\/code><\/li><\/ol>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"repeating-code-paths\">Repeating code paths<\/h4>\n\n\n\n<p>Any repeating Unity callback functions (i.e 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.<\/p>\n\n\n\n<ol><li><strong>Empty callback functions<\/strong>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 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. This becomes especially problematic if your app has 100&#8217;s of GameObjects with components that have empty repeating Unity callbacks.CSCopy<code>void Update()\n{\n}\n<\/code><\/li><\/ol>\n\n\n\n<p>&nbsp;Note<\/p>\n\n\n\n<p>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&#8221;, OnPreRender(), OnRenderImage(), etc.<\/p>\n\n\n\n<ol><li><strong>Operations to favor running once per frame<\/strong>The following Unity APIs are common operations for many Holographic Apps. 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) 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. Of course, some applications may require raycasts from different origins or against different&nbsp;<a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/LayerMask.html\">LayerMasks<\/a>.Copy<code> UnityEngine.Physics.Raycast()\n UnityEngine.Physics.RaycastAll()\n<\/code>b) Avoid GetComponent() operations in repeated Unity callbacks like Update() by&nbsp;<a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/mixed-reality\/performance-recommendations-for-unity#cache-references\">caching references<\/a>&nbsp;in Start() or Awake()Copy<code> UnityEngine.Object.GetComponent()\n<\/code>c) It is good practice to instantiate all objects, if possible, at initialization and use&nbsp;<a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/mixed-reality\/performance-recommendations-for-unity#object-pooling\">object pooling<\/a>&nbsp;to recycle and re-use GameObjects throughout runtime of your applicationCopy<code> UnityEngine.Object.Instantiate()\n<\/code><\/li><li><strong>Avoid interfaces and virtual constructs<\/strong>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. 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&nbsp;<code>UpdateUI()<\/code>&nbsp;method.<\/li><li><strong>Avoid passing structs by value<\/strong>Unlike classes, structs are value-types and when passed directly to a function, their contents are copied into a newly created instance. 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.&nbsp;<a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/programming-guide\/classes-and-structs\/how-to-know-the-difference-passing-a-struct-and-passing-a-class-to-a-method\">Learn more here<\/a><\/li><\/ol>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"miscellaneous\">Miscellaneous<\/h4>\n\n\n\n<ol><li><strong>Physics<\/strong>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. See&nbsp;<a href=\"https:\/\/docs.unity3d.com\/Manual\/class-TimeManager.html\">TimeManager<\/a>&nbsp;in Unityb) 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.Copy<code> Sphere &lt; Capsule &lt; Box &lt;&lt;&lt; Mesh (Convex) &lt; Mesh (non-Convex)\n<\/code>See&nbsp;<a href=\"https:\/\/unity3d.com\/learn\/tutorials\/topics\/physics\/physics-best-practices\">Unity Physics Best Practices<\/a>&nbsp;for more info<\/li><li><strong>Animations<\/strong>Disable idle animations by disabling the Animator component (disabling the game object won&#8217;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.&nbsp;<a href=\"https:\/\/docs.unity3d.com\/Manual\/MecanimPeformanceandOptimization.html\">Learn more here.<\/a><\/li><li><strong>Complex algorithms<\/strong>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<\/li><\/ol>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"cpu-to-gpu-performance-recommendations\">CPU-to-GPU performance recommendations<\/h2>\n\n\n\n<p>Generally, CPU-to-GPU performance comes down to the&nbsp;<strong>draw calls<\/strong>&nbsp;submitted to the graphics card. To improve performance, draw calls need to be strategically&nbsp;<strong>a) reduced<\/strong>&nbsp;or&nbsp;<strong>b) restructured<\/strong>&nbsp;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&#8217;s draw calls to limit state changes (i.e different materials, etc) can boost performance.<\/p>\n\n\n\n<p>Unity has a great article that gives an overview and dives into batching draw calls for their platform.<\/p>\n\n\n\n<ul><li><a href=\"https:\/\/docs.unity3d.com\/Manual\/DrawCallBatching.html\">Unity Draw Call Batching<\/a><\/li><\/ul>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"single-pass-instanced-rendering\">Single pass instanced rendering<\/h4>\n\n\n\n<p>Single Pass Instanced Rendering in Unity allows for draw calls for each eye to be reduced down to one instanced draw call. Due to cache coherency between two draw calls, there is also some performance improvement on the GPU as well.<\/p>\n\n\n\n<p>To enable this feature in your Unity Project<\/p>\n\n\n\n<ol><li>Open&nbsp;<strong>Player XR Settings<\/strong>&nbsp;(go to&nbsp;<strong>Edit<\/strong>&nbsp;&gt;&nbsp;<strong>Project Settings<\/strong>&nbsp;&gt;&nbsp;<strong>Player<\/strong>&nbsp;&gt;&nbsp;<strong>XR Settings<\/strong>)<\/li><li>Select&nbsp;<strong>Single Pass Instanced<\/strong>&nbsp;from the&nbsp;<strong>Stereo Rendering Method<\/strong>&nbsp;drop-down menu (<strong>Virtual Reality Supported<\/strong>&nbsp;checkbox must be checked)<\/li><\/ol>\n\n\n\n<p>Read the following articles from Unity for details with this rendering approach.<\/p>\n\n\n\n<ul><li><a href=\"https:\/\/blogs.unity3d.com\/2017\/11\/21\/how-to-maximize-ar-and-vr-performance-with-advanced-stereo-rendering\/\">How to maximize AR and VR performance with advanced stereo rendering<\/a><\/li><li><a href=\"https:\/\/docs.unity3d.com\/Manual\/SinglePassInstancing.html\">Single Pass Instancing<\/a><\/li><\/ul>\n\n\n\n<p>&nbsp;Note<\/p>\n\n\n\n<p>One common issue with Single Pass Instanced Rendering occurs if developers already have existing custom shaders not written for instancing. 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.<\/p>\n\n\n\n<p>See&nbsp;<a href=\"https:\/\/docs.unity3d.com\/Manual\/SinglePassStereoRenderingHoloLens.html\">Single Pass Stereo Rendering for HoloLens<\/a>&nbsp;from Unity for how to address this problem<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"static-batching\">Static batching<\/h4>\n\n\n\n<p>Unity is able to batch many static objects to reduce draw calls to the GPU. Static Batching works for most&nbsp;<a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/Renderer.html\">Renderer<\/a>&nbsp;objects in Unity that&nbsp;<strong>1) share the same material<\/strong>&nbsp;and&nbsp;<strong>2) are all marked as&nbsp;<em>Static<\/em><\/strong>&nbsp;(Select an object in Unity and click the checkbox in the top right of the inspector). GameObjects marked as&nbsp;<em>Static<\/em>&nbsp;cannot be moved throughout your application&#8217;s runtime. 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.<\/p>\n\n\n\n<p>Read&nbsp;<em>Static Batching<\/em>&nbsp;under&nbsp;<a href=\"https:\/\/docs.unity3d.com\/Manual\/DrawCallBatching.html\">Draw Call Batching in Unity<\/a>&nbsp;for more details.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"dynamic-batching\">Dynamic batching<\/h4>\n\n\n\n<p>Since it is problematic to mark objects as&nbsp;<em>Static<\/em>&nbsp;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. However, dynamic batching in Unity can be difficult to enable because GameObjects must&nbsp;<strong>a) share the same Material<\/strong>&nbsp;and&nbsp;<strong>b) meet a long list of other criteria<\/strong>.<\/p>\n\n\n\n<p>Read&nbsp;<em>Dynamic Batching<\/em>&nbsp;under&nbsp;<a href=\"https:\/\/docs.unity3d.com\/Manual\/DrawCallBatching.html\">Draw Call Batching in Unity<\/a>&nbsp;for the full list. Most commonly, GameObjects become invalid to be batched dynamically, because the associated mesh data can be no more than 300 vertices.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"other-techniques\">Other techniques<\/h4>\n\n\n\n<p>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. It is common to combine Textures into one big Texture, a method known as&nbsp;<a href=\"https:\/\/en.wikipedia.org\/wiki\/Texture_atlas\">Texture Atlasing<\/a>.<\/p>\n\n\n\n<p>Furthermore, it is generally preferable to combine meshes into one GameObject where possible and reasonable. Each Renderer in Unity will have its associated draw call(s) versus submitting a combined mesh under one Renderer.<\/p>\n\n\n\n<p>&nbsp;Note<\/p>\n\n\n\n<p>Modifying properties of Renderer.material at runtime will create a copy of the Material and thus potentially break batching. Use Renderer.sharedMaterial to modify shared material properties across GameObjects.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"gpu-performance-recommendations\">GPU performance recommendations<\/h2>\n\n\n\n<p>Learn more about&nbsp;<a href=\"https:\/\/unity3d.com\/learn\/tutorials\/temas\/performance-optimization\/optimizing-graphics-rendering-unity-games\">optimizing graphics rendering in Unity<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"optimize-depth-buffer-sharing\">Optimize depth buffer sharing<\/h3>\n\n\n\n<p>It is generally recommended to enable&nbsp;<strong>Depth buffer sharing<\/strong>&nbsp;under&nbsp;<strong>Player XR Settings<\/strong>&nbsp;to optimize for&nbsp;<a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/mixed-reality\/hologram-stability\">hologram stability<\/a>. When enabling depth-based late-stage reprojection with this setting however, it is recommended to select&nbsp;<strong>16-bit depth format<\/strong>&nbsp;instead of&nbsp;<strong>24-bit depth format<\/strong>. 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. However, there are two possible negative outcomes by using&nbsp;<em>16-bit depth format<\/em>.<\/p>\n\n\n\n<p><strong>Z-Fighting<\/strong><\/p>\n\n\n\n<p>The reduced depth range fidelity makes&nbsp;<a href=\"https:\/\/en.wikipedia.org\/wiki\/Z-fighting\">z-fighting<\/a>&nbsp;more likely to occur with 16-bit than 24-bit. To avoid these artifacts, modify the near\/far clip planes of the&nbsp;<a href=\"https:\/\/docs.unity3d.com\/Manual\/class-Camera.html\">Unity camera<\/a>&nbsp;to account for the lower precision. For HoloLens-based applications, a far clip plane of 50m instead of the Unity default 1000m can generally eliminate any z-fighting.<\/p>\n\n\n\n<p><strong>Disabled Stencil Buffer<\/strong><\/p>\n\n\n\n<p>When Unity creates a&nbsp;<a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/RenderTexture-depth.html\">Render Texture with 16-bit depth<\/a>, there is no stencil buffer created. Selecting 24-bit depth format, per Unity documentation, will create a 24-bit z-buffer, as well as an [8-bit stencil buffer] (<a href=\"https:\/\/docs.unity3d.com\/Manual\/SL-Stencil.html\">https:\/\/docs.unity3d.com\/Manual\/SL-Stencil.html<\/a>) (if 32-bit is applicable on a device, which is generally the case such as HoloLens).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"avoid-full-screen-effects\">Avoid full-screen effects<\/h3>\n\n\n\n<p>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&nbsp;<a href=\"https:\/\/docs.unity3d.com\/Manual\/PostProcessingOverview.html\">post-processing effects<\/a>&nbsp;such as anti-aliasing, bloom, and more.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"optimal-lighting-settings\">Optimal lighting settings<\/h3>\n\n\n\n<p><a href=\"https:\/\/docs.unity3d.com\/Manual\/GIIntro.html\">Real-time Global Illumination<\/a>&nbsp;in Unity can provide outstanding visual results but involves quite expensive lighting calculations. It is recommended to disable Realtime Global Illumination for every Unity scene file via&nbsp;<strong>Window<\/strong>&nbsp;&gt;&nbsp;<strong>Rendering<\/strong>&nbsp;&gt;&nbsp;<strong>Lighting Settings<\/strong>&nbsp;&gt; Uncheck&nbsp;<strong>Real-time Global Illumination<\/strong>.<\/p>\n\n\n\n<p>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.<\/p>\n\n\n\n<p><strong>Edit<\/strong>&nbsp;&gt;&nbsp;<strong>Project Settings<\/strong>, then select the&nbsp;<strong>Quality<\/strong>&nbsp;category &gt; Select&nbsp;<strong>Low Quality<\/strong>&nbsp;for the UWP Platform. One can also just set the&nbsp;<strong>Shadows<\/strong>&nbsp;property to&nbsp;<strong>Disable Shadows<\/strong>.<\/p>\n\n\n\n<p>It is recommended that you use baked lighting with your models in Unity.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"reduce-poly-count\">Reduce poly count<\/h3>\n\n\n\n<p>Polygon count is usually reduced by either<\/p>\n\n\n\n<ol><li>Removing objects from a scene<\/li><li>Asset decimation which reduces the number of polygons for a given mesh<\/li><li>Implementing a&nbsp;<a href=\"https:\/\/docs.unity3d.com\/Manual\/LevelOfDetail.html\">Level of Detail (LOD) System<\/a>&nbsp;into your application which renders far away objects with lower-polygon version of the same geometry<\/li><\/ol>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"understanding-shaders-in-unity\">Understanding shaders in Unity<\/h3>\n\n\n\n<p>An easy approximation to compare shaders in performance is to identify the average number of operations each executes at runtime. This can be done easily in Unity.<\/p>\n\n\n\n<ol><li>Select your shader asset or select a material, then in the top right corner of the inspector window, select the gear icon followed by&nbsp;<strong>&#8220;Select Shader&#8221;<\/strong><\/li><li>With the shader asset selected, click the&nbsp;<strong>&#8220;Compile and show code&#8221;<\/strong>&nbsp;button under the inspector window<\/li><li>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)<\/li><\/ol>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"optimize-pixel-shaders\">Optimize pixel shaders<\/h4>\n\n\n\n<p>Looking at the compiled statistic results using the method above, the&nbsp;<a href=\"https:\/\/en.wikipedia.org\/wiki\/Shader#Pixel_shaders\">fragment shader<\/a>&nbsp;will generally execute more operations than the&nbsp;<a href=\"https:\/\/en.wikipedia.org\/wiki\/Shader#Vertex_shaders\">vertex shader<\/a>, 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.<\/p>\n\n\n\n<p>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. 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. 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.<\/p>\n\n\n\n<p>Therefore, reducing the number of operations in the fragment shader can generally give far greater performance gains over optimizations in the vertex shader.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"unity-standard-shader-alternatives\">Unity Standard shader alternatives<\/h4>\n\n\n\n<p>Instead of using a physically based rendering (PBR) or another high-quality shader, look at utilizing a more performant and cheaper shader. The&nbsp;<a href=\"https:\/\/github.com\/Microsoft\/MixedRealityToolkit-Unity\">Mixed Reality Toolkit<\/a>&nbsp;provides the&nbsp;<a href=\"https:\/\/microsoft.github.io\/MixedRealityToolkit-Unity\/Documentation\/README_MRTKStandardShader.html\">MRTK standard shader<\/a>&nbsp;that has been optimized for mixed reality projects.<\/p>\n\n\n\n<p>Unity also provides an unlit, vertex lit, diffuse, and other simplified shader options that are significantly faster compared to the Unity Standard shader. See&nbsp;<a href=\"https:\/\/docs.unity3d.com\/Manual\/shader-Performance.html\">Usage and Performance of Built-in Shaders<\/a>&nbsp;for more detailed information.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"shader-preloading\">Shader preloading<\/h4>\n\n\n\n<p>Use&nbsp;<em>Shader preloading<\/em>&nbsp;and other tricks to optimize&nbsp;<a href=\"https:\/\/docs.unity3d.com\/Manual\/OptimizingShaderLoadTime.html\">shader load time<\/a>. In particular, shader preloading means you won&#8217;t see any hitches due to runtime shader compilation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"limit-overdraw\">Limit overdraw<\/h3>\n\n\n\n<p>In Unity, one can display overdraw for their scene, by toggling the&nbsp;<a href=\"https:\/\/docs.unity3d.com\/Manual\/ViewModes.html\"><strong>draw mode menu<\/strong><\/a>&nbsp;in the top-left corner of the&nbsp;<strong>Scene view<\/strong>&nbsp;and selecting&nbsp;<strong>Overdraw<\/strong>.<\/p>\n\n\n\n<p>Generally, overdraw can be mitigated by culling objects ahead of time before they are sent to the GPU. Unity provides details on implementing&nbsp;<a href=\"https:\/\/docs.unity3d.com\/Manual\/OcclusionCulling.html\">Occlusion Culling<\/a>&nbsp;for their engine.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"memory-recommendations\">Memory recommendations<\/h2>\n\n\n\n<p>Excessive memory allocation &amp; deallocation operations can have adverse effects on your holographic application, resulting in inconsistent performance, frozen frames, and other detrimental behavior. It is especially important to understand memory considerations when developing in Unity since memory management is controlled by the garbage collector.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"garbage-collection\">Garbage collection<\/h4>\n\n\n\n<p>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.<\/p>\n\n\n\n<p>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.<\/p>\n\n\n\n<ul><li><a href=\"https:\/\/unity3d.com\/learn\/tutorials\/topics\/performance-optimization\/optimizing-garbage-collection-unity-games?playlist=44069\">Optimizing garbage collection in Unity games<\/a><\/li><\/ul>\n\n\n\n<p>One of the most common practices that leads to excessive garbage collection is not caching references to components and classes in Unity development. Any references should be captured during Start() or Awake() and re-used in later functions such as Update() or LateUpdate().<\/p>\n\n\n\n<p>Other quick tips:<\/p>\n\n\n\n<ul><li>Use the&nbsp;<a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.text.stringbuilder?view=netframework-4.7.2\">StringBuilder<\/a>&nbsp;C# class to dynamically build complex strings at runtime<\/li><li>Remove calls to Debug.Log() when no longer needed, as they still execute in all build versions of an app<\/li><li>If your holographic app generally requires lots of memory, consider calling&nbsp;<a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.gc.collect?view=netframework-4.7.2\"><em><strong>System.GC.Collect()<\/strong><\/em><\/a>&nbsp;during loading phases such as when presenting a loading or transition screen<\/li><\/ul>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"object-pooling\">Object pooling<\/h4>\n\n\n\n<p>Object pooling is a popular technique to reduce the cost of continuous allocations &amp; 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.<\/p>\n\n\n\n<ul><li><a href=\"https:\/\/unity3d.com\/learn\/tutorials\/topics\/scripting\/object-pooling\">Object Pooling Tutorial in Unity<\/a><\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"startup-performance\">Startup performance<\/h2>\n\n\n\n<p>You should consider starting your app with a smaller scene, then using&nbsp;<em><a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/SceneManagement.SceneManager.LoadSceneAsync.html\">SceneManager.LoadSceneAsync<\/a><\/em>&nbsp;to load the rest of the scene. This allows your app to get to an interactive state as fast as possible. 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. One way to work around this is to set the AsyncOperation.allowSceneActivation property to &#8220;false&#8221; on the scene being loaded, wait for the scene to load, clear the screen to black, and then set it back to &#8220;true&#8221; to complete the scene activation.<\/p>\n\n\n\n<p>Remember that while the startup scene is loading, the holographic splash screen will be displayed to the user.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>https:\/\/docs.microsoft.com\/en-us\/windows\/mixed-reality\/ [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/2056"}],"collection":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2056"}],"version-history":[{"count":1,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/2056\/revisions"}],"predecessor-version":[{"id":2058,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/2056\/revisions\/2058"}],"wp:attachment":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2056"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2056"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2056"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}