Benchmarking and lifecycle of SceneManager.LoadSceneAsync (was: Holes in the Scene API?)

Is the Scene API complete? Seems there are holes
For example you can’t pass a Scene into LoadScene but you need a Scene for SetActive
Also LoadScene and LoadSceneAsync don’t “out” a Scene
I’m probably missing something or the way to use it is different because it seems well designed.

no man, the API is nice and tight.
You can do all that with SceneManager.sceneLoaded and .GetSceneByName

1 Like

The whole additive loading and scene management thing is still pretty new. And it’s been tacked on top of the old scene system. In the old system scenes didn’t exist at runtime. Now they do, but only kind of.

It’s adequate, but it’s not the cleanest API that could have been built. No doubt there will be future improvements.

You’re saying it’s just a new interface, not new backend? It looks brand new to me and does things I don’t recall the old system was able to, such as load a scene and keep them inactive with allowActivate.

Its more a new back end with bits of the old interface.

I’m pretty sure you can’t load a Scene as Scene is an object that represents an already loaded scene. It’s a pity we don’t have a built-in type representing scenes that can be loaded other than strings. You can build one with some inspector magic, but still. Being able to iterate all the scenes that can be loaded at runtime seems like a no-brainer feature.

For multi-scene editing, it would be very nice if LoadScene returned a scene. LoadSceneAsync having an async callback that got you the scene would also be a big improvement.

The current API is an enormous improvement on the old API (Application.LoadLevel), but it could do with some extensions and improvement.

1 Like

Have you profiled the cpu and memory usage during load and activation of a scene?
I just saw some interesting results:

  • when 100s of animated characters with only two simple surface shaders are loaded there is a 300ms spike for “Shader.CreateGPUProgram” seems like a lot for 2 shaders maybe can be optimized with Unity - Scripting API: ShaderVariantCollection EDIT: after one run in the editor and adding the resulting variant asset to that array in graphics the hiccup is gone. It’s a bit of black magic and the naming in graphics is inconsistent (ie: you need to read the documentation) but it seems to works well.
  • a large terrain with light baked onto it takes 5ms to deserialize into a scene, seems very fast
  • Application.Integrade assets in background eats a whopping 55ms when loading 60 uncompressed audioclips, 85 ms when deserializing 200 animated robots and
  • unloading a scene can eats up a lot, when unloading the robot scene it takes 120ms
  • instantiating by code is 3x faster than deserializing a scene for the same number of objects. EDIT: after running it a few times and reversing the order the scenes are loaded, it’s the other way around, instantiating by code is much slower than the same scene deserialized.
  • loading tons of uncompressed load-in-memory sounds has zero impact on load time! I don’t know what kind of voodoo is happening there but WOW the audio team outdid itself on that one.

Yes I use this. Unfortunately property drawers can’t detect a name change in the scene… so if you change the name of a scene you have to re-drop it in the inspector.

SceneField

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
[System.Serializable]
public class SceneField
{
    [SerializeField]
    private Object m_SceneAsset;
    [SerializeField]
    private string m_SceneName = "";
    public string name
    {
        get { return m_SceneName; }
    }
    // makes it work with the existing Unity methods (LoadLevel/LoadScene)
    public static implicit operator string( SceneField sceneField )
    {
        return sceneField.name;
    }
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(SceneField))]
public class SceneFieldPropertyDrawer : PropertyDrawer
{
    public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label)
    {
        EditorGUI.BeginProperty(_position, GUIContent.none, _property);
        SerializedProperty sceneAsset = _property.FindPropertyRelative("m_SceneAsset");
        SerializedProperty sceneName = _property.FindPropertyRelative("m_SceneName");
        _position = EditorGUI.PrefixLabel(_position, GUIUtility.GetControlID(FocusType.Passive), _label);
        if (sceneAsset != null)
        {
            sceneAsset.objectReferenceValue = EditorGUI.ObjectField(_position, sceneAsset.objectReferenceValue, typeof(SceneAsset), false);
            if( sceneAsset.objectReferenceValue != null )
            {
                sceneName.stringValue = (sceneAsset.objectReferenceValue as SceneAsset).name;
            }
        }
        EditorGUI.EndProperty( );
    }
}
#endif

Agreed, I’d like to know why string and int is used for scene operations instead of objects like the rest of unity assets, especially because, reading a the scene ascii (editor>asset serialization mode>force text), a scene looks exactly like a prefab.

I read a lot of complain about the new lifecycle so I made some test and here what I found.
Awake → OnEnable → SceneManager.sceneLoaded → Start
If you use the callback to switch active scene, you need to instantiate in Start()

I’ll stick with DontDestroyOnLoad() until I figure out all that loading and unloading and activation, seems to be an unnecessary pain point.

We’ve got one that doesn’t have this drawback. I’m not at work, and I didn’t make it, so I don’t have the exact details, but I think it stores the scene’s GUID rather than name. And uses ISerializationCallbackReceiver to grab the string when it’s built? I’ll check later.

1 Like

Base you genius, that’s what I needed. I ended up using AssetDatabase.GUIDToAssetPath(m_GUID) and retrofitted SceneField with that. Now living in the luxury of something that works as it should!

@SteenLund , I think you’re in charge of scene manager, maybe you can help the editor team fix their bugs until you add proper scene referencing: the one I’m talking about happens when you rename a scene that was added in the build list, the scene gets lost (grayed out).
this script

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
[System.Serializable]
public class SceneField
#if UNITY_EDITOR
: ISerializationCallbackReceiver
#endif
{
#if UNITY_EDITOR
public void OnBeforeSerialize ()
{
m_sceneName = AssetDatabase.GUIDToAssetPath(m_GUID);
}
public void OnAfterDeserialize ()
{
}
#endif

[SerializeField]
Object m_SceneAsset;
[SerializeField]
string m_GUID;
[SerializeField]
string m_sceneName;
public string name
{
#if UNITY_EDITOR
get{return AssetDatabase.GUIDToAssetPath(m_GUID);}
#else
get{return m_sceneName;}
#endif
}
//makesitworkwiththeexistingUnitymethods(LoadLevel/LoadScene)
public static implicit operator string( SceneField sceneField )
{
return sceneField.name;
}
}

#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(SceneField))]
public class SceneFieldPropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect _position, SerializedProperty _property, GUIContent _label)
{
EditorGUI.BeginProperty(_position, GUIContent.none, _property);
SerializedProperty sceneAsset = _property.FindPropertyRelative("m_SceneAsset");
SerializedProperty sceneGUID = _property.FindPropertyRelative("m_GUID");
_position = EditorGUI.PrefixLabel(_position, GUIUtility.GetControlID(FocusType.Passive), _label);
if (sceneAsset != null)
{
sceneAsset.objectReferenceValue = EditorGUI.ObjectField(_position, sceneAsset.objectReferenceValue, typeof(SceneAsset), false);
if( sceneAsset.objectReferenceValue != null )
{
sceneGUID.stringValue = AssetDatabase.AssetPathToGUID (AssetDatabase.GetAssetPath ((sceneAsset.objectReferenceValue as SceneAsset)));
}
}
EditorGUI.EndProperty( );
}
}
#endif

EDIT: Script now works in build, anything GUID related works only in the Editor so I had to do some of that ISerializationCallbackReceiver gymnastic you spoke of.

@SteenLund on second look, the bug I was referring to was due to that problem unity is having when an asset change doesn’t register unless save scene or save project is pressed

1 Like

I agree and this is something I want to fix in a future release.
LoadScene should return the scene even though it is not loaded in the same frame
LoadSceneAsync returns an AsyncOperation this operation should have a scene property.

1 Like

It is a new front end, well no so new any more as it was introduced in 5.3. The backend has also been heavily refactored and modified.

But “allowActivate” is not actually new this has existed on the AsyncOperation for a long time.

1 Like

Renaming a scene which has already been added to the build settings should be fixed and ship should ship soon, not sure if it will be 5.6 and be back ported. Keep an eye on the release notes :slight_smile:

1 Like

Very cool, thanks. I’d like to hear your comment on the weird results I got on using async with allowActivate=false. When are assets decompressed etc… and why don’t I see anything on the memory profiler until the scene is “activated”