Starting async load causes massive lag spike

This has had be stumped for a while now. In certain places in my game, I’m trying to asynchronous load the next scene in the background while the user is doing something else. This helps cut down the loading screen time the user sees.

I understand async loading is a 2 step process. Step 1, load data for the scene from disk into memory. This step is the one that’s able to be done asynchronously. Step 2, activate the new scene (e.g. call Awake and OnEnable for all enabled objects in the new scene). This step is not asynchronous.

This is all well and good. I can start the async load and disallow scene activation so step 1 takes place when the user doesn’t notice. Then, when the user decides to proceed to the new scene, I show a loading screen, then a allow scene activation.

The problem is that in certain circumstances, simply calling Application.LoadLevelAsync(sceneName) causes a massive 6 second stall in the application. So far, it seems like this only happens if you start an async load too quickly after a previous load operation. This stall shows up in the profiler under Loading.ReadObject > Loading.Lock PersistentManager. Unfortunately, I see no way to know how soon is too soon. And having a random 6 second stall is obviously unacceptable for the end user. What’s going on here? How do I prevent this?

The particular scenario in my game is as follows:

  1. Application opens to the Main Menu
  2. User chooses to start a game
  3. A “Garage” scene is loaded synchronously where the user can choose a motorcycle and rider
  4. As soon as the Garage is loaded, asynchronous loading of the first racetrack scene is started
  5. Application immediately stalls for 4-6 seconds.

I believe that if I delay 4) long enough, the stall will not occur. It seems as though the previous load operation is still performing some operation in the background and attempting to start a new load causes the application to wait for the previous operation to finish before starting the new one and continuing execution.

I’ve attached a screenshot of the profiler when this occurs. The first spike is the Garage scene activation. The second one is some initialization going on in that scene. There’s a 1 second delay. Then there’s a large spike from simply starting an async load (with scene activation disabled). I hacked in the delay to make the spike clearer.

EDIT: I tried delaying the async load for a full 30 seconds and the stall still occurs.

Are you profiling this in a standalone build or in the Editor? Especially in 4.x the async calls are actually pretty synchronous in the editor.

1 Like

The screenshot and numbers are from the Editor. It’s greatly reduced in a build, but the problem still exists. I’ll get some exact numbers tomorrow.

Looking at that profile it appears that all of the time is going into an AmplifyMotion call. Do you still get the stall if you disable AmplifyMotion? I wonder if they have some kind of bad interaction with async loading?

If you look at the stack in the profiler you’ll see the time is all spent in the Loading methods, not in the Amplify script. The Loading stuff shows up in seemingly random places. Before I added the delay it was showing up under GameObject.SetActive.

1 Like

Yeah, I saw that. It’s just curious that it shows up in Amplify at all. If those are internal functions, I’d expect to see them called by other Unity functions, not just directly from a custom script. Even if it moves around a lot, it’d be interesting to see what actual function calls are responsible for triggering that Loading Lock. Doing so might help find a way around the problem. While writing SECTR STREAM, I had to do stuff like that a lot, just isolate bad behavior and find an alternative implementation that avoided the internal issue.

After testing with a build, this issue does still occur. It doesn’t stall for multiple seconds, but there are bunch of heavy frames that end up dropping the average framerate from 180 to 25 on my machine. I’ve attached screenshots of each of the >50 ms frames. It appears most of the hits are from parsing shaders.

This makes for a crummy user experience and I’m wondering if I should avoid using async loading altogether. I’d really hate to have to avoid an engine feature because it performs so poorly, though.

Any chance someone from Unity could comment on what’s happening here and/or how to prevent it?

EDIT: I’ve confirmed this occurs in 4.6 builds as well.





I’m still trying to understand this issue if anyone has any input.

That shader compile has to happen sometime. You can try adding a call to PrewarmShaders at the start of the game or level load, but I don’t know how comprehensive that will be for objects in other scenes. You may need to have all shaders referenced in the main scene and then call prewarm to get rid of the hitches.

I thought shaders were compiled at build time? Why does this happen at runtime? And why every time the game is run instead of just the first?

It depends on your target but the gles spec doesn’t allow precomputation and it’s impractical on many regular GL systems.

This is running on Windows, so it should be using DirectX 10, not OpenGL.

Seems like they should be precompiled. I’d still try the call to prewarm shaders to see if it helps. Clearly, something thinks it needs to compile shaders.

Any improvement on this subject? I’m facing the same issue but on Android (while profiling the device, as it happens on build too)

1 Like

I’m getting a similar issue (v5.0.0f2 beta).

I get about 3ish huge spikes w/ a lot of time spent in Loading.ReadObject > Loading.Lock PersistentManager. Anyone know what this routine does?

I doubt this has anything to do with shader compiles, because I do a Shader.WarmupAllShaders() before the spikes happen. It happens in the midst of doing my async level loads.

3 Likes

I’m also getting the same issue on 4.6.3f1. It happens right when the Application.LoadLevelAsync call is made and it seems very unlikely that it has anything to do with Prewarming shaders since I set async.allowSceneActivation = false; and the scene does not switch over to the new one until about 5 seconds later.

Unity 5 has the same issue :frowning:

1 Like

I’ve put in a bug report for this (#677082) but haven’t heard back so far.

We’re having the same bug, but it seems to be only in a built player. We load small terrain chunks asynchronously and it works quite well, but every now and then Loading.LockPersistentManager shows up and takes a lot of time. It happens when we access the ResourceRequest.asset value. When I decompile UnityEngine.dll, it shows that ResourceRequest.asset calls Resources.Load(string path, Type systemTypeInstance);

I guess it has to do with some kind of multithreaded lock. Resource.Load must try to access some locked value while another background loading is taking place in another thread, making the main thread wait for this loading to end. It seems to happen less frequently in FixedUpdate(), but it may be an illusion.

No matter where I put the ResourceRequest.asset call, it still happens. It would be pretty nice if Unity would fix this. I might try to make a small project to submit a bug, but it’s a bit tricky.

This is a really annoying issue… I have created a Unity Answers for it just in case some kind of Unity God exists and knows the answer.

There.