Click here to Skip to main content
15,879,535 members
Articles / Mobile Apps / Android
Article

Unity Optimization Guide for Intel x86 Platforms: Part 2

15 Apr 2016CPOL6 min read 18.3K  
We will explore two main areas of optimizations in this guide: Scripting and Editor-based.

This article is in the Product Showcase section for our sponsors at CodeProject. These articles are intended to provide you with information on products and services that we consider useful and of value to developers.

Intel® Developer Zone offers tools and how-to information for cross-platform app development, platform and technology information, code samples, and peer expertise to help developers innovate and succeed. Join our communities for Android, Internet of Things, Intel® RealSense™ Technology and Windows to download tools, access dev kits, share ideas with like-minded developers, and participate in hackathon’s, contests, roadshows, and local events.

Contents

Go Back to Part 1 of the Tutorial:
Unity* Optimization Guide for Intel x86 Platforms: Part 1

Optimizations

We will explore two main areas of optimizations in this guide: Scripting and Editor-based. You will see a number of specifics for each optimization based on the roots in which it functions.

Scripting Optimizations

Script Frustum Culling and Co-routines

When you are profiling your application and see that a script’s Update() function does not need to be called every frame, you have a few great methods to reduce the amount of updates:

  • Script Frustum Culling
    • Use the following Monobehavior callbacks to cull scripts outside of the camera frustum that do not need to update when not in focus.
      Image 1
      Figure 12. Monobehavior callbacks which trigger when object with script leaves / enters the camera frustum
  • Co-routines
    • Co-routines are essentially functions with the ability to pause and resume execution. The power of co-routines can be leveraged by removing the original Update() function in your script and replacing it with a co-routine. You can then set how often you would like your co-routine to execute using the yield command. This snippet shows how to create a custom smart update that is called every 2 seconds, rather than the default setting of once per frame:
      Image 2
      Figure 13. More efficient update using co-routines

Smart Memory Management

When looking for ways to optimize your memory usage, it is helpful to check the Unity profiler first. A great way to get an overview of how you are managing memory is to check the ‘GC Alloc’ section of the Overview window (Figure 14) and step through your frames until you see a significant allocation.

Image 3

Figure 14. By inspecting many frames in a row, you can determine when GC will occur and adjust

It is also helpful to check how frequently garbage collection is being invoked. To see this, isolate the GarbageCollector field in the ‘CPU Usage’ sub-profiler (Figure 15):

Image 4

Figure 15. Point identified in CPU usage when GC occurs

When collects are displayed, you can then click on a peak in the graph and look for the call to GC.Collect (Figure 16). By doing this you can see how much time each collect takes:

Image 5

Figure 16. Statistics on GC

To avoid frequent allocations, it is advantageous to use structs instead of classes to have allocations be done on the stack, instead of in the heap. Multiple allocations to the heap can lead to significant memory fragmentation and frequent garbage collections.

Caching Frequent Objects and Components

As a rule of thumb, you should analyze your apps to find the most frequently used GameObjects and Components and make sure that these values are being cached. Any time you see an object being fetched every scene is an opportunity for caching and saving unnecessary computation.

The same rule applies to GameObject instantiation. In general, instantiation is a relatively slow call that should be avoided. If creating and destroying the same object types repeatedly in every scene, it is advantageous to maintain a list of those objects to be reused in an object manager script.

Unity recommends a central game manager to maintain lists of all of your cached game objects. After implementing this technique, you can include the following code snippet to compare the performance delta between the two methods by toggling a state button, or other control mechanism while viewing the CPU Usage profiler in real time. Here is the snippet (Figure 14) showing the difference in usage:

Image 6

Figure 17. Using STATE to toggle objects

Best Practices for Working with the Unity Physics System

When working with dynamic objects in Unity, there are a few well-known optimizations and pitfalls to avoid. Whether you plan to move the object yourself or allow Unity to take control of the physics of an object, add a Rigidbody component to your object. This tells the Unity physics system that the object is moveable. When you wish to move the object manually, simply check the isKinematic flag (Figure 18). You also want to make sure that the static checkbox at the top right corner of the inspector is unchecked for that object (Figure 19).

Image 7

Figure 18. isKinematic checked to take control over objects movement

Image 8

Figure 19. Static property unchecked to keep dynamic object out of static set

To make sure that you are handling dynamic objects properly in your app, open up the profiler, isolate the physics subsection of the CPU Profiler, highlight a frame that lands on the physics time step (24 updates per second by default), and verify that you do not see any “Static Collider.Move (Expensive delayed cost)” entries (Figure 20) in the overview window under the object’s FixedUpdate() call. The lack of a Static Collider.Move message indicates the physics in this section is working appropriately.

Image 9

Figure 20. Static Collider.Move (Expensive delayed cost) appears when you are not managing dynamic objects properly

Disable Fully Transparent Objects

For objects using a material with a ‘Fade Out’ rendering mode, or really anything that ever becomes fully transparent, it is important to make sure that you set the MeshRenderer component for that object to disabled once the object is fully transparent. These objects will always be dispatched in a draw call regardless of the alpha value. For example, sometimes developers use full screen quads to paint on damage indicators or vignette effects that pulse in and out when an event triggers. It’s important to know that the engine itself does not track when objects are fully transparent and resources will be wasted if care isn’t taken. The following screenshots were taken in the same scene, with the only difference being the alpha value of the semi-transparent objects in the foreground.

Image 10

Figure 21. Five semi-transparent planes with the corresponding GPA frame capture. Semi-transparent objects are visible and taking 37.4% of the scene at 2,657.5 μs.

Image 11

Figure 22. The five previously mentioned planes have their material’s alpha value set to 0. The corresponding GPA frame capture shows that there are still draw commands occurring on the GPU. These draws are taking 32.1% of the scene at 2,316.5 μs.

To make sure that you aren’t dispatching unnecessary draw calls, always check the alpha value associated with your potentially transparent objects if possible. For a simple material that renders based on color, just use a mechanism along the lines of the following code snippet.

Image 12

Figure 23. When editing an objects transparency, always check that it is visible and disable it if necessary to save resources.

Continue to Part 3 of the Tutorial:
Unity* Optimization Guide for Intel x86 Platforms: Part 3

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Intel is inside more and more Android devices, and we have tools and resources to make your app development faster and easier.


Comments and Discussions

 
-- There are no messages in this forum --