Search Unity

LoadAllAssetsAsync from AssetBundle is not really async

Discussion in 'Editor & General Support' started by FairGamesProductions, Apr 29, 2018.

  1. FairGamesProductions

    FairGamesProductions

    Joined:
    Mar 8, 2014
    Posts:
    81
    We are making a WebGL app that has to use AssetBundles to load props into a scene. The problem is that once the bundle is downloaded the app freezes for around 2.5 MINUTES until the prefabs are loaded into a list... We have tried all the load methods. One by one, all at once, normal and async and we get the same thing every time. It takes a LONG time to load the prefabs. Our current code is:
    Code (CSharp):
    1. public IEnumerator LoadPrefabsAsync()
    2.      {
    3.          print("start loading prefabs");
    4.          AssetBundleRequest request = assetBundle.LoadAllAssetsAsync<GameObject>();
    5.          yield return request;
    6.          prefabs = request.allAssets as GameObject[];
    7.          while (!request.isDone)
    8.              yield return null;
    9.          //List<GameObject> objs = new List<GameObject>();
    10.          //foreach (var i in assetNames)
    11.          //    {
    12.          //    AssetBundleRequest request = assetBundle.LoadAssetAsync(i);
    13.          //    objs.Add(request.asset as GameObject);
    14.          //    yield return null;
    15.          //    }
    16.          //prefabs = objs.ToArray();
    17.          //Debug.Log(prefabs.Length + " prefabs from " + assetBundle.name + " loaded.");
    18.          prefabsLoaded = true;
    19.          yield return null;
    20.      }
    Has anyone found any kind of workaround or fix or this?
     
  2. oscarlosu

    oscarlosu

    Joined:
    Mar 4, 2015
    Posts:
    12
    Hi FairGamesProductions,

    I'm having the same issue as you with AssetBundle.LoadAllAssetsAsync and LoadAssetAsync. Did you find out anything else?

    I've made a reproduction project (find Bitbucket repo here) where I try to load an asset bundle with audio clips (~300MB compressed / ~2 GB uncompressed). Reproduction steps are:

    1. Open either CallbackTest or CoroutineTest. The first one listens to the completed events of the async requests of AssetBundle.LoadAllAssetsAsync and LoadAssetAsync and the second one uses a coroutine to monitor the progress of the async requests.
    2. Press play and notice that the animated cube stops moving during most of the duration of AssetBundle.LoadAllAssetsAsync.
     
  3. Ryanc_unity

    Ryanc_unity

    Unity Technologies

    Joined:
    Jul 22, 2015
    Posts:
    332
  4. oscarlosu

    oscarlosu

    Joined:
    Mar 4, 2015
    Posts:
    12
    Hi Ryanc_unity,

    I'm having the same issue, but not using WebGL? Admittedly, it's not a matter of minutes, but i can't have things moving smoothly on the screen.
     
    Last edited: May 11, 2018
    erenaydin likes this.
  5. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,618
    It's a known issue that Unity's asynchronous systems often cause the game to freeze or stutter, unfortunately.

    The public issue tracker contains various of these reports, such as:
    https://issuetracker.unity3d.com/issues/async-load-is-not-async
    https://unity3d.com/search?refinement=issues&gq=async freeze

    Here is a talk from Playdead, how they achieved "stutter free gameplay". Unfortunately, as far as I remember, you'd require Unity source code to fix/workaround those issues. It's worth watching though, lots of good points made in that talk!
     
    oscarlosu likes this.
  6. Ryanc_unity

    Ryanc_unity

    Unity Technologies

    Joined:
    Jul 22, 2015
    Posts:
    332
    On platforms that have threading support the stuttering is usually caused by the last step of loading process: the integration step. Depending on what types are being loaded, the integration step can happen on a background thread, or needs to happen on the main thread, and in some cases is not time-sliceable. If you use the profiler, you can see fairly detailed information during a loading process what is being run on the main thread vs background thread during the loading process. At lot of our work under the hood has been refactoring these things to integrated on a thread and also time-sliceable to remove those hitches.
     
    oscarlosu and Peter77 like this.
  7. oscarlosu

    oscarlosu

    Joined:
    Mar 4, 2015
    Posts:
    12
    Hi Peter77!

    Thanks for the links! That Playdead talk sounds great. I will definitely check it out.

    I did check the issue tracker, but all the related issues I found were from rather old versions of Unity (the one you linked to was on 5.5). I'm working on Unity 2018.1, so I wasn't sure how relevant those issues where at this point.

    My understanding was that only the last phase of the loading, the "activation phase" should freeze the main thread (after the AsyncRequest.progress reaches 0.9).
     
    Last edited: May 16, 2018
  8. oscarlosu

    oscarlosu

    Joined:
    Mar 4, 2015
    Posts:
    12
    Hi Ryanc_unity,

    Thanks for getting back to me. All my tests are on a standalone project. An I'm seeing the stuttering both when AsyncRequest.progress is between 0 and 0.9, and in the final activation phase after that. I only expected stuttering on the last phase between 0.9 and 1.

    I will have a look at the profiler and see.
     
  9. Ryanc_unity

    Ryanc_unity

    Unity Technologies

    Joined:
    Jul 22, 2015
    Posts:
    332
    Let me know what you see in the profiler, I might not have an immediate fix, but could probably figure out a work around until more of the internals are cleaned up. Also code snippets for exactly how you are loading from a bundle are very useful as well.
     
  10. oscarlosu

    oscarlosu

    Joined:
    Mar 4, 2015
    Posts:
    12
    I made a test project that is available on bitbucket (using git LFS for the ~300MB asset bundle), here is the snippet:

    Code (CSharp):
    1.  
    2. public class AsyncBundleCoroutineLoader : MonoBehaviour {
    3.     public string bundlePath;
    4.     public Text text;
    5.     AssetBundle assetBundle;
    6.  
    7.     void Start () {
    8.         StartCoroutine(Load());
    9.     }
    10.  
    11.     IEnumerator Load()
    12.     {
    13.         AssetBundleCreateRequest assetBundleCreateRequest = AssetBundle.LoadFromFileAsync(bundlePath);
    14.         while(!assetBundleCreateRequest.isDone)
    15.         {
    16.             text.text = "LoadFromFileAsync progress: " + (assetBundleCreateRequest.progress * 100.0f) + "%";
    17.             yield return null;
    18.         }
    19.         Debug.Log("AssetBundle.LoadFromFileAsync completed");
    20.         assetBundle = assetBundleCreateRequest.assetBundle;
    21.         AssetBundleRequest assetBundleRequest = assetBundle.LoadAllAssetsAsync();
    22.         while (!assetBundleRequest.isDone)
    23.         {
    24.             text.text = "LoadAllAssetsAsync progress: " + (assetBundleRequest.progress * 100.0f) + "%";
    25.             yield return null;
    26.         }
    27.         text.text = "Loading completed";
    28.         Debug.Log("AssetBundle.LoadAllAssetsAsync completed");
    29.     }
    30.  
    31.     private void OnDestroy()
    32.     {
    33.         assetBundle.Unload(true);
    34.     }
    35. }
     
  11. TitanUnity

    TitanUnity

    Joined:
    May 15, 2014
    Posts:
    180
    Hi we may be having a similar issue here. We just upgraded to Unity 2018.1.3f1 and noticed a large increase in our Time ms to get assets from downloaded asset bundles. This is an example of the code that is now failing us in Unity 2018:

    Code.png

    Previously we ran Unity 5.5.2p2 with no issues and smooth asset bundle loading. The profiler looks like this during the LoadAssetsAsync call:
    552p2.png

    Now with Unity 2018.1.3f1 (and several other versions we tried in 2018 and in the 2017 range), we see a loading freeze / pop, where Unity hangs almost as if it is synchronously loading the bundle assets:
    Unity2018.png


    Notice how the AssetBundle.asset line in the profiler in 5.5.2p2 has a 0ms time and no additional nested calls, however in Unity 2018 we're seeing a much larger > 700ms time and several additional nested calls starting with "AssetBundle.asset/allAssets [Forced assets loading]." Any idea what that means or work arounds we can try?

    This is a deal breaker for us to upgrade and is holding up our team. Thanks!

    *This was demonstrated on Windows PC for a PC Standalone build.
     
    Last edited: Jun 8, 2018
  12. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,618
    If it works in an earlier Unity versions, but stops working in Unity 2018, you most likely found a bug.

    If this is important to you, you should submit a bug-report, following the advice given in this document: https://unity3d.com/unity/qa/bug-reporting

    Unity Technologies does not use the forum to track bugs. If you do not submit a bug-report, they're most likely not going to fix it.

    If you submit a bug-report, make sure to post the Case number in this forum thread, for UT staff to pick up.
     
  13. TitanUnity

    TitanUnity

    Joined:
    May 15, 2014
    Posts:
    180
    Ok bug report submit, haven't received a Case number yet..
     
  14. TitanUnity

    TitanUnity

    Joined:
    May 15, 2014
    Posts:
    180
    (Case 1047238) LoadAssetAsync behaves like sychronous call instead, creates large runtime hang / freeze
     
    Peter77 likes this.
  15. TitanUnity

    TitanUnity

    Joined:
    May 15, 2014
    Posts:
    180
    Confirmed that the loading performs smoothly all the way through 5.6.6f2 but begins failing as soon as we switch to Unity 2017+
     
    Peter77 likes this.
  16. TitanUnity

    TitanUnity

    Joined:
    May 15, 2014
    Posts:
    180
    Ha! Issue is now working ok on our end, we found a missing yield at a later point in the same function:

    This code was missing a yield:
    Code (CSharp):
    1. abr = ab.LoadAssetAsync<Texture2D>("AssetName");
    2.         if (abr.asset == null)
    3.         {
    4.             Debug.LogException(new System.Exception("abr.asset is null"));
    5.             yield break;
    6.         }
    Our loading issue is smooth again in Unity 2018.1.3f1 with this:
    Code (CSharp):
    1. abr = ab.LoadAssetAsync<Texture2D>("AssetName");
    2.         yield return abr;
    3.  
    4.         if (abr.asset == null)
    5.         {
    6.             Debug.LogException(new System.Exception("abr.asset is null"));
    7.             yield break;
    8.         }
    The only thing we can't explain is why the first set of code works smoothly in 5.5.2p2 given that this was actually an error on our end?
     
  17. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,618
    That's strange indeed, because according to the documentation:
    https://docs.unity3d.com/ScriptReference/AssetBundleRequest-asset.html
     
  18. Ryanc_unity

    Ryanc_unity

    Unity Technologies

    Joined:
    Jul 22, 2015
    Posts:
    332
    Actually, not all that strange. There was a race condition that could cause a deadlock when accessing the asset property that I fixed around that time (I think), so it properly stalls and waits for the loading process to complete. What was happening before was that we were force loading just the asset bundle object on the main thread while at the same time loading that object and more on the background thread.
     
    x4000 likes this.
  19. MathieuGirard

    MathieuGirard

    Joined:
    Nov 19, 2017
    Posts:
    3
    Hi all,

    I have just found the same type of issue after migrating from 2017.3.1 to 2018.2.5, except my issue is with AsynLoad of a scene.

    Here is an extract of the code I use:
    Code (CSharp):
    1.  
    2. AsyncOperation sceneLoadOperation = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(locationDefinition.SceneFilePath, UnityEngine.SceneManagement.LoadSceneMode.Additive);
    3.  
    4. while (!sceneLoadOperation.isDone)
    5. {
    6.     if (this.LocationLoadUpdated != null)
    7.     {
    8.         this.LocationLoadUpdated.Invoke(sceneLoadOperation.progress);
    9.     }
    10.  
    11.     yield return null;
    12. }
    13.  
    I have tried adding "yield return sceneLoadOperation;" before the "while" statement, but the result is the same : a huge performance spike in the profiler while loading the level.

    Do you have any idea of what I am doing wrong?

    Edit: just to clarify, this happens only in the editor (but was fine in 2017.3), but all my runtime content is in the Resources folder, pending my migration to the addressable system. Maybe some changes in the internal data structure of Unity, for the addressables system, is causing this?

    Thanks!
     
    Last edited: Aug 31, 2018
  20. greyforest

    greyforest

    Joined:
    Jun 25, 2015
    Posts:
    16
    When I use
    Code (CSharp):
    1. while (assetBundleRequest.isDone) {yield return null;}
    I also get the "AssetBundle.asset/allAssets [Forced assets loading]" spike just like the screenshot someone else posted.
    But another bug is:if I use
    Code (CSharp):
    1. yield return assetBundleRequest;
    it cost a lot of monobehaiour missing errors.
    unity version is 2018.3.b+
     
    Last edited: Nov 29, 2018
  21. greyforest

    greyforest

    Joined:
    Jun 25, 2015
    Posts:
    16
    i found out the problem(monobehaiour missing),when LoadSceneAsync finish(or even LoadScene), scene.isLoaded is still false。
    I use
    Code (CSharp):
    1. while (!scene.isLoaded || !scene.IsValid())
    2.         {
    3.             //Debug.LogFormat("scene.isLoaded={0}, scene.IsValid() = {1}", scene.isLoaded, scene.IsValid());
    4.             yield return null;
    5.         }
    after load finish and everything goes fine.

    i'll test asset loading spike again
     
  22. MFKJ

    MFKJ

    Joined:
    May 13, 2015
    Posts:
    264
    It means that there is no difference either you use LoadAsset or LoadAssetAync? Both will freez the game as it runs in main thread? If this is the case then, what you will recommend for smooth asset bundle loading?? I have to load/unload so many asset bundles based on my camera position what you will recommend for best practices?
     
  23. ANS-JFelipe

    ANS-JFelipe

    Joined:
    Feb 19, 2019
    Posts:
    1
    Hi:
    I have the same problem trying to load assets async.
    I share a Log where you can see that Bundle Request is not done (status == false), but Progress is 0 (402 times).

    upload_2019-6-4_14-28-18.png
     
  24. MFKJ

    MFKJ

    Joined:
    May 13, 2015
    Posts:
    264
    I am also looking for help to smoothly load bundles in webgl. Any help will be appreciated.
     
  25. nyudeb

    nyudeb

    Joined:
    Mar 20, 2015
    Posts:
    19
    This should work:

    Code (CSharp):
    1.    
    2.     private void Start()
    3.     {
    4.         StartCoroutine("myCoolBundle.unity3d");
    5.     }
    6.  
    7.     AssetBundleCreateRequest bundleLoadRequest;
    8.  
    9.     private IEnumerator LoadBundle(string bundlePath)
    10.     {
    11.         StartCoroutine(LoadFromFileAsync(bundlePath));
    12.  
    13.         while (!bundleLoadRequest.isDone)
    14.         {
    15.             Debug.Log(bundleLoadRequest.progress);
    16.             loadingBar.GetComponent<Image>().fillAmount = bundleLoadRequest.progress;
    17.             yield return null;
    18.         }
    19.     }
    20.  
    21.     private IEnumerator LoadFromFileAsync(string filePath)
    22.     {
    23.         bundleLoadRequest = AssetBundle.LoadFromFileAsync(Path.Combine(Application.streamingAssetsPath, filePath), 0);
    24.         yield return null;
    25.     }
     
  26. kingna

    kingna

    Joined:
    Feb 6, 2017
    Posts:
    1
    Hi:
    I had a similar problem.
    Here is my code and the results.

    Unity: 5.6.3 p3
    for( 1000 )

    Code (CSharp):
    1. AssetBundleRequest mRequest = assetbundle.LoadAssetAsync(assetName);
    2.          
    3.             while (!mRequest.isDone)
    4.             {
    5.                 yield return null;
    6.             }
    7.  
    8.             if (null != mRequest.asset)
    9.             {
    10.                 ABLog.Log("not Null");
    11.             }
    12.             else
    13.             {
    14.                 ABLog.Log("Null");
    15.             }

    upload_2019-7-4_14-37-48.png
     
  27. Petr-Sovis

    Petr-Sovis

    Joined:
    Feb 5, 2013
    Posts:
    10
    Hi,
    still doesn't work

    when I execute:
    bundleLoadRequest = AssetBundle.LoadFromFileAsync(Path.Combine(Application.streamingAssetsPath, filePath), 0);

    1) bundleLoadRequest.done is true immediately (thats weird)
    2) when I use "yield return bundleLoadRequest;" it stops updates and waits

    is this bug compression method related ? will it be better on different compression method ? I use LZ4

    Any help ?

    Btw I cant find any bug report of LoadFromFileAsync

    Cheers. Petr
     
  28. juliens137

    juliens137

    Joined:
    Oct 2, 2017
    Posts:
    26
    Hi,

    I am still experiencing this issue all the way in 2020. When I run AssetBundle.LoadFromFileAsyn(Path), isDone is true immediately.

    I tried making my while condition while (request.progress < 1), but progress stays at 0 forever.

    I'm currently using Unity 2018.4.9f1. Has this been fixed in any later versions of 2018.4?

    Thanks
     
  29. Heartwood_jw

    Heartwood_jw

    Joined:
    Sep 20, 2017
    Posts:
    3
    This seems to work most consistently for me and reduces the chance of being stuck in a while loop:

    Code (CSharp):
    1.  
    2. yield return new WaitUntil(() =>
    3. {
    4.         loadingFill.fillAmount = request.progress;
    5.         return request.progress >= .99f;
    6. });
    7.  
     
    hopeful likes this.
  30. styGGx

    styGGx

    Joined:
    Aug 27, 2017
    Posts:
    19
    I'm also battling with this. I thought it was because I'm on 2018.3 - but then I upgraded my project to 2019.4 LTS, but it still happens.
     
  31. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    328
    https://forum.unity.com/threads/multithreading-and-webgl.817986/

    Code (CSharp):
    1. Threads are not supported due to the lack of threading supporting in JavaScript. This applies to both Unity’s internal use of threads to speed up performance, and to the use of threads in script code and managed dlls. Essentially, anything in the System.Threading namespace is not supported.
    For platforms which supports threading, helpful may be following source code:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Threading.Tasks;
    3. using UnityEngine;
    4. using System;
    5. using System.Runtime.CompilerServices;
    6. using System.IO;
    7.  
    8. public class AssetBundleAsyncAwait : MonoBehaviour
    9. {
    10.     public async Task<Texture2D> LoadTextureAsync(string filePath, string address)
    11.     {
    12.         AssetBundleCreateRequest handle = await AssetBundle.LoadFromFileAsync(filePath);
    13.         AssetBundleRequest request = await handle.assetBundle.LoadAssetAsync<Texture2D>(address);
    14.         Texture2D texture = request.asset as Texture2D;
    15.         return texture;
    16.     }
    17.  
    18.     async void Start()
    19.     {
    20.         Texture2D texture = await LoadTextureAsync(Path.Combine(Application.streamingAssetsPath, "test.bundle"), "Assets/icon.png");  
    21.         GameObject plane = GameObject.CreatePrimitive(PrimitiveType.Plane);
    22.         plane.GetComponent<Renderer>().material.mainTexture = texture;
    23.     }
    24. }
    25.  
    26. // https://docs.unity3d.com/ScriptReference/AssetBundleCreateRequest.html
    27. public class AssetBundleCreateRequestAwaiter : INotifyCompletion
    28. {
    29.     readonly AssetBundleCreateRequest _Request;
    30.     Action _Action;
    31.  
    32.     public AssetBundleCreateRequestAwaiter(AssetBundleCreateRequest request)
    33.     {
    34.         this._Request = request;
    35.         request.completed += OnRequestCompleted;
    36.     }
    37.  
    38.     public bool IsCompleted { get { return _Request.isDone; } }
    39.  
    40.     public AssetBundleCreateRequest GetResult() { return this._Request; }
    41.  
    42.     public void OnCompleted(Action action)
    43.     {
    44.         this._Action = action;
    45.     }
    46.  
    47.     private void OnRequestCompleted(AsyncOperation command)
    48.     {
    49.         _Action();
    50.     }
    51. }
    52.  
    53. public static class AssetBundleCreateRequestAsync
    54. {
    55.     public static AssetBundleCreateRequestAwaiter GetAwaiter(this AssetBundleCreateRequest request)
    56.     {
    57.         return new AssetBundleCreateRequestAwaiter(request);
    58.     }
    59. }
    60.  
    61. // https://docs.unity3d.com/ScriptReference/AssetBundleRequest.html
    62. public class AssetBundleRequestAwaiter : INotifyCompletion
    63. {
    64.     readonly AssetBundleRequest _Request;
    65.     Action _Action;
    66.  
    67.     public AssetBundleRequestAwaiter(AssetBundleRequest request)
    68.     {
    69.         this._Request = request;
    70.         request.completed += OnRequestCompleted;
    71.     }
    72.  
    73.     public bool IsCompleted { get { return _Request.isDone; } }
    74.  
    75.     public AssetBundleRequest GetResult() { return this._Request; }
    76.  
    77.     public void OnCompleted(Action action)
    78.     {
    79.         this._Action = action;
    80.     }
    81.  
    82.     private void OnRequestCompleted(AsyncOperation command)
    83.     {
    84.         _Action();
    85.     }
    86. }
    87.  
    88. public static class AssetBundleRequestAsync
    89. {
    90.     public static AssetBundleRequestAwaiter GetAwaiter(this AssetBundleRequest request)
    91.     {
    92.         return new AssetBundleRequestAwaiter(request);
    93.     }
    94. }