Give Page Feedback | API

Chunk Streamers - Overview

Chunk Streamers are responsible for getting new Asset Chunks into your game and removing Asset Chunks that are no longer needed from the game. Many of the most common methods for loading/unloading assets have been covered by the default Chunk Streamers, including loading/unloading scenes (either Addressable or not) and instantiating/destroying prefabs (again, either addressable or not), however you are free to create Custom Chunk Streamers to add other loading/unloading methods (including future asset types Unity might add).

In addition, you can use them to change the logic behind what is loaded. This is especially useful for procedural worlds, where rather than loading a preset group of Asset Chunks based on an editor configured layout, the determination about which Asset Chunks to load is determined at runtime.

No matter which option you choose, you will find dedicated Sections for each within this Chapter.

Common Concepts

The remaining Sub Sections cover concepts common to all Default Chunk Streamers, therefore rather than re-write the same information on each Chunk Streamer Section, we have decided to put the information here.

Two Frame Loading

The preferred and most performant way of loading Asset Chunks is over multiple frames, however when the SAM Initializer is initialized in a non-gradual manner, the ability to load Assets in two frames max becomes necessary. Out of the four default Chunk Streamers, only the Addressable Scene Streamer is incapable of two frame loading. In addition, on the WebGL platform, the Addressable Prefab Streamer is also incapable of two frame loading.

When possible, single frame loading is preferred, however for some asset types, a pre-load frame is required to get the assets into the game within two frames (for example, non-addressable scenes).

When creating custom Chunk Streamers, it's up to you on whether you want to support Single/Double Frame Loading. If you don't want to or can't support it, simply avoid using non-gradual SAM Initializer Initialization.

Chunk Streamer Users

Each Zone LOD Group that uses a Chunk Streamer registers with it. When it does, the streamer creates a special user object internally to store user related data, and gives calling code an ID so that when they communicate with the Streamer, the Streamer will know which user data to use for load/unload operations. This allows Chunk Streamer's to be used with multiple users, so long as all users find the Chunk Streamer's settings (set in its inspector) acceptable.

The Loading Process

In Unity when using SAM, the loading process typically involves one or two precursor steps in order to prepare the system to load a single Asset Chunk, followed by the actual loading of the assets (which occurs on a background loading thread, and involves loading both the asset chunk and any dependencies it relies on), and then finally an integration step where the asset chunk + dependencies are integrated onto the main thread (unloading works more or less in reverse).

Unity gives us some control over this process by allowing us to set the maximum amount of frame time the integration/de-integration step takes in a single frame. This is controlled using the Application.backgroundLoadingPriority property. You can set this value manually or let the Chunk Streamer do it for you by enabling Set Load Priority and choosing an option from the Load Priority setting. Which value you use is largely dependent on your target frame rate, or more specifically, how much CPU time you have to work with each frame. In most use cases, using a value of BelowNormal is recommended, which equates to around 4ms of frame time.

A lower value will be more performant but loading will be slower, while a higher value may be less performant but will result in faster loading.

Note that these settings are global and apply to all Chunk Streamers (including custom ones). You can adjust the settings on any Chunk Streamer you have in any of your scenes, and the changes will apply across all Streamers. You can also configure the SAM Initializer to use a higher temporary setting during initialization, which can be useful for getting the initial game up and running faster!

Additional Loading Performance Settings

Prior to v2.0.0 of SAM, the number of Asset Chunks that were loaded or unloaded was controlled via a single setting called Async Load Strategy. The options for this setting were as follows:

1) One Chunk At A Time - Only a single Asset Chunk was loaded by the Streamer at a time.

2) One Cell At A Time - All Asset Chunks belonging to a World Cell were loaded at the same time.

3) All Together - All Asset Chunks from all World Cell's (in a single LoadAndAttachChunksToCells method call) were loaded at the same time.

In v2.0.0 (and greater), we have replaced this setting with some new settings which give you a greater deal of control over the loading and unloading processes. These are:

1) Default Max Async Load Ops To Start In A Single Frame - Controls the number of async load ops that can be started in a single frame. Async ops are usually executed in a fixed way. On the frame you start the load op, there is a specific operation that tells Unity to start preparing for the load, which triggers some file read operations on a background thread. Two frames later Unity actually kicks off the loading, where the work is delegated to a background loading thread. One or two frames after this (or more if loading large assets), the asset are integrated onto the main thread.

Most of these operations conflict with each other, so if you try to start too many in a single frame you increase the likelihood of running into problems.

You can look for the SAM.InitializeAsyncLoadOfAssetBundle Profiler Marker, and if you see a large amount of time wasted due to Semaphore.WaitForSignal under this marker, it indicates you need to reduce the number of load ops that can be started in a single frame!

2) Default Max Concurrent Async Operations - This is the maximum number of async load ops that can be running at the same time. In our experience setting this to a value of 0 (which means there is no limit) is usually best, as setting the max async ops that can be started in a single frame is usually sufficient to eliminate thread switching issues, which is generally the cause of most performance issues. However, feel free to experiment with different values!

3) Default Max Chunks To Configure In A Single Frame - Configuration is an operation that varies depending on the type of assets you are loading. For prefabs, it involves the instantiation of the underlying prefab asset and its attachment to the World Cell, while for scenes it only involves the attachment of the Asset Chunk to the Cell. Since instantiation can be a heavy operation, using a fixed value can be important to control the impact of the instantiation on game performance. On the other hand, since the Scene Configuration is close to free, you can usually use a value of 0 (no limit).

4) Default Max Unload Ops To Start In A Single Frame (Not Found on Prefab Chunk Streamer) - Similar to 1, except this controls how many unload ops can be started in a single frame. A value of 3 works well in our experience, however in many cases you may find a smaller or larger value works better.

5) Default Max Async Handles To Release In A Single Frame (Addressables Only) - How many addressable handles can be released in a single frame? You can usually use a value of 0 (no limit), however you should profile to make sure there are no issues

6) Default Max Concurrent Unloading Scenes (Addressable Scenes Only) - The number of async unload ops that can be running at the same time. A value of 5 works well in our experience, although you may find other values work better for your particular game!

When profiling these settings, the biggest indication of a problem will be in the form of Semaphore.WaitForSignal or more commonly Loading.LockPersistentManager.

Also pay special attention to the amount of time used on the background loading thread! Because typically only 2 Asset Chunks can be loaded in the background in one frame, you should strive to fill the background thread as much as possible without going over the single frame CPU time allotment, so it can do as much work as possible in a single frame. If the background thread is only executing for half of the time, it indicates that your Asset Chunks can and should be larger (i.e., maybe your Terrain can be bigger, or your Multi Chunks can have more children per chunk).

On the other hand, if you are noticing that the background loading tasks are flooding over from one frame into the next, this will usually cause thread switching issues (visible as Loading.LockPersistentManager during the Integrate step in the profiler), indicating that your Asset Chunks need to be smaller.

Overriding Performance Settings

The settings listed above are default settings, as indicated by their names. In some situations, it may make sense to use alternative values for one or more of these settings, for one or more Zone LOD Groups. You can do this using the Extra Data feature on the Streamable Grid. Just provide the key the Streamer should use to access the override, and if a Zone LOD Group contains Extra Data that uses that key, it will be used rather than the Default value.

In addition, each Default value can be modified at runtimen using a similarly named method. For instance, the Default Max Async Load Ops To Start In A Single Frame value can be modified using the UpdateDefaultMaxAsyncLoadOpsToStartInSingleFrame method. When you modify a default value, the change is applied to the loading/unloading of any Zone LOD Groups which are using the Default value (i.e., that are not using an Extra Data Override). Do note, however, that modifications are not carried over between game saves, so you'll need to make the modification each time your game runs.

Memory Freeing

All Chunk Streamers contain a Memory Freeing Strategy setting which can be used to conveniently release certain memory after unloading chunks or cells, however the memory freeing method (Resources.UnloadUnusedAssets), although asynchronous, is still a very expensive operation to run and its execution can decrease your game's performance. As such, it is generally recommended to leave the strategy set to Dont Free Memory, and instead manually call Resources.UnloadUnusedAssets when it's performance impact will not be noticed by the player (such as when the player is standing still for a consistent time period).