MonoBehaviour call optimization for Unity developers

MonoBehavior calls optimization

What if I told you that Unity can be wasting a lot of CPU performance just by calling your MonoBehaviour functions? It doesn’t really matter what your scripts are doing. If you have hundreds or thousands of them, you should be aware that there’s a new field of optimization.

Magic methods

MonoBehaviour functions calls are slow. And I’m talking about functions like Update(), LateUpdate(), OnRender() etc. They are so-called magic methods, and if you’re familiar with object-oriented programming languages, this concept looks like calling a method using reflection mechanism (reflection enables method calls even if you don’t know the interface). Reflection calls are expensive, so Unity does everything that is possible to cache any operations, so the set of CPU instructions needed to call a magic method each frame could be minimal. But it can still be slow, very slow…

Why is it so slow?

I’m not gonna talk about the details (but if you really want to read about the details, look at the end of this article for the links), so just imagine that Unity tries to be as flexible and easy to use as possible. Making something easily costs CPU power because the engine cannot make any assumptions about your game and it needs to do a bunch of checks to call your magic functions on the right objects, in the right order, and to not crash in the meantime.

Can it become faster?

Oh this is my favorite part. Yes! It can! How? You have to take the responsibility of calling Update() function by defining your own function and calling it from a manager. This way, you’re taking responsibility for updating your objects. How much faster it can become? Well, it depends on the platform. Let me use the measurements done by Valentin Simonov on official Unity blog:

Mono with fast but no exception settings.

Here you see that the difference can be worth the time. This is a measurement of calling Update() 10000 times.

Writing a manager

I will present a fairy simple example of a manager called BoxManager that is managing BoxManaged scripts. Manager has two responsibilities:

  1. Keeping the list of managed objects updated
  2. Calling update-like functions on managed objects when manager Update() is called.

The code may look like this:

As you can see, it’s really simple. Before implementing Update() function let’s take a look at BoxManaged.cs.

It registers itself when enabled and de-registers when disabled. Fair enough. ManagedUpdate() function is a function that will replace Update() magic function. Let’s implement BoxManager.Update(), so it will be able to call all BoxManaged.ManagedUpdate() at once.

And that’s it! Really! Now in ManagedUpdate() you can do everything you would normally do in the Update() function.

Please note that I did not use foreach for iterations. Firstly, because it’s generating small amount garbage Unity’s version of Mono. Secondly, because it simply seems to be slower.

Should I care?

It depends on what kind of game are you creating and what is the target platform. Ask yourself a question – do you have many MonoBehaviour objects with Update() calls? It doesn’t necessarily need to be Update(), it can be anything that it is invoked with each frame. Then, if you’re targeting mobiles, it’s definitely worth to try! Targeting standalones? It’s still something you may consider, especially if you’re planing to have huge amount of objects.

Sometimes you may need a manager even if you’re have a relatively small amount of objects. On iOS there was (I don’t know if it has been fixed or not) a problem with OnRender() function. Having it on 30-40 objects could decrease the game’s performance twice! The solution? A manager like the one presented above, but instead of calling Update() it should be calling OnRender() code. Yes, it will work!

Please keep in mind that this is one of many optimization strategies that you can use. Yet this one is quite hidden – unless you know about it, you will have a hard time to find about this one. That’s the reason why this article has been brought to life.

References:

https://blogs.unity3d.com/2015/12/23/1k-update-calls/

 

Should You Install Unity Cache Server on localhost

Should you install Unity Cache Server on localhost?

Do you already know how to use Unity Cache Server? If you’re one of the maniacs trying to optimize every aspect of their development environment, then you most probably do! It’s a great solution for the teams that are working on large projects, especially for mobile devices. But does it make sense to install it for only one person… locally?

How Unity imports assets

The first thing you need to understand is the way Unity imports assets. When you put a file into your Assets folder, Unity executes an asset processor. The textures will be converted to optimal texture format for your target platform: sounds to mp3/ogg (or any other) conversion, models to internal Unity mesh format and so on. Some conversions are done so fast, that you won’t even notice it, but for some it may take enormous amount of time. For instance, processing Android and iOS textures may even take several hours!

This is not an issue if you’re working on a game for a single platform, but usually you are not. For instance, if you’re working on an Android game, most probably you’d like to build an iOS version too. Unfortunately when you switch your target platform, Unity removes previously processed data as you’d never intend to use it again. As a result, switching between multiple platforms may take more time than actual development of your game!

Let’s install Cache Server

Cache Server solves that issue by storing processed assets in the persistent database for later use. If you’re working alone or if your internet connection is not that great, it may be a wise choice to install it locally.

Cache Server is written on the top of NodeJS. If you’re not familiar with this technology, don’t worry. It’s distributed along with cache server zip file, so you don’t even need to install it! (well, I don’t know now why I even mentioned it…)

Now it’s time to get the Cache Server files. These can be found on Team License web page (Team License is now available for everyone). Just click on the download button.

a280306e-a6e8-4ebe-806a-9ff04bd439a3

Unpack the downloaded zip contents in the location where you want to keep your processed asset database. Cache Server by default creates its database in-place, so be prepared to have at least 50 gigabytes of free space on your drive. Now you’re ready to launch it. Go into the CacheServer directory and double-click on RunWin.cmd file. There are appropriate script files for Mac and Linux too.

d4a57c8c-7df2-41ad-824d-881f87cdbfd2

When you see the output like this one, you know that your Cache Server is operational:

f7548b51-d00c-46d0-8afe-be95a1b26bd5

Now, don’t close this command line window! Your cache server works only when it’s open. When closed, you can re-launch it again and your data will be still there, but if it’s not running Unity is not storing anything in it (obviously).

Configuring Unity

Let’s now open Unity preferences. Go into the Cache Server tab, and use localhost as the IP Address.

3fc6f3ce-5cd7-43a8-8a67-6c7a6ea9b3bf

When you see Connection successful message, this means that your Cache Server is fully operational! Now you have to reimport your assets at least once to have it uploaded to the cache server. Later on instead of usual asset importing progress you will be seeing something like this:

using cache server

How long it will take depends only on the speed of your hard drive.

If you’re interested in more information about the Cache Server, you can find it in the official Unity manual. Please also note that Cache Server may require a license different than free (it got a little confusing after latest changes in licensing, so I cannot tell for sure).

Choosing Between Forward or Deferred Rendering Paths in Unity

One of the most important Unity features is the ability to choose a rendering path.  For those who are not very familiar with Unity, choosing (usually) between forward and deferred rendering paths may be something comparable to choosing between “normal”  and “strange looking and something’s broken” rendering methods. To understand better why there is more than one rendering path, first you will need to understand the motivation behind it.

It’s all about lighting

Lights are expensive, mostly because a lot calculations has to be done to find out the valid color of a pixel when there’s a light in range. In Unity lights can be evaluated per-vertex, per-pixel or as Spherical Harmonics (SH). In this article we will talk only about the former two.

pixel vs vertex lighting

In per-pixel lighting, each pixel color is computed individually (as on the left.) You can see that even when I use low-poly sphere for this example, the lighting still makes it look round. If it wasn’t for the edges, it’d be really hard to spot where all the vertices are. Then, there’s per-vertex lighting. It makes one light calculation per vertex. All the other pixels between vertices evaluate the color using regular color blending algorithm (without further light calculations.) This is the cheapest method of lighting and yeah… it looks cheap (if you’re wondering where’s the pixel and vertex lighting switch, it’s hidden in the Light component under Render Mode option. Important option is forces the light to be pixel light, Not Important is vertex light, Auto makes the strongest light a pixel light.)

It’s not a secret that game developers love per-pixel lighting much more than per-vertex lighting. Yet it has a significant downside. Each light causes the additional rendering pass of each object in the range. There’s a limit of four lights that can affect the object. What’s more, there’s also a limit of shadows – based on Unity documentation only one light can have shadows (for some reason I’ve managed to get two shadows in Unity 5.3.4, so I’m not really sure about this one.)

Deferred rendering to the rescue!

There’s a technique that allows you to use as much lights as you want on your scene with keeping the performance at reasonable level. It does not limit the number of shadows and it does not cause additional draw passes if scene objects are within light range (objects casting shadows are exceptions.) It’s called Deferred Shading Rendering Path.

4 lights deferred lighting

Why is it so different? Mostly because most of the models are rendered without lighting calculations and when the scene rendering is nearly done lights are applied to rendered 2D image. Making changes on this stage is usually called doing something in screen-space. Knowing that, we can say lighting in deferred rendering is screen-space. To understand it better, let’s look at the Frame Debugger.

Scene rendering starts with rendering all geometries:

deferred opaque

This is a flat image, so how graphics card will know how to apply lights and shadows? Thanks to the depth buffer! You can think of depth buffer as of another image that is hidden from you and that stores the information about how far from the camera each pixel is located. When represented as image, it may look like this:

cf538839-afaf-4afb-911b-52db5c062af7

Depth information alone isn’t enough to figure out how light should be applied on the surface. Still, we need at least one more thing – the orientation. Orientation in 3D space is usually represented by normals. The unusual thing is that along with color buffer and depth buffer, there is a buffer with normals!

346210b1-c144-4a66-b1bc-bd7488e3b06b

How can you tell that these are normals? It’s pretty easy! Just look at the Scene Gizmo.

scene gizmo

Do you see the color resemblance? Red cone (x) points to the left, so do left faces on previous image. Green (y) to the top and blue (z) bottom-right (from this perspective). It all matches the colors of faces from before.

Basing on that information, lights and shadows can be rendered. It really doesn’t matter how many objects there are on your scene. Everything gets done only on the final image.

After lighting pass

After lighting pass

The image above is the result is an inverted version (1 – color) of lighting pass. At the end it is blended with the first opaque image to get the final result.

Which one should I choose?

After reading all of this you may be full of enthusiasm to use the new rendering path, but hold your horses! Deferred rendering is not a remedy for all of the world problems. It has some…

Limitations

It would be too great to be true, wouldn’t it? There are some limitations.

First of all, deferred rendering does not allow us to render semi-transparent objects. That’s because if something semi-transparent exists on the scene, there’s no way to write down depth and normals for objects that is visible through semi-transparent objects and for current object itself. Unity handles this limitation rendering semi-transparent objects using forward rendering path at the end of the whole process. It works quite well, these objects can cast a shadow, but unfortunately they are unable to receive shadows from other objects. They can also cause some unexpected issues, not known when using forward rendering.

The second limitation is the lack of anti-aliasing support. The reason is similar to the issue with semi-transparent objects, but Unity does not try to workaround it in any way. Instead you can use screen-space AA algorithms (image effects), but the visual effect may be less good-looking.

Another limitation is that you can use up to four culling masks. In the documentation you can read:

that is, your culling layer mask must at least contain all layers minus four arbitrary layers, so 28 of the 32 layers must be set. Otherwise you will get graphical artifacts.

And finally there’s no support for the Mesh Renderer’s Receive Shadows flag.

Requirements

If that’s not enough, deferred rendering works only on a limited set of graphics cards. When it comes to PCs, you can safely assume that all graphics cards not older than 10 years will support it. When it comes to mobile devices, you should assume nothing. But that’s not a big issue, because…

Performance

The most important thing is that deferred rendering in most cases will get a worse performance on mobile devices than forward rendering. It’s because of additional passes that need to be done on each frame. If you’re using only one light, then it may not be worth it.

On the other hand, adding extra lights is quite cheap. In the worst case scenario performance will drop linearly and compared to forward lighting, it’s independent of number of objects on the scene.

Cities: Skylines (done in Unity) decided to use deferred rendering path. There's a lot of small lights in this game and it still performs really well.

Cities: Skylines (made with Unity) decided to use deferred rendering path. There’s a lot of small lights in this game and it still performs really well.

Resources

I hope that this article will cast some light on what rendering path you should choose for your game. Anyway, you may also be interested in these resources:

How to Use Unity’s Resources Folder

Unity has several kinds of special folders. One of them is the Resources folder. Simple concept of storing assets is well-explained in the official documentation:

Generally, you create instances of assets in a scene to use them in gameplay but Unity also lets you load assets on demand from a script. You do this by placing the assets in a folder called Resources or a sub-folder (you can actually have any number of Resources folders and place them anywhere in the project). These assets can then be loaded using the Resources.Load function.

Still the reason why we might want to use the Resources folder may be a little confusing. First you have to understand how Unity build process is works and how Unity is able to access game assets.

Unity build process

Before you will build your game, you have to declare what scenes your game consists of. All of this can be done in Build Settings window.

build settings window

There are at least two reasons why Unity asks you to do this:

  • It needs to know what scene should be loaded first (the top scene)
  • It needs to know what assets should be included in your build (dependencies)

What are scene dependencies? They’re assets which are connected to the scene hierarchy in any way, usually as a component field.

Unity Logo object contains Sprite Renderer object that references Unity Logo asset.

Unity Logo object contains Sprite Renderer object that references Unity Logo asset.

The dependency diagram may look like this:

dependency diagram 1

In this case there are two scenes. Scene 1 is using Asset 1 and Asset 2. Scene 2 is using Asset 2 and Asset 3. What happens if you decide not to build Scene 2?

dependency diagram 2

Only Asset 1 and Asset 2 will be included in the build since Asset 3 is referenced only by Scene 2, that is no longer included in the build. Thanks to this dependency tracking Unity will include in your build only these assets, which are actually used. Needless to say that you don’t have to worry about storing assets you’re not using at the moment. It will not affect your build size in any way.

Override!

There’s a way to get around this process. If you put your assets into a Resources folder, they will be always included in your build. But be careful! You need a really good reason to do so!

As I said before, in most cases when you need to use an asset, you make a reference to it within a scene. It’s really easy to use any kind of attached asset this way. So why would you need to use an asset without keeping a reference to it? There may be several reasons and each one depends on specific needs of the, but let’s look at one case what is quite common for most games.

When an asset is directly referenced from the scene, it will be loaded into the memory before your scene will be launched. Thankfully to that, player will not experience any frame-drops related to assets loading (with small exceptions). The price is of course the time needed for these assets to be loaded. Sometimes it may be not acceptable.

Example – loading screen with different backgrounds

Many game loading screens are displaying random images to be less boring.

Many game loading screens are displaying random images to be less boring.

Loading screen is something that usually is also a scene. Let’s think of a case when you want to display a random image on the background while your actual game level is loading. You’ve collected 15 images and you add these to loading scene images rotation script. It is working great, but when you play your game you realize that your loading scene requires more time to load than you need to pass your actual game levels!

This is caused by assets pre-loading mechanism and can be easily fixed using Resources folder. First remove all the references to your textures from the scene. Then put your images into Resources/LoadingImages directory like this:

resources images

Then somewhere in the code you can use a code like this one:

Note that Random.Range() returns a random number between first argument inclusive and second argument exclusive, that’s why there’s +1.

If you will need to attach this texture to an Image component, you can do it like this:

A word of caution

Use Resources folder only when you really need to. Loading assets on demand will make your FPS rate drop, and having indirect dependencies is makes your work much more difficult.  It’s worth to mention again that these assets will always be included in your build, even if you don’t use them. You have been warned!