Search Unity

Feedback Loading Resource creates massive lagspikes

Discussion in 'Editor & General Support' started by joshcamas, Aug 30, 2019.

  1. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,277
    Hello!

    For some reason, when I load a prefab and instantiate as a Resource using Resources.LoadAsync, this results in massive lagspikes in the build. (IE 1,000 - 300 ms). I've made sure to set Application.backgroundLoadingPriority to low.



    If I instantiate using a normal reference, or even load it then instantiate using Addressables, this lagspike doesn't occur. This is extremely odd.

    Is this a known limitation with Resources?

    Josh

    EDIT: I tested it without instantiation: in other words, ONLY using Resources.LoadAsync. And the lag still occurs. The prefab I'm trying to load does pack a lot of data, sort of... but loading from Addressables doesn't cause lag, and one would imagine that loading an asset using Resources asynchronously would simply make the operation take a long time, even if the prefab was sizable. So I'm not sure what's going on here.
     
    Last edited: Aug 30, 2019
  2. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,277
  3. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I don't know the answer, but my understanding is when you load using Resources it loads the asset into memory at that time. When you use a normal reference the resource is loaded when the scene initially loads (so the lag is during the scene transition instead of while in the middle of a scene) so when you instantiate from a normal reference there is no loading occurring, so no associated lag spike other than that caused just by the instantiation of the object. I haven't tried addressables.

    edit:
    Also, async operations in the editor aren't actually async - instead they block (at least that is the case with async scene loading, and I'd assume that is the case most everywhere). So make sure you're testing this in a build.
     
    Last edited: Sep 10, 2019
  4. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,277
    The issue isn't with instantiation, since the lag still occurs when all I do is load the resource into memory. (note this is not a scene, but a prefab) Since it's supposed to be async, causing the game to freeze for 1,000 ms isn't a good outcome oof
     
  5. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    Sorry, just edited my above post. Make sure you are testing this in a build, not the editor, as async operations block the main thread while running the in the editor (and I believe webGL also).
     
  6. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,277
    Yus this is in build ☺️☺️ thank ya
     
  7. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,195
    There are two approach I know of to address this.

    For resources that you use in most of your scenes, adding the prefab to "Preloaded Assets" in the Player settings will cause those to be loaded into memory right at the start of the game. This will significantly reduce the performance hit of instantiating more of that prefab.

    upload_2019-9-10_16-7-49.png

    I generally only use this approach for objects that are in pretty much every scene in the game.


    The other approach is more generally used for dynamic assets you only need in a subset of scenes. I use the Addressables system in my game, but it should be pretty much the same for objects in Resources of if you're using Asset Bundles. Anyway, each of my scenes has a SceneController script on it that contains a list of prefabs that should be pre-loaded. When the scene is initially loaded, while my "Loading" screen is still showing, I Load each of those assets in advance so that when I later instantiate them the performance impact is minimal. Basically, I just call "Addressables.LoadAssetAsync<GameObject>(address)" for each of the things I need to preload.

    This approach require a bit of curation to ensure that ensure that each scene preloads the correct assets it's going to use. That's a little bit of work, but it's pretty simple.
     
    joshcamas likes this.
  8. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,277
    Sadly my world is an open world game, so loading my entire map into memory is simply not a solution for me

    Thank you for explaining what works for you though!! :)
     
  9. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,195
    Ah. Well, I've never used this asset before, so I don't "recommend" it, but I believe that I've seen a lot of people mention using it for large, open-world games:

    https://assetstore.unity.com/packages/tools/utilities/world-streamer-36486

    You'd definitely want to verify that it actually does what you want, and prevents the game from hanging when loading up the next chunk of stuff.
     
  10. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,277
    Thanks for the link, I've already built my own world streamer and heavily into my current project, so sadly using another workflow isn't a solution for me. I know that world streamer uses scenes to split up their world, so that explains how they manage to reduce memory hiccups.

    My main reason for making this post is to understand why Resource.LoadAsync has massive spike issues, but Addressable loading async does not.
     
  11. yusufdemir2012

    yusufdemir2012

    Joined:
    Dec 21, 2013
    Posts:
    50
    @joshcamas It's been 4 years, did you find any solution for this? We have same problem
     
  12. miraycalskn

    miraycalskn

    Joined:
    Jun 28, 2020
    Posts:
    10
    I dealt with this problem last month. As Unity's official docs, resources folder causes many many problems natively It really pushed me hard to solve but i solved it by using Adressables System. But i must say it takes some nerve to switch all systems. Because Addressable assets doesn't load synchronous, they load asynchronous. So you need to revise your code a lot. Here you can find more information about Addressables Package. Bit tricky to use and makes build times longer but whatever fixes immediately. Also here is few tricks from my experience;
    • If you faced resources issue, probably you have lots of public references in your scene. So convert them to Addressable Assets too.
    • Keep material instances as usual. I don't know that did i do something wrong but materials didn't load stable in my build.(Sometimes they loaded, sometimes they didn't and i have no idea why tried to fix but couldn't detect the problem. Keep that in mind)
    • Also it would be nice if you unload unnecessary assets too. This will impact your performance so much.
    And here is a sample function from my Addressable Loader Script. I created multiple loader scripts for each asset group i got in Resources folder. Also it would be better not to load scriptable objects with too many fields, it would be best if you separate your assets. And don't forget to remove assets from Resources folder. I simply duplicated Resources folder and renamed it as "Addressable Assets".

    Code (CSharp):
    1.  
    2. public async void SetAsset(MeshFilter destination, AssetReference target, bool selfOverride = false)
    3.     {
    4.         bool isEditor = false;
    5. #if UNITY_EDITOR
    6.         if (!selfOverride) isEditor = true;
    7.         else isEditor = false;
    8. #endif
    9.         if (isEditor)
    10.         {
    11. #if UNITY_EDITOR
    12.             if (!UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
    13.             {
    14.                 var path = UnityEditor.AssetDatabase.GUIDToAssetPath(target.AssetGUID);
    15.                 Object[] data = UnityEditor.AssetDatabase.LoadAllAssetRepresentationsAtPath(path);
    16.                 int counter = 0;
    17.                 for (int i = 0; i < data.Length; i++)
    18.                 {
    19.                     if (data[i].name == target.SubObjectName)
    20.                     {
    21.                         if (counter == 0) counter++;
    22.                         else
    23.                         {
    24.                             destination.mesh = data[i] as Mesh;
    25.                             return;
    26.                         }
    27.                     }
    28.                 }
    29.                 destination.mesh = UnityEditor.AssetDatabase.LoadAssetAtPath<Mesh>(path);
    30.             }
    31.             else
    32.             {
    33.                 SetAsset(destination, target, true);
    34.             }
    35. #else
    36. return;
    37. #endif
    38.         }
    39.         else
    40.         {
    41.             if (!target.IsValid())
    42.             {
    43.                 AsyncOperationHandle<Mesh> asyncOperationHandle = target.LoadAssetAsync<Mesh>();
    44.                 while (!asyncOperationHandle.IsDone)
    45.                 {
    46.                     await Task.Delay(taskDelayTime);
    47.                 }
    48.                 await Task.Delay(taskDelayTime);
    49.                 asyncOperationHandle.Completed += Completion;
    50.                 void Completion(AsyncOperationHandle<Mesh> asyncOperationHandle)
    51.                 {
    52.                     destination.mesh = target.Asset as Mesh;
    53.                 }
    54.             }
    55.             else
    56.             {
    57.                 while (!target.IsDone)
    58.                 {
    59.                     await Task.Delay(taskDelayTime);
    60.                 }
    61.                 await Task.Delay(taskDelayTime);
    62.                 destination.mesh = target.Asset as Mesh;
    63.             }
    64.         }
    65.     }
    Here is a sample usage for this function;
    Code (CSharp):
    1.  
    2. GeneralAssetLoader.LevelLoader.SetAsset(myVisualParent.GetComponent<MeshFilter>(), GeneralAssetLoader.LevelLoader.DynamiteMeshes[1]);
    3.  
    DynamiteMeshes variable here is defined as :
    Code (CSharp):
    1. public AssetReference[] DynamiteMeshes;
    Also as you can see in the function it contains some extra parts for Unity Editor Scripting. I dealt with that too so i kept that sample here, hope it helps helpful thread about that
     
  13. miraycalskn

    miraycalskn

    Joined:
    Jun 28, 2020
    Posts:
    10
    Also forgot to mention you need to prepare your scripts to asynchronous load. This means you need to make direct referenced functions to async too. Also you must wait until tasks are complete before the player can start the game. You can simply do that by adding a blocker value to start and end parts of your loading functions and wait until it returns true at start or you most likely receive an error. And one last thing keep that Task.Delay parts in code. Rn i don't exactly remember the reason why i added them but i remember it was necessary. I know it's loads of work to do but if you're building an open world game this is the way... You can't go any further with Resources folder. It's basically impossible with that system. Asset Bundles is another system that i'm not really experienced in. But as i know Addressables Package is built over that and it's more efficient.