Search Unity

How does Application.LoadLevel() work under the hood?

Discussion in 'Editor & General Support' started by BTStone, May 13, 2015.

  1. BTStone

    BTStone

    Joined:
    Mar 10, 2012
    Posts:
    1,084
    Hey fellow devs,

    I need some in-depth-knowledge about how Application.LoadLevel() works. As for that matter how Unity handles scene-transitions.

    We're developing a PS Vita exclusiv game - that being said we do have the hardware to test our game for the device (a DevKit and a TestKit).
    Now this is the problem:

    We have two scenes.

    Scene_01:

    Scene_01.png

    And here's the Scene_02:

    Scene_02.png

    As you see the second scene is bigger. We're working with 2DToolkit for our Sprites and Ferr2D for our Terrains. And as you see, this scene has more sprites and terrains to show.

    If you look at the first scene there's a Spot labeled"SP_01" - there's a TriggerBox where the player can press the Up-Button on the device to "march" into the woods - At this point the screen will fade out, the next scene is loaded and will fade in. So far so good.

    The funny thing is that we got a bunch of errors using Application.LoadLevel() for this.
    I reported the problem in the official Sony DevNet-Unity forum and the errors will be fixed in a soon-to-be-released Patch, but there's a more crucial problem for us: the loading time.

    In Editor loading the second scene takes roughly a second, which is great.
    On the PS Vita DevKit/TestKit it takes up to 12 seconds. This is an absolute NoGo.

    Now what's really funny is our workaround:
    Instead of using Application.LoadLevel() we do this:

    Code (CSharp):
    1. for (int i = 0; i < levelObjectsToDestroy.Length; i++)
    2. {    
    3.      Destroy(levelObjectsToDestroy[i]);
    4. }
    5.  
    6. Application.LoadLevelAdditive(levelToPreLoad);
    - we're populating a GameObject-Array with all the GOs of the current scene
    - when the Player reaches the TriggerBox and hits up, we're deleting all these GameObjects
    - after that we use Application.LoadLevelAdditive() instead of .LoadLevel()

    -> Boom! Loading Time goes down to roughly 1 second, like in the editor. From 12 seconds with LoadLevel() to around 1 second with LoadLevelAdditive()

    (For the record, deleting the GameObjects and using .LoadLevel() shows no difference, still 12 seconds!)

    I would really like read an explanation about how .LoadLevel() works internally, since we had the impression that our approach with deleting all the LevelObjects and then loading the next scene with LoadLevelAdditive(), is somewhat similar to .LoadLevel(), but it seems it isn't.


    So I'm all ears! :)
     
  2. mbowen89

    mbowen89

    Joined:
    Jan 21, 2013
    Posts:
    635
    What happens if you load an "empty" scene between?
     
  3. BTStone

    BTStone

    Joined:
    Mar 10, 2012
    Posts:
    1,084
    Same results, same results with LoadLevelAsync(), too.
     
  4. mbowen89

    mbowen89

    Joined:
    Jan 21, 2013
    Posts:
    635
    Have you tried removing some stuff from the next scene (just for testing) to see if it's certain things that are causing the load times more than others?

    Also, maybe try building it to Android or something, just to see how the level loading compares to the Vita build, if that's an option for you.
     
  5. BTStone

    BTStone

    Joined:
    Mar 10, 2012
    Posts:
    1,084
    Yeah, we tried that, doesn't have that much of an impact (saving maybe 1-2 seconds). As mentioned above, we have a workaround which works great. With LoadLevelAdditive() we have loading times of ~1 second. Can't beat that.

    I just would like to know how LoadLevel() works exactly in order to understand and find a real solution to that problem. Maybe it's a Unity bug, maybe it's a PS Vita thing.

    Trying to built for Android is sadly not an option, since we don't have Android-Devices :/
     
  6. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    456
    HI,

    I believe what you are seeing is caused by asset garbage collection that happens when you use Application.LoadLevel. The same would probably happen if you called Resources.UnloadUnusedAssets().

    When using LoadLevelAdditive we don't do asset garbage collection.
     
    BTStone likes this.
  7. Kiori

    Kiori

    Joined:
    Jun 25, 2014
    Posts:
    160
    @SteenLund is this caused because part of the assets are still in memory while the new scene is being loaded?
     
  8. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    456
    Yes.

    When loading a new level the steps are roughly:
    1: Load all new assets required
    2: Load new level GameObjects
    3: Destroy previous level GameObjects
    4: Awake new level GameObjects
    5: Clean up unused assets

    The reason for not destroying all assets before loading the new scene is that the new scene might reference some assets from the previous scene, so we don't want to load them again.

    On most platforms step 5 is not that slow but on others it is because it looks the step can potentially visit a lot of memory to figure out what assets are referenced.
     
    BTStone likes this.
  9. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    456
    Though I have to add, 12 seconds spent cleaning up assets does sound like a lot, so I could be complete wrong.
     
  10. BTStone

    BTStone

    Joined:
    Mar 10, 2012
    Posts:
    1,084
    Hey @SteenLund

    thanks for the explanation!

    Yeah, I already thought of the GC as the main-reason for this. The thing with our scenes is all the assets are identical. They are the same materials, textures for Sprites and Terrains. In the second scene there are just more Sprites/Terrains from the very same TextureAtlas.
    Don't get why it takes so long for cleaning things up, because basically there isn't much to clean up, since the same assets are within the next scene.
    So the system should realise: "Oh, the next scene has the same textures I already have in memory, no need to unload them. I'll just load the new GameObjects of that scene and destroy the ones from the previous scene."

    Maybe it is a PS Vita-thing. Was told that ~10 seconds of loading isn't that uncommon on the Vita :/
     
  11. Kiori

    Kiori

    Joined:
    Jun 25, 2014
    Posts:
    160
    @SteenLund
    So if the same asset/object is used in both scenes, is it kept the way it was or is it reset?
    Is there a way to keep in, in case its reset?

    Also, if he used assetBundles would that make it easier on the GC for whatever strange reason?
    Do assetBundles influence this process in any way?

    Maybe something like that is also lagging the system considering BTStone said its all more of the same...

    By reset i mean, lets say a character has 'blood' on him because he just killed some zombie-figure, so he has a property applied to him(through whatever method) that make the gameobject effectively different than the standard version, which has nothing on it.
    On the new scene, is the blood still there, so effectively it got a dontDestroyOnLoad thing going for it, or did it get reset, and recreated, while simply reusing the character sprite?


    @BTStone
    And yeah Sony systems are known to have issues with resource management, since ever really.
    Porting to their systems, historically, is the stuff of legends, 'here there be dragons'.
    That' why MGS is such a technical achievement.
     
    BTStone likes this.
  12. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    456
    The assets are not unloaded, I simply think that checking the heap for references is the killer in this case. There is also other stuff like managers being loaded and RenderSettings being reset that happens when calling LoadLevel, which does not happen when using LoadLevelAdditive. All those things add up to make the loading appear slow on the Vita
     
    BTStone likes this.
  13. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    456
    No AssetBundles would not make it easier on the GC. With AssetBundles you would need to make sure there are no more references to assets and then call Resources.UnloadUnusedAssets to regain some memory, but that can be very slow.

    If you are using DontDestroyOnLoad your character is NOT reset during scene loading, he will remain bloody and still feel guilty about the killing he has done because zombies were people with families once :)
     
    BTStone likes this.
  14. Kiori

    Kiori

    Joined:
    Jun 25, 2014
    Posts:
    160
    Well 'if' you use 'don't destroy on load', what i meant was simply if the character is in scene 1 and scene 2(no dontDestroyOnLoad here), will it get reset for scene 2?

    Is this standard procedure or is it only needed when using loadLevelAdditive?

    Like in a normal situation, loaded the asset bundle, with the proper variant, then loaded the scene level, now we are going to load the next scene...
    then load the new/orSame assets, with variants, load the new scene, then call unloadUnusedAssets?

    Cause in my experience this seems to happen automatically. I was actually profiling asset bundles these past days, and GC 'seems' to do the job with them just fine.
     
  15. mbowen89

    mbowen89

    Joined:
    Jan 21, 2013
    Posts:
    635
    The only way anything gets saved between scenes is if it's tagged dont destroy. You can have the same prefab in two scenes, and the loaded scene will have it's same prefab reset to, well the prefab defaults.

    Also, you wouldn't actually even have the prefab in your 2nd scene because if you use dont destroy it's carried over from first scene.

    @BTStone Why don't you just use dont destroy and see how that goes. You said yourself it's the same objects I believe? Just have in your scripting modify them, or why do you even need to load a different scene? Just different thoughts I'm thinking here...
     
    BTStone likes this.
  16. BTStone

    BTStone

    Joined:
    Mar 10, 2012
    Posts:
    1,084
    I didn't say it's the same objects, I said it's the same materials/texture atlas. Well, kind of "same" sprites then, but they are placed at higher numbers and at different places in the next scene. Way too much effort to make a DontDestroy-Script to ALL of our Sprites/Terrains in the scene and rearange them when necessary.

    Why do I need to load a different scene? Easier to structure the scenes/level-segments. And sooner or later I have to anyway, this problem occurs with every "bigger" scene when using LoadLevel().

    As mentioned above: We have a super-performant workaround with LoadLevelAdditive(), I just wanted to know how LoadLevel() works under the hood in order to get a better understanding of it and maybe come up with a better alternative. And as @SteenLund explained there's not much that I can do from my side, so I'll go with LoadLevelAdditive() for now.

    Paul Dunning stated in the Sony-DevNet-Forums that he'll investigate this issue. Maybe it is indeed a Playstation-Thing, since I've never heard something similar on other platforms, at least PC/Mobile.
     
    Kiori likes this.
  17. Kiori

    Kiori

    Joined:
    Jun 25, 2014
    Posts:
    160
    @SteenLund
    Another thing, so when an object has dontdestroyonload on it. and it gets loaded into the new scene, in truth does it get disabled, loaded then re-enabled?

    Edit: nevermind, i tripled tested it, its not how it happens.
    I thought that might be related to the issue, guess not.
     
    Last edited: May 17, 2015