Search Unity

SceneManager.LoadSceneAsync progress stuck at 0 while loading multiple scenes in using Additive

Discussion in 'Scripting' started by NerbertClimax, Mar 8, 2018.

  1. NerbertClimax

    NerbertClimax

    Joined:
    Jun 23, 2017
    Posts:
    4
    Hi,

    We're having a 'random' bug where loading seems to just get stuck at 0% and never even starts. This used to be happening on builds but recently I've started seeing this in the editor and gave me more insight in where it actually got stuck, namely the AsyncOperation progress that is stuck on 0.

    The coroutine that monitors the async operation waits for it to hit 0.9, so it can then move on to allowing it to initialize the loaded scene by setting allowSceneActivation to true. But because progress never gets past 0, it is stuck.

    The following bit of code is a condensed version of how we're loading the scenes in:


    private IEnumerator LoadScenes(string[] scenes)
    {
    m_loading = true;

    List<AsyncOperation> asyncs = new List<AsyncOperation>();

    // Load
    for (int i = 0; i < scenes.Length; ++i)
    {
    AsyncOperation async = SceneManager.LoadSceneAsync(scenes[i], LoadSceneMode.Additive);
    asyncs.Add(async);
    async.allowSceneActivation = false;
    Debug.Log("Loading: " + scenes[i] + " - " + async.progress);
    while (async.progress < 0.9f)
    {
    Debug.Log("progress: " + scenes[i] + " - " + async.progress);
    yield return null;
    }
    Debug.Log("Load done: " + scenes[i]);

    }

    // Initialize
    for (int i = 0; i < asyncs.Count; ++i)
    {
    AsyncOperation async = asyncs[i];
    async.allowSceneActivation = true;
    while (!async.isDone)
    {
    yield return null;
    }

    Debug.Log("Init done: " + scenes[i]);
    sceneUnloadQueue.Add(scenes[i]);
    }

    m_loading = false;
    }


    I've tried this in a separate project with some small test scenes and load/unload the same set of scenes over and over again, but it can't reproduce it getting stuck on 0, so it might be that the size of the scene that gets loaded has something to do with it.

    When looking in the Editor's Hierarchy, I can see that the scenes are there, grayed-out with the "(is loading)" text behind it. In the particular case where it gets stuck, there are 2 scenes loaded and active, some objects marked as "DontDestroyOnLoad" and 3 scenes that were loaded in using the LoadSceneMode.Additive using almost identical code to the one above.

    2 out of the 3 scenes have the progress in the AsyncOperation at 0.9 (as expected), while the last one is stuck at 0.0. Placing a breakpoint in Visual Studio shows that we are still in the coroutine as well as the Debug.Log printing out the current value each frame.

    The file sizes of the scenes loaded are:

    scene1 - 349,896 bytes
    scene2 - 15,173,817 bytes
    scene3 - 254,274 bytes


    This method of loading in scenes works most of the time, it just so happens to be stuck at random occasions, something we suspect already happened when we were on Unity 5.6, while we are now on Unity 2013.2.1p3.

    Afaik there is no way to abort or restart the async loading operation? Is there anything else I need to be aware of that might be causing the loading progress to get stuck at 0?
     
    Tymianek likes this.
  2. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    838
    I'd file a bug report, because it looks like a bug.
     
  3. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
    I haven't had coffee for a while so this may sound stupid BUT
    I've tried that code a few day ago myself. I abandoned async because the the scenes I was loading were very tiny. (25KB)
    So consider the following with a rock salt mining complex:
    Maybe. And I mean a really dumb maybe. The scene gets loaded so fast (because it's tiny) that the load progress is already reset by the time you're checking for progress. I mean, in your example we're talking like 4 statements between starting an async task and checking for progress. Including the incredibly slow Debug.Log.

    Is my post even making sense? Because this sounds like something that shouldn't be happening (especially the progress being reset to 0 if no task was called.)
     
  4. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    There's a few caveats to SceneManager. Okay, so rule one. You can only add scenes of an additive type, anything else will completely freeze the program. The next one is do not async.allowSceneActivation = true; until ALL scene have fully loaded. I have this setup, which works for me:

    Code (CSharp):
    1.  
    2.         public static bool Setup
    3.         {
    4.             get
    5.             {
    6.                 if (!m_Setup)
    7.                 {
    8.                     SceneManager.sceneLoaded += LoadLevelReady;
    9.                     m_Setup = true;
    10.                 }
    11.                 return true;
    12.             }
    13.         }
    14.         public static void LoadScenes(string[] sceneNames)
    15.         {
    16.             if (Setup)
    17.             {
    18.                 for (int i = 0; i < sceneNames.Length; i++)
    19.                 {
    20.                     m_SceneNamesPendingLoad.Enqueue(sceneNames[i]);
    21.                 }
    22.  
    23.                 if (ScreenFader != null)
    24.                 {
    25.                     ScreenFader.StartFade(Color.black, 0.25f);
    26.                 }
    27.  
    28.                 SceneManager.LoadSceneAsync(SceneLoaderName);
    29.             }
    30.         }
    31.  
    32.         private static void LoadLevelReady(Scene scene, LoadSceneMode mode)
    33.         {
    34.             if(scene.name == SceneLoaderName)
    35.             {
    36.                 List<AsyncOperation> sceneLoadingTasks = new List<AsyncOperation>();
    37.  
    38.                 bool firstLoad = true;
    39.                 while (m_SceneNamesPendingLoad.Count > 0)
    40.                 {
    41.                     if (firstLoad)
    42.                     {
    43.                         sceneLoadingTasks.Add(SceneManager.LoadSceneAsync(m_SceneNamesPendingLoad.Dequeue(), LoadSceneMode.Single));
    44.                         firstLoad = false;
    45.                     }
    46.                     else
    47.                     {
    48.                         sceneLoadingTasks.Add(SceneManager.LoadSceneAsync(m_SceneNamesPendingLoad.Dequeue(), LoadSceneMode.Additive));
    49.                     }
    50.                 }
    51.  
    52.                 GameObject.Find(SceneDirectory.RootFolderName).GetComponentInChildren<LoadingVisualiser>().PassLoader(sceneLoadingTasks);
    53.             }
    54.         }
    Code (CSharp):
    1.  
    2.     using System.Collections;
    3.     using System.Collections.Generic;
    4.     using System.Linq;
    5.     using UnityEngine;
    6.     using UnityEngine.UI;
    7.  
    8.     public class LoadingVisualiser : MonoBehaviour
    9.     {
    10.         [SerializeField] private Text m_ProgressText;
    11.         [SerializeField] private Slider m_ProgressSlider;
    12.  
    13.         public void PassLoader(AsyncOperation operation)
    14.         {
    15.             StartCoroutine(LoadSceneProcess(operation));
    16.         }
    17.  
    18.         public void PassLoader(List<AsyncOperation> operations)
    19.         {
    20.             StartCoroutine(LoadSceneProcess(operations));
    21.         }
    22.  
    23.         private IEnumerator LoadSceneProcess(AsyncOperation operation)
    24.         {
    25.             operation.allowSceneActivation = true;
    26.  
    27.             while (!operation.isDone)
    28.             {
    29.                 m_ProgressText.text = string.Format("{0}%", operation.progress.ToString("00.0"));
    30.                 m_ProgressSlider.value = operation.progress;
    31.  
    32.                 yield return null;
    33.             }
    34.         }
    35.  
    36.         private IEnumerator LoadSceneProcess(List<AsyncOperation> operations)
    37.         {
    38.             GameObject[] sceneObjs = FindObjectsOfType<GameObject>();
    39.          
    40.             while (!operations.TrueForAll(x => x.isDone))
    41.             {
    42.                 float averageProgress = operations.Select(x => x.progress).ToArray().Average();
    43.  
    44.                 m_ProgressText.text = string.Format("{0}%", averageProgress.ToString("00.0"));
    45.                 m_ProgressSlider.value = averageProgress;
    46.  
    47.                 yield return null;
    48.             }
    49.          
    50.             // Now they're all complete we can load the scenes together as one.
    51.             operations.ForEach(x => x.allowSceneActivation = true);
    52.          
    53.             for(int i=0; i < sceneObjs.Length; i++)
    54.             {
    55.                 Destroy(sceneObjs[i]);
    56.             }
    57.         }
    58.     }
     
  5. NerbertClimax

    NerbertClimax

    Joined:
    Jun 23, 2017
    Posts:
    4
    I don't believe it would be reset to 0 if it's loaded in the same frame, but just to be sure I've added some test code to basically force it to allowSceneActivation set to true after 10 seconds being stuck at 0 (10 seconds is way more time it needs for that scene), but both isDone remains at false and it won't actually move the progress all the way to 1.0 after setting the allowSceneActivation to true. Also, the Editor's Hierarchy keeps showing the "(is loading)".
     
  6. NerbertClimax

    NerbertClimax

    Joined:
    Jun 23, 2017
    Posts:
    4
    I've been careful not to start multiple async loads in the hopes that it won't interfere with each async load request, but I notice you're starting multiple loads at once and wait for all of them to finish loading. I've modified my code to do the same to see if that makes any difference, but no luck.

    The allowSceneActivation is only set to true once all async operations have finished and I don't even reach that code in my case due to the progress being stuck at 0, so that shouldn't be an issue either.
     
  7. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    Yeah, I had a similar issue when I was making additive scene loading. It sounds silly, but to be killingly certain. Have you tried checking using 'async.isDone', rather than < 0.9f ? It 'shouldn't' make a difference, but it's worth maybe ticking off the list. I found with my issue, it was exactly the same symptom, but mine was revolving around additively loading multiple scenes and allowing scene activation. I know it's not fit for your purpose neccessarily, but maybe try loading the first scene as a 'LoadSceneMode.Single'. For me, that's what fixed it.
     
  8. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
    As you're doing them singularly there could be the smallest change waiting for 0.9 instead of isDone, in the behind the scenes code is not allowing the scene to be totally ready before activating in the scene. Supposing there's any unique files in the scene that require scripting (like for example substance designer materials, that script the materials before the scene can be instantiated), that could potentially trip it up. Sadly I can only offer some suggestions, instead of 'this is definitely the issue'.
     
  9. NerbertClimax

    NerbertClimax

    Joined:
    Jun 23, 2017
    Posts:
    4
    I have checked isDone, which remains at false. What I have in my scenario is that there is one scene loaded as single, while all others are loaded additive, in this case 4 scenes are queued up to be loaded as additive, but it gets stuck on the third entry.

    Interestingly, I've tried mixing the loading up as follows:
    • LoadSceneAsync(scene, LoadSceneMode.Additive)
    • allowSceneActivation set to false
    • Wait for progress to reach 0.9
    • allowSceneActivation set to true
    • Wait for isDone to be true
    • Continue to next scene
    This seems to fix the issue, as it won't get stuck on 0 anymore. Since it gets stuck at 0 on the scene after loading a big scene in, it seems to indicate that it is memory related. By allowing it to activate, it probably empties the memory pool it uses under the hood, allowing the next scene to load in just fine.

    The problem now is that I have to make sure nothing is breaking in my setup as the project has been relying on the fact that I load them all in to 0.9 before allowing them to activate. I don't expect it to, but who knows... It would be good if there is more feedback to know what's going on while loading, even some OutOfMemory exception/status that can be read would be good.
     
    Nigey likes this.
  10. Nigey

    Nigey

    Joined:
    Sep 29, 2013
    Posts:
    1,129
  11. luislodosm

    luislodosm

    Joined:
    Apr 26, 2016
    Posts:
    26
    Same issue. The ideal is:

    - Load all scenes async. AllowSceneActivation = false;
    - Fading out.
    - Wait for all scenes to load. async.progress < 0.9f;
    - Wait for fade out to finish.
    - Activate scenes. AllowSceneActivation = true;
    - Fading in.

    But doesn't work. So this is what I did:

    - Load all scenes async.
    - Large fading out.
    - Activate scenes.
    - Fading in.
     
    Last edited: May 29, 2018
  12. carbon_studios

    carbon_studios

    Joined:
    Jun 14, 2020
    Posts:
    8
    I think I figured it out. Checking if its progress too quickly creates a bug that resets its progress to 0. Checking its progress every frame is too fast. I only check its progress once every 100ms, and problem solved.
     
  13. In2Play

    In2Play

    Joined:
    Apr 25, 2016
    Posts:
    66
    This didn't work for me either. Why doesn't unity fix this?
     
  14. AlejandroRivasMistwall

    AlejandroRivasMistwall

    Joined:
    Oct 21, 2020
    Posts:
    3
  15. Tymianek

    Tymianek

    Joined:
    May 16, 2015
    Posts:
    97
    Turns out you can't have more than one scene loading at a time.
    You can start multiple loadings, but the next loading can only occur, after the previous one .isDone==true. Using allowSceneActivation=false will block the next loading until you activate the scene with allowSceneActivation=true.
     
    Last edited: Feb 26, 2021
    andreiagmu likes this.