Unity Addressables: always enough memory

image

You lead a team of several programmers and artists working on porting a beautiful PS4 VR game to Oculus Quest. You have six months to do it. What will be your first move? Let's try using Unity Addressables.

You understand that you have to solve several rather difficult tasks at the same time. Some will be more difficult for you than others, it depends on your experience in each of the areas. If you choose which of them deprived you of sleep most often, then what will it be?

I suppose the following: approximately 70% of readers will say that the biggest problem when porting a game to Quest is CPU / GPU performance. I can answer this: most likely you are right. Improving productivity is one of the hardest areas in a VR game. Optimizations of this type require a thorough study of the product, and this takes time. Sometimes further optimization is impossible, because of which you usually have to get rid of the costly elements of gameplay and graphics. And itโ€™s dangerous to disappoint players.

Speed, speed, speed ... What to expect in this regard from the Quest platform? How productive is it? The fact is that if you already had experience in developing it, then you know that despite its mobility, it is surprisingly powerful.

โ€œCome on, author, why are you lying? My phone starts to slow down as soon as I open the second browser tab. How can you say that mobile platforms can be productive? โ€

The huge difference lies in the Quest active cooling system , which gives a huge advantage to its CPU / GPU that is not provided by any other mobile platform. This is a powerful fan that blows dust off your hair and keeps the processor from melting on your face.

In addition, a more specialized OS is better optimized for rendering virtual reality (surprise) than standard Android. Over the past few years, mobile hardware has begun to quickly catch up with fixed platforms.

But at the same time, I will not deny that the task of constant rendering with a frequency of 72 fps will be difficult, especially for the ports of VR games that come from powerful platforms. When we talk about Oculus Quest, you can imagine the Snapdragon 835 with a screen, battery, four cameras and a fan.

What looks like a flaw can actually be taken as an advantage. This mobile platform is a well-researched device with weak iron. We can say that there are a thousand known tricks to quickly reduce the load on the CPU and GPU to an acceptable level. If you are interested, then you can read about it in my subsequent posts. And in this article we will put performance out of the question.

Our attention in this problem may be that, in comparison with PS4, Quest has one hardware characteristic that is half as much: RAM capacity . Namely, the volume decreased from 8 to 4 GB of RAM. This is an approximation, because on both platforms the operating system does not allow to use them completely, so that you can trackseveral subsystems necessary for ecosystem functioning. In Quest, you can use up to about 2.2 GB of RAM; if more, chaos is already beginning.

โ€œBut what do you mean by chaos ?โ€ The fact is that for the game it is critical to implement the correct memory management. It happened because we have two limitations:

  • Hard memory limit : if you exceed a certain threshold, then the OS will just kill the game
  • Soft memory limit

Obviously, we do not want the first or second to happen in the game. Can you imagine the fury of a player who has lost the last two hours of passing? Yes, he will definitely go to the application store and will not say anything pleasant there.

Of course, the guaranteed availability of 2.2 GB of RAM is not so much. Usually this is not a problem for new projects in which statistics are constantly monitored from the very beginning, but it definitely becomes a difficulty for a port with much weaker hardware.

If you have dealt with similar ports in the past, you will quickly realize how extremely difficult it becomes to halve the amount of available RAM . It greatly depends on how well the gameโ€™s architecture was ready for such a change, but in most cases it only leads to tears.

The most popular strategies for reducing memory requirements are changing the compression parameters of assets, optimizing scripts, reducing shader variations, etc. Most often, the very first decision is to change the texture import settings, but if necessary, you can use compression of meshes, animations, and sound. The problem is that such techniques are usually complex and have their own ceiling .

Not all platforms support the same import parameters: when developing for different devices, the assembly pipeline costs significantly increase, not to mention the complexity of QA, design graphics and programming. For example, does this support Android ASTC device, or only ETC2 (and generally any of them)? Oh, and we still need 64-bit builds, but at the same time we want to keep players with 32-bit versions. How many individual APKs do we need to create and test for each update in the game? If you want to simplify your life, then you should not rely only on these techniques.

Therefore, we need to go deeper. Of course, we want everything to be as simple as possible, especially when creating a port. Recycling the game entirely for performance is an even worse option than just not porting it. As part of the topic of the article, I will show one of the most important advantages: you will learn how to reduce the required amount of memory by half in just a few hours .

Is not that great?

Well, let's, let's, ask me: is this really possible in your case? I will answer: it depends on the initial conditions, but in my experience, the answer is YES . Unity Addressables can do a great service here. What is the trick? You have to invest and master the process. But such a workflow will allow you to win the title of employee of the month.

If you are interested, then continue reading.

In this post, we will go from traditional asset management to addressables-based asset management . To illustrate this process, we are porting a simplified old-school project to the new era of Unity Addressables.

You may ask a question: why don't you just show the result at your real job?

In a world without competition, I would just show you all the materials I created. However, in the real world, I will most likely be punished for this. And then theyโ€™ll put me in jail.

So instead, I offer my help: we will work out a project that presents all the difficulties that you will encounter tomorrow in your next project. And to begin with, we'll take Unity Addressables into our family of recommended packages .

In this post I will introduce you to Addressables so that you can implement your own Unity Addressables system in minutes .


Unity Addressables: why are they needed?


Attention should be paid to this important section. Our task is to recognize simple ways to optimize memory usage and quickly implement them. There are various ways to do this, but one of the most powerful and at the same time simplest is to load the first scene and launch the profiler. Why?

Because the non-optimized architecture of the game can be recognized at any time of the gameplay , so the fastest way to check this is by profiling the first scene. The reason for this is that the often overly active use of scripts like singleton-s containing links to all assets, just in case .

In other words, many games usually have an omnipotent script that creates hell of links to assets .This component keeps every asset loaded constantly, regardless of whether it is currently being used.

How bad is that?

Situations are different. If your game is likely to be limited by memory? then this is a very risky decision, because the game will not scale well with the increase in the number of added assets (for example, think about future DLCs). If you are developing for heterogeneous devices, for example, for Android, then you do not have a single amount of memory; Each device has its own capacity, so you have to rely on the worst case. The OS can decide to kill the application at any time if the user suddenly switches to reply to a message on Facebook. When he returns, a surprise will be waiting for him - the game has already been closed.

Is it fun?

Absolutely not.

Complicating the situation is the fact that if you later decide (or someone decides for you) to port the game to another, less powerful platform while maintaining cross-play, you can only wish good luck. You definitely will not want to face such a technical problem.

On the other hand, are there situations in which traditional asset management is appropriate? Yes of course. If you are developing for a uniform platform, such as for PS4, and most of the requirements are known from the very beginning, the benefits of global objects could potentially outweigh the added complexity of an improved memory management system.

Because you have to admit: the good old global object that stores everything we need is a simple solution if it suits you. It will simplify the code and preload all the referenced assets.

Be that as it may, traditional memory management is unacceptable for developers seeking to maximize the use of iron resources . You are reading an article, which means you want to improve your skills. So the time has come to do it.

Meet Unity Addressables.

Project Requirements with Unity Addressables


If you plan to just read this post, then the screen will be enough. If you want to do everything with me. then you will need the following:

  • Hands
  • Clever mind
  • Unity 2019.2.0f1 or higher
  • Level 1 project with GitHub (download zip or via command line)
  • The desire to delve into the insides of Unity Addressables

The git repository contains three commits, one for each level of this post (unless I mix something up and create a commit with a fix).

Download the project in ZIP format directly from GitHub


Level 1 Developer: Traditional Asset Management


We will start with the simplest asset management method. In our case, for this we have to make a list of direct links to skybox materials in the component.

If you do this with me, then preparing will take three simple steps:

  1. Download the project from git
  2. Open a project in Unity
  3. Click the play button!

Fine. Now you can click on the buttons to change the skybox. So original ... and boring. As I understand it, so far no Unity Addressables.

Soon you will see why we endure these moments of boredom.

Firstly, how is our project structured? It relies on two main systems. For one thing, we have a Game manager object . This component is the main script that stores links to skybox materials and switches them depending on UI events. It's pretty simple.

using UnityEngine;

public class Manager : MonoBehaviour
{
    [SerializeField] private Material[] _skyboxMaterials;

    public void SetSkybox(int skyboxIndex)
    {
        RenderSettings.skybox = _skyboxMaterials[skyboxIndex];
    }
}

Manager provides the UI system with a function to apply specific material to the scene through the use of the RenderSettings API .

Secondly, we have a CanvasSkyboxSelector . This game object contains a canvas component that renders a set of vertically distributed buttons. Each button, when pressed, calls the aforementioned Manager function , which replaces the rendered skybox depending on the id of the button. In other words, the OnClick event of each button calls the SetSkybox function of the Manager object. It's simple, isn't it?


Unity Addressables - scene hierarchy

Now it's time to get started. Let's open the profiler ( ctrl / cmd + 7 or Window - Analysis - Profiler ). I will assume that you are familiar with this tool, which means you know what to do with the top record button . After a few seconds of recording, stop it and look at the metrics: CPU, memory, etc. Is there anything interesting?

Performance is pretty good, and this is not surprising given the scale of the project. You can simply turn this project into a VR game and I guarantee that none of the players will feel sick, as is often the case in Eve: Valkyrie .

In our case, we will focus on the memory section. In simple viewing mode, you will see something like this:


Level 1 asset management - simple memory profiling

Texture size values โ€‹โ€‹seem too big to display one skybox at a time, don't you find? A surprise awaits you: a similar pattern can be found in many non-optimized games, the development of which you will lead . But in our case itโ€™s just a set of skyboxes. In other projects, these will be characters, planets, sounds, music ...

If the responsibility for working with a lot of assets lies with you, then I am glad that you are reading this article. I will help you make the transition to a solution that scales well.

The time has come for magic. Switch the memory profiler to verbose mode. Watch it!


Level 1 Asset Management - Detailed Memory Profiling

Damn it, what happened here? All skybox textures are loaded into memory, but only one of them is displayed at a time. See what we got? This raw architecture takes as much as 400 MB .

This definitely does not suit us, given that this is just a small piece of the future game. The solution to this problem itself will become the basis of the next section.

Summarize:

  • Traditional asset management involves direct links
  • Therefore, all objects are constantly loaded
  • The project does not scale well


Level 2 Developer: Unity Addressables Process


In games, we start from level 1, and that suits us, but as soon as we figure out the rules of the gameplay, it's time to leave the safe city walls and raise our level. This is what this section is about.

Now download the level 2 project .

As we saw earlier in the profiler, all skyboxes are loaded into memory, even though only one is actively used . This solution does not scale, because at some stage we will be limited by the number of different asset variations that we can offer the player. What advice can I give? Do not limit the interest of the game to users.

Let me help you. Take a shovel to dig a jailbreak tunnel of traditional asset management. Let's add a new interesting tool to our collection: the Unity Addressables API .

The first thing we need to do is install the Addressables package. To do this, go to Window โ†’ Package Manager :


Unity Package Manager - Unity Addressables

After installation, we need to mark the materials as addressables. Select them and activate the addressables flag in the inspector window.


Managing Level 2 Assets (Unity Addressables)

So we politely ask Unity to include these materials and their texture dependencies in the addressables database. This database will be used during builds to pack assets into chunks, which can be easily loaded at any time in the game.

Now I will show you something cool. Open Window -> Asset Management -> Addressables . Guess what this is? This is our database that is eager to come to life!


Managing Asset Level 2 Unity Addressables is the main window

Dear Reader, this was the easy part. And now the fun begins.

I want you to visit our old friend from the previous section: Sir Manager . If we check it, we will find that it still stores direct links to assets! We do not need this.

Instead, we will teach the manager how to use indirect links, i.e. AssetReference (in Unreal Engine they are called soft references).

Let's make our component more beautiful:

using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class Manager : MonoBehaviour
{
    [SerializeField] private List<AssetReference> _skyboxMaterials;

    private AsyncOperationHandle  _currentSkyboxMaterialOperationHandle;

    public void SetSkybox(int skyboxIndex)
    {
        StartCoroutine(SetSkyboxInternal(skyboxIndex));
    }

    private IEnumerator SetSkyboxInternal(int skyboxIndex)
    {
        if (_currentSkyboxMaterialOperationHandle.IsValid())
        {
            Addressables.Release(_currentSkyboxMaterialOperationHandle);
        }

        var skyboxMaterialReference = _skyboxMaterials[skyboxIndex];
        _currentSkyboxMaterialOperationHandle = skyboxMaterialReference.LoadAssetAsync();
        yield return _currentSkyboxMaterialOperationHandle;
        RenderSettings.skybox = _currentSkyboxMaterialOperationHandle.Result;
    }
}

Here the following happens:

  • 7, (AssetReference) . , . . .
  • 13: , . ,
  • 18-20 , , , , . , API Addressables, , . โ€” , , addressable-.
  • addressable 23, LoadAssetAsync, yield ( 25), . . !
  • , , , 26. Result, , .


Level 2 Asset Management (Unity Addressables) - AssetReference List

Remember: this code is not ready for production. Do not use it when programming an airplane. I decided that for the sake of simplicity, sacrifice reliability.

But enough explanation. It's time to see him in action.

Please follow these steps:

  1. In the addressables window, prepare the content ( build player content )
  2. Then build for the selected platform
  3. Run it and connect the profiler (memory) to it.
  4. Do not drop your jaw in surprise.


Level 2 (Unity Addressables) - Build Player Content


Level 2 Memory Management (Unity Addressables) - Memory Profiler

Aren't the cooked assets tasty?

I like it when the profiler is satisfied. And now we see the happiest profiler in the world. A satisfied profiler means the following: firstly, there are more satisfied players who can play your game even on Nokia 3210 . Secondly, these are satisfied producers. And for you, this means that your wallet will be satisfied.

This is the power of the Addressables system.

But Addressables impose small additional labor costs. On the one hand, programmers need to provide support for asynchronous workflows (this is easily implemented using coroutine). In addition, designers will have to study the capabilities of the system, for example, addressable groups, and gain experience for making informed decisions. And finally, the IT department will be very happy that you will have to configure the infrastructure for transferring assets over the network if you prefer to host them online.

I must congratulate you. I will explain what we have achieved:

  • Proper memory management.
  • Faster boot up.
  • Faster installation time, reduced application size in the store.
  • Increased device compatibility.
  • Asynchronous architecture.
  • They opened the door for storing this content online โ†’ that is, to the separation of data from the code.

I would be proud of such achievements. This is a good return on our labor investment.

Oh, and don't forget to mention the experience with Addressables in an interview.

Supporting materials: creating instances and link counting. Information on this topic can be found in my post .

Optional: alternative download strategies. You can read about them in my post .

Summarize:

  • Addressables-based asset management scales remarkably well.
  • Addressables add asynchronous behavior
  • Do not forget to prepare the content for changes, otherwise the game will have a pinkish tint!


Level 3 Asset Management (??) - Network Content Delivery

Level 3 Asset Management (??) - Network Content Delivery


In the previous section, we made the most important breakthrough. Improved your skills by moving from a traditional asset management system to addressables-based workflow. This is a huge victory for the project, because thanks to the small amount of time we have provided space for scaling the volume of assets, while maintaining a low level of memory consumption. This achievement actually upgraded you to level 2, congratulations! However, we still have to answer another question:

Is this all?

No . We barely touched on the topic Addressables, there are other ways to improve the project thanks to this powerful package.

Of course, you do not need to remember all the details of using Addressables, but I highly recommend that you read them briefly, because in the future you will most likely meet with new tests, and you will be grateful for a deeper study. That is why I prepared another short guide.

From it you will learn about the following aspects:

  • Addressables Window : Important Details
  • Addressables profiling : don't let memory leaks ruin your life
  • Network Delivery : Reduced time from installation to gameplay
  • Assembly pipeline integration
  • Practical strategies : speeding up the workflow, eliminating the need for ten-minute coffee breaks

And, more importantly, we will answer the following questions:

  • What is the hidden meaning of Send Profiler Events ?
  • How useful is the AddressableAssetSettings API ?
  • How to integrate all this with the BuildPlayerWindow API ?
  • What is the difference between Virtual Mode and Packed Mode ?

Level 3 guidance can be found in my post .

All Articles