San Mateo 2023 Flashcards
Animators: UI
Something not mentioned on this slide,is the use of animators for UI. The animator system is particularly inefficient when used with Unity UI. Animators animating a property of a UI element will force it to rebuild the Canvas and Layout every frame, and cause unnecessary batching work.
So we recommend avoiding animators with UI.
Physics Simulations: What Are They
Physics ensure that the objects correctly accelerate and respond to collisions, gravity, and various other forces. Physics can apply to your 3D, 2D, object-oriented, or data-oriented games.
Physics Simulations: Performance Cost
Physics calculations can be quite expensive, especially on mobile devices. The total amount of physics calculation depends on the number of non-sleeping rigid bodies and colliders in the scene
and the complexity of the colliders. Even if these aren’t present in your scene, depending on the settings of your game Physics can still have CPU overhead, due to to how frequently it’s being simulated, despite not being used. There are ways to reduce this performance cost, or eliminate it altogether if Physics aren’t necessary for your project.
Physics Simulations: Disable or Reduce Collisions
There are a few possible areas where you can disable or reduce collisions. For instance avoid using the collision module for your particle systems unless you need it. Additionally, Mesh Colliders are generally expensive, so consider substituting more complex Mesh Colliders with primitive or simplified ones to approximate the original shape.
Physics Simulations: Change FixedUpdate to Update
If you do need physics in your game, but your logic doesn’t strongly depend on deterministic physics calculations, we suggest moving fixedupdate logic to Update. That is because this has the frequency of the physics system, and is called every fixed frame rate frame. Like I had mentioned, this is by default set at .02 in your physics settings, so could potentially be called twice per frame.
Physics Simulations: Check Project Settings
There are a few changes you can make in your project settings to reduce the impact of physics on your performance. The one mentioned broadly in the project reviews was disabling Auto Simulation. Auto Simulation has CPU overhead due to housekeeping operations of the physics simulations, and will automatically run the physics simulations. If you don’t need Physics simulations running constantly, you can choose to disable this, but enable it manually to give you explicit control over when physics runs.
You can also disable Auto Sync Transformations in your project settings to avoid excess synchronization that occurs before every raycast operation.
Physics Simulations: Adjust Fixed Timestep
The physics engines work by running on a Fixed Timestep, which defines the time delta used by each Physics Step. The default value is at 0.02, which equates to 50 frames per second. If you were to leave your physics Timestep at this default value with a target frame rate of 30 fps, it would be called twice per frame. If you need physics in your game, you can increase the Fixed Timestep so Physics aren’t as heavy on the CPU. This can be done via Time Manager or during runtime using the Time.fixedDeltaTime property.
Managed Allocations and Garbage Collection: What is it/How does it work
Automatic memory management allows you to write code quickly and easily, and with few errors without needing to manually request the release of memory. The garbage collector manages a section of memory called the Managed Heap, and will periodically release and allocate memory. However, this convenience does come with memory and performance problems if you’re not careful;
Managed Allocations and Garbage Collection: Performance Cost
- this memory management process impacts runtime performance, because allocating managed memory is time-consuming for the CPU, and could result in stuttering because the garbage collector is unpredictable with when it releases and allocated memory.
- Garbage collection might also stop the CPU from doing other work until it completes.
- Also, when the garbage collector releases an object, the memory it occupied is freed up. However this free space doesn’t become a part of a single pool of free memory, and instead the memory becomes fragmented.
- This ultimately leads to the heap expanding, because the managed heap can’t find or free up a single block of contiguous memory to assign new allocations.
Managed Allocations and Garbage Collection: Avoid Temporary Allocations
It’s common for an application to allocate temp data to the managed heap in each frame, but this does affect the performance as I mentioned before. If a program allocates one kilobyte (1KB) of temporary memory each frame, and it runs at 60 frames per second, then it must allocate 60 kilobytes of temporary memory per second. Over the course of a minute, this adds up to 3.6 megabytes of memory available to the garbage collector. So try to get to as close to 0 bytes allocated per frame as possible. As an example of an issue with managed allocations, in one game your team was seeing around 32.8 Kilobytes allocated every frame. Per frame allocations can quickly add up so this is particularly important, and it can cause runtime hitches caused by Garbage Collection.
Managed Allocations and Garbage Collection: Use Project Auditor
Most of the code paths that cause managed memory allocation can be easily spotted with the help of the Project Auditor. In particular, the code analysis section of the tool that uses Roslyn Analyzers helps to quickly identify the location of managed memory allocations. We recommend that teams try the tool and strive for having code without allocations during gameplay.
Managed Allocations and Garbage Collection: Avoid Debug Logging
Debug logging is great for diagnosing issues, but it does create strings, which are subject to garbage collection. You should aim to remove logging from your final builds. If left in the final build, these calls will still try to write to some console, and the string will still be allocated. You can create a wrapper to make a custom logger, which can enable or disable logging in your build, but will still create string garbage from where the log function was called from.
Resources Folder: What is it
This is the system that allows developers to store Assets within one or more folders named Resources and to load or unload Objects from those Assets at runtime using the Resources API.
The Resources folder may be useful in some trivial cases, if the content is:
- Generally required throughout the project’s lifetime
- Not memory intensive
- Not prone to patching, or does not vary across platforms or devices
The resources folder can be very useful for fast prototyping, but for full production it presents many problems and we don’t recommend using it for several reasons
Resources Folder: Performance Issues
It can bloat the size of a project’s build, lead to uncontrollable excessive memory utilization, and significantly increase application start up times. Every asset within Resources folders, and every asset it references, are included in Resources system data. Because of this, the time required to initialize the Resources system increases with the number of files within Resources folders.
Another common issue with Resources folders is that everything inside a Resources folder will be included in the build, even debug and placeholder assets. Unity can strip unused assets in other folders to remove them from builds, but it has no way of knowing which assets might be loaded from Resources at runtime, so it does not discriminate.
Asset management because very difficult as the number of assets in those folders increase.
The Resources system makes it hard to deliver custom content to specific platforms and eliminates the possibility of incremental content upgrades.
Resources Folder: Switch to Addressables
Using Addressables can improve runtime memory by introducing weak references to prevent assets from being loaded. Weak references mean that you have control over when the references asset is loaded into and out of memory, and the addressable system will find all the necessary dependencies and load them too. Addressables can be daunting to set up, but it’s worth it. We recently put out a blog about addressables best practices, and have a specific guide about setting up addressables for a mobile game with frequent content updates that might come in handy.
We do recommend completely switching to addressables, because if you use asset bundles and resources together in tandem, that can lead to duplication of assets, leading to further memory bloat.
Resources Folder: Use Project Auditor
I’m suggesting project auditor again, because in this case, project auditor’s Asset Diagnostics view can provide an overview of all the assets contained in Resources and their dependencies. This will hopefully help asset management as the resources folder grows.
Resources Folder: Use Build Report Inspector
Build Report Inspector is the tool I mentioned that lets you access information about your last build. However the important feature here is the ability to analyze which assets are included in the build. This can help you catch any assets still being included in the Resources folder.
Animators: What Is It
I’m sure you’re all familiar, but I’ll just briefly introduce the animation system in Unity for context to this problem. Unity’s animation system is based on Animation Clips which contain the info on how characters or objects move. Those animation clips are then organized into a flowchart system called the animator controller, and this is responsible for blending or switching between given animations based on what’s happening in the game. These animator controllers are attached to animator components.
Animators: Performance Cost
The Mecanim animation system does have a performance cost, because it is a more complex system than the legacy animation system. Mecanim has temporary buffers it uses for blending, and there is additional copying of the sampled curve and other data. The Mecanim layout is optimized for animation blending and more complex setups. Additionally, Mecanim was designed to handle complex animations with work split across multiple threads, in the case of simple animations overhead of scheduling jobs results in worse performance.
Animators: Use Legacy Animations for Simple Animations
Using the Mecanim animation system puts an additional strain on the CPU. Mecanim was designed to handle complex animations with work split across multiple threads, in the case of simple animations overhead of scheduling jobs results in worse performance. In comparison, the Legacy Animation component runs on the main thread and is much better suited for simple animations, because it does not have this overhead and doesn’t use a state machine.
Something that can be classified as a “Simple Animation” is one with fewer than 400 curves. The attached graph on this slide demonstrates the performance comparison of using legacy animations versus the newer animation system based on the amount of curves within the animation. The recommendation is if you’re using legacy animation, is to do simple tweening instead.
Animators: Configure Import Settings to Disable Unused Features
Occasionally, it’s possible to import in empty animation clips with assets, or in some cases instantiate objects with unused Animator Controllers and attached avatars. When this situation happens, although these assets are seemingly not being used, this could take a significant amount of memory. We recommend turning off the generation of an Avatar by setting the Animation Type to None in Rig tab. You should also disable importing empty animation clips that are exported with models by disabling Import Animation in the Animation tab. This will ensure that you don’t have unused assets taking up memory.
Animators: Cull Animators
The CPU frame time of animator components are divided among three steps: preparing animation frames, processing frames when the animation begins, and processing frames when the animation ends. CPU time of the first step depends on the number of enabled Animator components in the active scene. CPU time of the second and third steps depends on the number of enabled Animator components and the culling mode of those components. Based on this, it’s very important to cull animators when the object no longer needs to be animating or when it’s out of view. We recommend exploring different animator culling modes that will cull them when the renderer is not visible. The culling mode “Cull Completely”, where the animation will be completely disabled when not visible. You’ll want to make sure you’re disabling the animator component, as disabling the game object won’t have the same effect.
Animators: Use Shader Animations
In many cases we’ve seen in the Game the simplest animations can be offloaded to the GPU by animating them in a shader. For example, making a flag wave in a shader will allow you to remove the skin weights from the mesh, bones, and animator component entirely.
Texture Memory: Performance Cost
Textures are often the most memory intensive assets in a project, due to resolutions, accidental duplication, or using the wrong compression format for your chosen platforms. There are a lot of checkboxes that can easily go overlooked when importing your textures in that cause memory to balloon, but we have great tools for diagnosing these issues and there are solutions for bringing down your texture memory.