Search Unity

Editor is taking 20 seconds to enter playmode in an empty scene

Discussion in 'Editor & General Support' started by lynxelia, Feb 5, 2018.

  1. lynxelia

    lynxelia

    Joined:
    Sep 17, 2014
    Posts:
    18
    Does anyone have any advice on how to reduce the time it takes to enter play mode in the editor?

    Even when playing a scene that is completely empty, the editor seems to take about 20 seconds to enter play mode after pressing play. This is really hurting our team's productivity. I've checked just about every thread about this subject but couldn't find any solutions that fixed the problem.

    According to the Profiler window, unity seems to be spending the majority of it's time in EnterPlaymode>ReloadAssemblies>SerializeBackups, but I have no idea how to reduce the time spent in SerializeBackups. Deep profiling didn't yield any additional information either.

    ProfilerReloadAssemblies.png

    We are currently using Unity 2017.3.0f3. If anyone has any advice on what we should try, please let us know.
     
    Last edited: Feb 5, 2018
    ROBYER1 likes this.
  2. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    Usually SerializeBackups/DeserializeBackups time is coming from plugins and custom windows which are opened in the Editor.
    Entering Playmode involves domain reload (to use player-like scripts) and editor scripts has to survive this.
    Thus we serialize all alive Editor objects (Windows, statics, etc.) and restore them after domain reload.
    You can try to use Deep Profiler to see which plugins, classes (windows) are being saved and restored. And detailed related objects view can give a clue about some scripts as well.
     
    Peter77 likes this.
  3. lynxelia

    lynxelia

    Joined:
    Sep 17, 2014
    Posts:
    18
    @alexeyzakharov All right after running the Memory Profiler in "Detailed" mode, it seems like even when I open a new empty scene, all the objects from the previous scene are still hanging around in memory. So this means every time I open a scene the memory usage gets higher and higher. If I'm understanding you correctly, unity will attempt to serialize and deserialize all these objects even though they aren't used in the current scene, and that's why the time to enter play mode gets is so high even in an entirely empty scene. As a first step to fixing this, is there a way to force unity to clear out all these unused objects from memory? I wrote this script to try to unload unused assets, but it doesn't seem to do the trick. The objects are still stubbornly in memory. The Memory Profiler doesn't seem to give me any hint as to why these are still in memory. Everything in the "Referenced By" column are objects that were in the previous scene.

    Code (CSharp):
    1. [InitializeOnLoad]
    2. public static class MemoryManager
    3. {
    4.     static MemoryManager()
    5.     {
    6.         EditorSceneManager.sceneOpened += OnSceneOpened;
    7.     }
    8.  
    9.     static void OnSceneOpened(Scene scene, OpenSceneMode mode)
    10.     {
    11.         GarbageCollect();
    12.     }
    13.  
    14.     [MenuItem("Tools/Garbage Collect")]
    15.     static void GarbageCollect()
    16.     {
    17.         EditorUtility.UnloadUnusedAssetsImmediate();
    18.         GC.Collect();
    19.     }
    20. }
     
    Last edited: Feb 6, 2018
  4. lynxelia

    lynxelia

    Joined:
    Sep 17, 2014
    Posts:
    18
    @alexeyzakharov Also I'm trying to use the deep profiler but I'm not sure how to interpret the results. The "Other" category is the thing that seems to be taking the most amount of memory (almost 1 gb in my case). The highest memory usage under Other is ManagedHeap.ReservedUnusedSize and System.ExecutableAndDlls
     

    Attached Files:

  5. lynxelia

    lynxelia

    Joined:
    Sep 17, 2014
    Posts:
    18
    I think I'm getting a better idea of what's happening. The MemoryManager script above IS working, but the reason the garbage collector isn't collecting the objects from the old scene is because there are ghost references to those objects hanging around.

    So I believe the problem is because I made 2 incorrect assumptions:

    1. I assumed unity automatically unloads references when you open a new scene: I thought that when you open a scene that has some objects in it, and then open a brand new scene, Unity would automatically unload all unused references and garbage collect to get rid of those now unused objects from the previous scene. Unity definitely does not do that. Is this a bug? I can work around it by including the MemoryManager script I wrote above in my project, but cleaning up unused references from old scenes seems like something Unity should be doing by default when opening a new scene.

    2. I assumed that when an object is destroyed, it releases its references to other objects so those objects can be garbage collected: For example, I thought that if a MonoBehaviour has a Sprite field in it and that sprite field is assigned a value in the inspector, when that MonoBehaviour is destroyed, that assigned Sprite will be considered no longer referenced and will be garbage collected. This is not the case. Even when destroying the MonoBehaviour a ghost reference will still point to that Sprite and the Sprite will never be garbage collected. It'll just persist and continue to take up memory until you close unity. The solution is to set the Sprite field to null in the MonoBehaviour's OnDestroy method.

    So if you write your MonoBehaviour like this, any sprite you assign will hang around in memory forever and never be garbage collected:

    Code (CSharp):
    1.  
    2. public class ReferenceHolder : MonoBehaviour
    3. {
    4.     public Sprite m_SpriteValue;
    5. }
    6.  
    So you always need to write your MonoBehaviour like this, so that all object fields are set to null OnDestroy

    Code (CSharp):
    1.  
    2. public class ReferenceHolder : MonoBehaviour
    3. {
    4.     public Sprite m_SpriteValue;
    5.  
    6.     void OnDestroy()
    7.     {
    8.         m_SpriteValue = null;
    9.     }
    10. }
    11.  
    Luckily, all MonoBehaviours in our project inherit from one MonoBehaviour class. So instead of making this change to all our classes, we can simply use reflection to automatically null out any nullable field OnDestroy:

    Code (CSharp):
    1.  
    2. public class BaseMonoBehaviour : MonoBehaviour
    3. {
    4.     protected virtual void OnDestroy()
    5.     {
    6.        foreach (FieldInfo field in GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
    7.         {
    8.             Type fieldType = field.FieldType;
    9.             if (!fieldType.IsPrimitive)
    10.             {
    11.                 field.SetValue(this, null);
    12.             }
    13.         }
    14.     }
    15. }
    16.  
    Hopefully this information helps someone out there. I'll start cleaning up references using this method and see if that reduces the time spent in SerializeBackups and DeserializeBackups when entering play mode. If it does (or if it doesn't), I'll continue posting in this thread.
     
    Last edited: Feb 7, 2018
  6. lynxelia

    lynxelia

    Joined:
    Sep 17, 2014
    Posts:
    18
    Minor update: In addition to nulling out fields I had to clear out array, list, and dictionary fields as well so those would also be picked up by the garbage collector

    Code (CSharp):
    1.  
    2. void OnDestroy()
    3. {
    4.     foreach (FieldInfo field in GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
    5.     {
    6.         Type fieldType = field.FieldType;
    7.  
    8.         if (typeof(IList).IsAssignableFrom(fieldType))
    9.         {
    10.             IList list = field.GetValue(this) as IList;
    11.             if (list != null)
    12.             {
    13.                 list.Clear();
    14.             }
    15.         }
    16.  
    17.         if (typeof(IDictionary).IsAssignableFrom(fieldType))
    18.         {
    19.             IDictionary dictionary = field.GetValue(this) as IDictionary;
    20.             if (dictionary != null)
    21.             {
    22.                 dictionary.Clear();
    23.             }
    24.         }
    25.  
    26.         if (!fieldType.IsPrimitive)
    27.         {
    28.             field.SetValue(this, null);
    29.         }
    30.     }
    31. }
    32.  
     
    Last edited: Feb 7, 2018
  7. lynxelia

    lynxelia

    Joined:
    Sep 17, 2014
    Posts:
    18
    Ok so following the steps above I've managed to get the enter play mode time down from 17 seconds to 6 seconds. There are still a few assets that are stubbornly sticking around even after calling EditorUtility.UnloadUnusedAssetsImmediate and GC.Collect. I'll see if I can pinpoint what is holding onto these references.

    I believe it may have something to do with the OnValidate function. Weirdly enough, I had a MonoBehaviour attached to a game object. That MonoBehaviour had a reference to a RigidBody2D also attached to the same game object, and on the OnValidate function of the MonoBehaviour I was setting some fields on the RigidBody2D. For some odd reason this caused the entire game object and all its asset references to not be garbage collected. No idea why. Possibly a unity bug? In any case, I just made sure to not set any fields on the rigidbody2d during OnValidate and that fixed the issue.

    I'm guessing there may be more unity bugs like this that are tripping up the garbage collector. I'll see if I can track them down and get the EnterPlayMode time down even further.
     
    Last edited: Feb 7, 2018
    Loius, alexeyzakharov and f0ff886f like this.
  8. f0ff886f

    f0ff886f

    Joined:
    Nov 1, 2015
    Posts:
    201
    Thank you for all this information, just jumping into PlayMode has almost turned into a coffee task for me. I'll try to experiment with this myself, unfortunately I don't have one base class I inherit from so the changes would be a bit more involved.
     
  9. SmartMediaNL

    SmartMediaNL

    Joined:
    Sep 29, 2016
    Posts:
    77
    @lynxelia Thanks for the information. I will make changes when i can myself. Never realized this could / would happen. As it turns out I moved my project to a 6Gb Ramdrive to increase the speed. (As even a SD is to slow)
    It seems I can make it even faster if I implement your suggestions i bet. Thanks
     
  10. lynxelia

    lynxelia

    Joined:
    Sep 17, 2014
    Posts:
    18
    I'm glad people seem to be finding this helpful! I'm sure there are many others that have this problem since our team has found many forum posts about the issue but didn't find any good answers in them. These steps seem to be working for us, so hopefully it will work for you too. I'll continue posting findings here.
     
  11. lynxelia

    lynxelia

    Joined:
    Sep 17, 2014
    Posts:
    18
    Here's a few more tips:

    1. !EditorApplication.isPlayingOrWillChangePlaymode: Surround code you don't want to be executed while entering play mode with this entering play mode check

    Code (CSharp):
    1. #if UNITY_EDITOR
    2.             if (!EditorApplication.isPlayingOrWillChangePlaymode)
    3.             {
    4.                 // time intensive code you don't want executed when entering play mode
    5.             }
    6. #endif

    1. OnValidate: OnValidate will be called on every object in your scene while entering play mode, so make sure you don't have anything too time intensive in your OnValidate methods. Calls to GetComponent, GetComponentsInChildren, GameObject.Find, FindObjectOfType, etc, in OnValidate will greatly increase the time it takes to enter playmode. If you do have time intensive code in your OnValidate methods, surround the code with the "entering play mode check" in tip #1.

    2. InitializeOnLoad: Search your project for any classes marked with the attribute [InitializeOnLoad]. Any classes marked with this attribute will be run when unity starts up AND every time you enter play mode. The unity documentation mentions the former but does not mention the latter. If you do have classes marked with [InitializeOnLoad], make sure you surround anything in their constructor with the "entering play mode check" in tip #1. Keep in mind, even if you don't remember adding this attribute anywhere to your code, this is a common attribute that is used in lot of asset store packages and unfortunately the creators of these packages don't realize that this code will be executed every time you enter play mode. In our case, we included a script in our project that automatically scans for empty folders in the project and deletes them every time unity is opened. (We included this script so we didn't have the meta hell caused by git and unity whenever someone moves files around and deletes/renames folders.) What we didn't realize was that this empty folder check wasn't only happening on starting up unity but also every time we entered play mode, which increased the play mode enter time significantly.
     
    Last edited: Feb 8, 2018
  12. lynxelia

    lynxelia

    Joined:
    Sep 17, 2014
    Posts:
    18
    @alexeyzakharov Any comment on the fact that unity does not unload and garbage collect objects from the previous scene when opening a new scene? Is this expected behaviour for unity or should I report this as a bug? We split our game across several scenes, so without that MemoryManager script I wrote above, the EnterPlayMode times would really start to stack up every time we switch to a different scene to edit it.
     
    Last edited: Feb 8, 2018
  13. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    @lynxelia Thank you a lot for the detailed investigation!

    We have looked at the serialization code with @lukaszunity and it seems that your findings are correct - we do serialize managed counterpart of destroyed UnityEngine.Object.
    We have discussed different possibilities how avoid this and came to conclusion that we can ignore destroyed objects during serialization. That would cause acceptable minor behavior change - scripts won't be able to use managed part of destroyed object during domain reload. Which makes total sense, because Destroy is equivalent to Dispose and means that object is in invalid state and can't be used anymore.

    It would be very helpful if you can file a bug and provide a repro project so we can validate potential solution.

    Also we'll add markers to InitializeOnLoad and OnValidate, so the activity there can be visible without deep profiling.
     
    hippocoder, SmartMediaNL and Peter77 like this.
  14. lynxelia

    lynxelia

    Joined:
    Sep 17, 2014
    Posts:
    18
    @alexeyzakharov Wonderful! So If I'm understanding correctly, once the change is made to ignore managed references in destroyed objects during serialization, I will no longer need to clear and null out all the fields in my MonoBehaviours OnDestroy, right? Out of curiosity, how long has this bug been around? Was it something that was introduced in a fairly recent version of unity? I have another project on an older version of unity (Unity 5.2.2) and am wondering if I need to port the workaround to that project as well (since we aren't planning on updating the version of unity for that project).

    How about unity not calling EditorUtility.UnloadUnusedAssetsImmediate and GC.Collect when opening a scene? That's a separate bug right? Or is that expected behaviour? Because without doing that, the old references from the previous scene always stick around to the next scene you open.

    I'll try to find time this weekend to create a small repo project for both these issues. Thanks for looking into it!
     
    hippocoder likes this.
  15. lynxelia

    lynxelia

    Joined:
    Sep 17, 2014
    Posts:
    18
    Ok we encountered one more bug with garbage collection. We figured out how to avoid the issue, but I'm not too familiar with what's going on under the hood of unity to understand why it's happening.

    As we suspected, OnValidate seems to mess with garbage collection if you set a field on any other component during OnValidate.

    For example:

    Code (CSharp):
    1. [RequireComponent(typeof(Rigidbody2D))]
    2. public class Example : MonoBehaviour
    3. {
    4.     void OnValidate()
    5.     {
    6.         GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Dynamic;
    7.     }
    8. }
    Attaching a MonoBehaviour like this to a GameObject will cause that GameObject and all its managed references to not be garbage collected no matter what.

    This was the issue that was causing those stubborn asset references to stick around for us even after clearing out and nulling all their fields and calling EditorUtility.UnloadUnusedAssetsImmediate and GC.Collect.

    Luckily we have another workaround for this. We mainly use OnValidate to validate properties whenever a dev changes those properties in the inspector. We don't actually want this validation to occur any other time (e.g. when the asset is loaded or when entering play mode). So as a work around, we rolled our own "OnValidateProperty" method. This method is guaranteed to only execute when a property is changed in the inspector, no where else. Moving all our code from OnValidate into this method prevents all those weird issues that trip up the garbage collector.

    To use this workaround, instead of inheriting from MonoBehaviour, you need to make sure all your MonoBehaviours inherit from a BaseMonoBehaviour class with the following OnValidateProperty method (though for cleanliness I am ommiting it below, remember to also include the OnDestroy code I mentioned before in this BaseMonoBehaviour class):

    Code (CSharp):
    1.  
    2. [CanEditMultipleObjects, CustomEditor(typeof(BaseMonoBehaviour), true)]
    3. public class BaseMonoBehaviourEditor : Editor
    4. {
    5.     public override void OnInspectorGUI()
    6.     {
    7.         serializedObject.Update();
    8.  
    9.         SerializedProperty iterator = serializedObject.GetIterator();
    10.         bool enterChildren = true;
    11.         while (iterator.NextVisible(enterChildren))
    12.         {
    13.             enterChildren = false;
    14.  
    15.             OnPropertyGUI(property);
    16.         }
    17.  
    18.         serializedObject.ApplyModifiedProperties();
    19.     }
    20.  
    21.     public void OnPropertyGUI(SerializedProperty property)
    22.     {
    23.         OnPropertyGUI(property, new GUIContent(property.displayName, property.tooltip));
    24.     }
    25.  
    26.     public void OnPropertyGUI(SerializedProperty property, GUIContent label)
    27.     {
    28.         BaseMonoBehaviour t = target as BaseMonoBehaviour;
    29.  
    30.         EditorGUI.BeginChangeCheck();
    31.  
    32.         DrawProperty(property, label);
    33.  
    34.         if (EditorGUI.EndChangeCheck())
    35.         {
    36.             property.serializedObject.ApplyModifiedProperties();
    37.             property.serializedObject.Update();
    38.             t.OnValidateProperty(property.name);
    39.         }
    40.     }
    41.  
    42.     public void OnPropertyGUI(Rect position, SerializedProperty property, GUIContent label)
    43.     {
    44.         BaseMonoBehaviour t = target as BaseMonoBehaviour;
    45.  
    46.         EditorGUI.BeginChangeCheck();
    47.  
    48.         DrawProperty(position, property, label);
    49.  
    50.         if (EditorGUI.EndChangeCheck())
    51.         {
    52.             property.serializedObject.ApplyModifiedProperties();
    53.             property.serializedObject.Update();
    54.             t.OnValidateProperty(property.name);
    55.         }
    56.     }
    57.  
    58.     public virtual Rect GetPropertyControlRect(SerializedProperty property, GUIContent label, params GUILayoutOption[] options)
    59.     {
    60.         return EditorGUILayout.GetControlRect(!string.IsNullOrEmpty(label.text), EditorGUI.GetPropertyHeight(property), options);
    61.     }
    62.  
    63.     protected virtual void DrawProperty(SerializedProperty property, GUIContent label, params GUILayoutOption[] options)
    64.     {
    65.         Rect position = GetPropertyControlRect(property, label, options);
    66.         DrawProperty(position, property, label);
    67.     }
    68.  
    69.     protected virtual void DrawProperty(Rect position, SerializedProperty property, GUIContent label)
    70.     {
    71.         EditorGUI.PropertyField(position, property, label);
    72.     }
    73. }
    74.  
    This way in your class you can easily validate properties without worrying about it tripping up the garbage collector or taking up scene load time.

    Code (CSharp):
    1. [RequireComponent(typeof(Rigidbody2D))]
    2. public class Example : BaseMonoBehaviour
    3. {
    4.     public bool m_CanMove;
    5.  
    6.     public override bool OnValidateProperty(string propertyName)
    7.     {
    8.         if (propertyName == "m_CanMove")
    9.         {
    10.             if (!m_CanMove)
    11.             {
    12.                 GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Static;
    13.                 return true;
    14.             }
    15.         }
    16.  
    17.         return base.OnValidateProperty(propertyName);
    18.     }
    19. }
    After doing this, we have completely clean scene loads! No more ghost references hanging around and taking up valuable memory. Woo!
     
    Last edited: Feb 11, 2018
  16. lynxelia

    lynxelia

    Joined:
    Sep 17, 2014
    Posts:
    18
    I have a post above that lays out a full solution for the final garbage collector bug we encountered (the OnValidate bug). But the post says "This message is awaiting moderator approval, and is invisible to normal visitors." I'm not sure how to contact a moderator to get it approved. But if anyone is encountering the same garbage collection issue with OnValidate that we did and wants the full solution we used, I'm working on creating a full writeup of all these issues and their various workarounds. I'll post a link to that writeup here in a bit.
     
    Last edited: Feb 9, 2018
  17. lynxelia

    lynxelia

    Joined:
    Sep 17, 2014
    Posts:
    18
    masterton and Peter77 like this.
  18. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,618
    Has the bug-report been added to the public issue tracker already? I can't seem to find it, but would like to keep an eye on it.
     
  19. lynxelia

    lynxelia

    Joined:
    Sep 17, 2014
    Posts:
    18
    @Peter77 I've sent 3 bug reports and repo cases for steps 2, 3, and 4 of that write-up I did. I don't believe they've been added to the public issue tracker yet, but I'll post here once they are.
     
    alexeyzakharov and Peter77 like this.
  20. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    Yes, we want to make it so you shouldn't modify scripts to cleanup zombie references.

    I'm pretty sure it was always like that (though I didn't look too much back in time)

    We do call UnloadUnusedAssets when we open scene in the Editor or use LoadScene (nonadditive) API. That happens after the scene is opened. But we don't do it when opening scene backup while entering playmode. That probably requires more discussion as doing it after scene is opened means additional performance impact for entering playmode. It might reduce enter playmode time when called before serialization in a way that preserves assets used by a scene alive, so we don't load back them during scene loading.

    Thanks for that! Please put them here once you have ids.
     
  21. lynxelia

    lynxelia

    Joined:
    Sep 17, 2014
    Posts:
    18
    Hmm that doesn't seem to be the case, in my experience. Or maybe unity is calling UnloadUnusedAssets but not GC.Collect afterwards? I sent a bug report and a repro project that demonstrates that unused assets are definitely not unloaded and garbage collected when opening scenes (Case 1000791) and I just got an email back saying "We have been able to reproduce this bug and have sent it for resolution with our developers."

    In any case, it should hopefully be an easy fix on your guy's end!

    Thanks again for the support on these issues.
     
    Peter77 likes this.
  22. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    We don't call GC.Collect.
    I think what might speedup enter playmode sequence is:
    1. Save current scene with storing list of assets which are referenced by a scene.
    2. Destroy scene.
    3. [Optional] Call GC.Collect.
    4. Call UnloadUnusedAssets leaving alive referenced by scene assets.
    5. Backup alive objects and don't follow managed parts of destroyed ones.
    6. Reload domain.
    7. Restore objects and load scene.

    Thanks for the project!
     
  23. lynxelia

    lynxelia

    Joined:
    Sep 17, 2014
    Posts:
    18
    All right I'm happy to report all 3 bugs have made their way onto the public issue tracker. You can track their progress there:

    https://issuetracker.unity3d.com/is...bage-collected-after-destroying-monobehaviour

    https://issuetracker.unity3d.com/is...e-not-unloaded-when-opening-a-different-scene

    https://issuetracker.unity3d.com/is...s-used-it-to-set-a-field-on-another-component

    For some reason, the description of the 3rd bug was butchered really badly. It should say "Assets are *not* unloaded if onvalidate is used to set a field on another component"
     
    alexeyzakharov likes this.
  24. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    978
    Here's a script to measure the compilation/playmode load time:

    Code (CSharp):
    1.  
    2. #if UNITY_EDITOR
    3. using UnityEngine;
    4. using UnityEditor;
    5.  
    6. [InitializeOnLoad]
    7. class CompileTime : EditorWindow
    8. {
    9.     static bool IsCompilingTrackingTime;
    10.     static bool IsPlaymodeTrackingTime;
    11.     static bool HasPlaymodeTracked = true;
    12.     static double EnterPlaymodeStartTime;
    13.     static double CompileStartTime;
    14.  
    15.     static CompileTime()
    16.     {
    17.         EditorApplication.update += Update;
    18.         CompileStartTime = PlayerPrefs.GetFloat("CompileStartTime", 0);
    19.         if (CompileStartTime > 0)
    20.         {
    21.             IsCompilingTrackingTime = true;
    22.         }
    23.     }
    24.  
    25.     static void Update()
    26.     {
    27.         if (EditorApplication.isCompiling && !IsCompilingTrackingTime)
    28.         {
    29.             CompileStartTime = EditorApplication.timeSinceStartup;
    30.             PlayerPrefs.SetFloat("CompileStartTime", (float)CompileStartTime);
    31.             IsCompilingTrackingTime = true;
    32.         }
    33.         else if (!EditorApplication.isCompiling && IsCompilingTrackingTime)
    34.         {
    35.             var finishTime = EditorApplication.timeSinceStartup;
    36.             IsCompilingTrackingTime = false;
    37.             var compileTime = finishTime - CompileStartTime;
    38.             PlayerPrefs.DeleteKey("CompileStartTime");
    39.             Debug.Log("Script compilation time: \n" + compileTime.ToString("0.000") + "s");
    40.         }
    41.  
    42.         if (EditorApplication.isPlayingOrWillChangePlaymode && !IsPlaymodeTrackingTime && !EditorApplication.isPlaying)
    43.         {
    44.             IsPlaymodeTrackingTime = true;
    45.             EnterPlaymodeStartTime = EditorApplication.timeSinceStartup;
    46.             PlayerPrefs.SetFloat("EnterPlaymodeStartTime", (float)EnterPlaymodeStartTime);
    47.         }
    48.         else if (EditorApplication.isPlaying && HasPlaymodeTracked)
    49.         {
    50.             HasPlaymodeTracked = false;
    51.             var finishTime = EditorApplication.timeSinceStartup;
    52.             var playmodeLoadTime = finishTime - PlayerPrefs.GetFloat("EnterPlaymodeStartTime");
    53.             PlayerPrefs.DeleteKey("EnterPlaymodeStartTime");
    54.             Debug.Log("Playmode load time: \n" + playmodeLoadTime.ToString("0.000") + "s");
    55.         }
    56.     }
    57. }
    58.  
    59. #endif
     
  25. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    Hi guys!

    Small update on the topic as we are now working on optimizing enterplaymode.
    We've looked into all three reported issues.

    1. https://issuetracker.unity3d.com/is...bage-collected-after-destroying-monobehaviour
    This one we want to be addressed, though a bit differently from what I originally thought.
    We tried multiple things that could make the fix transparent for developers.
    Automatically nullify managed references - raised several issues:
    - Performance overhead when destroying objects.
    - Breakage of functionality in some projects when entering playmode.
    - Dissonance with IDisposable pattern (You can think of Object.Destroy as Dispose, although even historically UnityEngine.Object is not IDisposable, the way it is used is very similar).
    - Introduction of implicit behavior for Object.Destroy.
    We also tried to not traverse those managed objects during AssetGC and decided that this creates an inconsistent behavior with Mono GC, as we would also need to alter GC.Collect (which would be nontrivial) for UnityEngine.Object. And again be inconsistent with how IDisposable pattern works.

    So instead what I think makes sense to do is to notify developer that there are manages reference that are not nullified after after Destroy is called. This can be an optional feature of the Editor/Development Player with no performance overhead in Release. Combined with new MemoryProfiler it would make the discovery of zombie objects much easier.

    Wrt serialization of those zombie objects during domain reload - we've verified that we are not traversing and don't serialize managed fields of those which were destroyed.

    2. https://issuetracker.unity3d.com/is...e-not-unloaded-when-opening-a-different-scene
    I've checked what has been happening and apparently it was related to the selection. Inspector or Project View can reference objects and keep them alive even though their holder was explicitly destroyed from the code or new scene was loaded. This creates some memory pressure, but only in the Editor and the effect doesn't scale with amount of objects in the scene or project. Thus we decided to not fix it.

    3. https://issuetracker.unity3d.com/is...s-used-it-to-set-a-field-on-another-component
    The potential fixes we investigated could cause compatibility issues and projects breakage. We won't fix it as it has more downsides than upsides..

    Thank you for the raising the issues and repro projects! That really helped to see the behavior and analyze it.
     
    nirvanajie, hippocoder and FeastSC2 like this.
  26. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,618
    alexeyzakharov likes this.
  27. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    Yes, we definitely want to address this.
    We are currently exploring several ways how we can improve EnterPlaymode time.
    One of promising approaches is to avoid scene reload. We would like to add to Project Settings an option to reset currently loaded objects in the scene instead of reloading them from disk (and emulate lifecycle behavior we currently have). That would remove AwakeFromLoad call from EnterPlaymode sequence as objects are already loaded.
    Another approach we are considering is to avoid domain reload when scripts didn't change. Combined they should make the complexity linear and depending only on scripts count. And improve usability in case you don't want to simulate 100% of what's happening in the Player during entering Playmode, but want a fast preview.
     
  28. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,618
    That's great, a very much appreciated improvement :) Thanks for the reply!
     
    alexeyzakharov likes this.
  29. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    978
    Good luck if you manage to shave off a few seconds for big projects it'll be awesome!
     
  30. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    Just discovered this thread. I'm suffering every day as my project is getting larger and larger.
    Reset instead of Reloading is what it should've been in the first place.
    If nothing changed between the Play sessions, why reload everything? It makes me dumbfounded.
    If you have worked on large scale games, you would have never made it behave like so.

    UE4 always let you go into Play mode instantly no matter how bloated your scenes are.
    They do it by making reload only once when assemblies are compiled and Edit mode the same as Play mode.
    Edit mode is basically Play mode with Tick fixed at 0, except for particles.

    Please make an experimental version ASAP.
    You can perhaps create an additional "Quick Play" button so that we can use it for a quick test and for the full test we will use the normal Play button. And when "Quick Play" becomes stable, it can replace the normal "Play" button.
    What I normally do is to create a simple scene with few objects, thus there is very little can go wrong with the Quick Play.
    Things take a really long time in Unity and please don't make us suffer for long.

    Thanks.
     
  31. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    I understand the pain and we are trying different approaches to improve a situation. We are working right now on landing what was discussed above as an experimental feature.
    Unfortunately enter playmode without domain reload approach changes current behavior and would require modifications of a game code. E.g. static values are not being reset. While game code is relatively easy to fix, utilities and asset store packages might need a special treatment. We are collecting all potential issues to help with with adoption.
     
    teutonicus, Grizmu, optimise and 2 others like this.
  32. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    Thanks, it is currently my number one wish and it will make me really happy.

    Static values that require reset sounds like the code is written wrong in the first place. When you make the experimental version, make it a requirement for Asset developers to adopt the new way. There are so many Assets are abusing "InitializeOnLoad" for a quick clean up out there. It should be called only when the new assemblies are loaded, not between Play sessions.
    And they are called even when they are not used in the scene which drives me crazy.

    Great day!
     
    Last edited: Feb 20, 2019
    hippocoder likes this.
  33. Lars-Steenhoff

    Lars-Steenhoff

    Joined:
    Aug 7, 2007
    Posts:
    3,527
    500 milliseconds that's all it should take to enter playmode ;)
     
    Last edited: Feb 20, 2019
  34. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    Haha, that sounds familiar. It was one of the important announcement during last years Keynotes from Joachim, many got so excited including me.
    He was talking about 500ms compiling time using incremental compiler but it got postponed/scrapped.
    However, I believe entering play mode in 500ms is feasible.
     
    Lars-Steenhoff likes this.
  35. optimise

    optimise

    Joined:
    Jan 22, 2014
    Posts:
    2,129
    IIRC it's 500 ms to compile code.
     
  36. Lars-Steenhoff

    Lars-Steenhoff

    Joined:
    Aug 7, 2007
    Posts:
    3,527
    optimise likes this.
  37. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    Yeah, Joachim owes us an apology, but I'll forgive him if Unity can make Enter to Play mode in 500ms. So please make this the highest priority. I'm in too much pain.
     
  38. optimise

    optimise

    Joined:
    Jan 22, 2014
    Posts:
    2,129
    Will it ship at Unity 2019.2 cycle?
     
  39. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    A lot of developers like me don't use asset store (or do but are willing to update asset store code), and want performance to develop with, but decisions "because it might hurt asset store" prevent the performance we could have...

    So the engine is held back because of... a shop? Seems illogical if I'm honest. Why not make it optional so those of us who don't use random assets from random developers aren't held back by them?

    If you're making money from asset store, shouldn't you also make it your business to make sure code sold on asset store runs well? Otherwise you take 30% for garbage, often literally.

    I realise the post could come across challenging and that's unavoidable unfortunately as there are some strange decisions being made not in favour of performance.
     
    Peter77, chrisk and optimise like this.
  40. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    It will definitely be in 2019.3, but I can't guarantee it lands to 2019.2.

    There is _absolutely_ no dependency on asset store. I was saying that this option will not be 100% compatible with game code and thus some one time action would be required to use it. That is why it will be opt-in experimental setting, so you can continue working as is if you are ok with current enter playmode sequence.
    When some feature lands purely depends on if the feature is ready for a release cycle (before stabilization period for a particular release).
     
    Last edited: Feb 27, 2019
    hippocoder, Peter77 and optimise like this.
  41. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    Hi, alexeyzakharov, this is the most painful feature in Unity right now. I'm not sure if I can continue to suffer for that long. I hope you bring it to the roadmap table with the most urgent, besides bugs, and make it available as early as possible. I fully understand that it's experimental and I was hoping you would bring 2019.1.
    Millions of users can immediately benefit and I don't think there is anthing you can benefit more than this interms of saving time in every day usage.
    Thanks.
     
    Sycoforge and alexeyzakharov like this.
  42. Lars-Steenhoff

    Lars-Steenhoff

    Joined:
    Aug 7, 2007
    Posts:
    3,527
    there is one more thing that can save time:

    Background processing for importing and compressing textures.
    This would save me a lot of time because now the editor is locked.
     
  43. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,336
    @alexeyzakharov

    Is there any work being done on improving our ability to profile things that take a long time? If I try to deep profile the time it takes to enter play mode, Unity tells me that the profiler has to discard the frame because there's too much data. That doesn't help very much! There's actually exactly zero overlap between frames I care about and frames the profiler handles.

    It's also a real pain to profile entering playmode outside of deep profiling. I have to turn the profiler on, and then wait half a minute for the game to go into play mode. Then I have about 2 seconds to turn off profiling, because otherwise the frames I'm profiling has been scrolled out of the screen by frames I don't care about.
     
    FeastSC2 and alexeyzakharov like this.
  44. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    A flood gate is open on clunky Editor. ^^
    I do also have other problem where I often click on check-box twice. The slow Editor update makes me thinking I didn't hit the tiny checkbox correctly. But slow to enter the play mode overwhelms all other pains.
    If I have any influence, I'll fix the clunky Editor with the highest priority. ECS, Jobs, SRP are all nice but it requires user adoption whereas making Editor faster doesn't. Everyone will benefit right away.

    Can we perhaps hear the announcement; "Performance by Default for Editor Too!!!" at GDC?
     
  45. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    yes,
    2019.1 will have more important information exposed without deep profiler - static constructors/runtimeinitialize (#3 reason of slow domain reloads), mono jit samples, finalizer thread, etc.

    2018.2 had increased default pool size for profiler.
    2018.3 has a cmdline/scripting api parameter to configure that pool.
    If you still have an issue please file a bug - I'll make sure you can see all the data.

    What do you think would solve this problem for you?
    1. Adding "Stop on spike" toggle.
    2. Making visible profiler frames count configurable.
    3. Dump data to temp folder continuously (round robin) so you can inspect it later.
     
    Baste likes this.
  46. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,336

    A stop on spike toggle would be the best ones out of that. The profiler count isn't really the problem, because in this instance I'm only caring about a couple of frames.

    That being said, it wouldn't be a complete fix - for entering playmode taking a while, there's usually 2 or 3 spikes - 1 spike when starting to go into playmode, 1 spike for going into playmode, and occasionally 1 spike on the very first frame after entering playmode in cases where a lot of scripts are yielding on startup to wait for some resource.

    So ideally I'd want to wait a couple of frames past when the spike happens, to get a picture of the surrounding time - is the spike single-frame or several frames, what's happening in the vicinity, those kinds of things.

    3 is also interesting, if it's doable.


    Another option would be "keep only frames over a certain ms threshold". That would be usefull both when profiling this, but also in general when trying to make the game run smoothly - if I'm targetting 16.67 ms frames, I would perhaps be interested in looking at all frames that are above 16ms, to see if there's common issues there.

    Some of your collegues are helping me with getting 2018.3 to not crash. Once that's done, I'll check it out for sure, thanks! Do you remember what the API entrypoint is?
     
    alexeyzakharov likes this.
  47. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,618
    That's an interesting thought, sounds really useful to me!
     
    alexeyzakharov likes this.
  48. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    DwinTeimlon, Baste and Peter77 like this.
  49. optimise

    optimise

    Joined:
    Jan 22, 2014
    Posts:
    2,129
  50. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,336
    @alexeyzakharov, 2018.3 in combination with a bit of purging of unnecessary bits allowed me to finally get to the relevant information, which is great. It revealed a couple of things that might be of interest, though:

    - Deep profiling entering play mode takes a lot of ram. In my case, about 20 gigs. Unity became almost completely unresponsive until I closed all my other programs - I assume paging was going on. After that, the profiler worked, but slowly. Not as slowly as it used to back in the days (you've made a massive improvement there), but still slowly.
    I realize that deep profiling does generate a lot of data, but could it be possible to somehow reduce the amount of memory needed?

    - I tried to save the stack trace, which after a short while crashed Unity due to out of memory errors:
    upload_2019-4-2_17-39-6.png
    The saved stack trace file is corrupt, as you'd expect, but surprisingly the seconds up until I start entering play mode can be read! So that's impressive. That being said, it should probably not be possible to crash the editor by saving the profiler data.

    - The reason why things are taking so long (in this case) is that static batching is spending an enormous amount of time. When I'm not profiling, turning static batching on increases the time it takes to enter play mode by about a minute. The profiler's telling me that all of that time is spent in Array.Sort. This happens in a setup where we have a lot of scenes loaded additively. If I take all of the additive scene's content and put it into a single, master scene, the overhead from static batching is almost gone. Who do I get in touch with to talk about this? It's probably not just affecting us.

    EDIT: made a thread.
     
    Last edited: Apr 3, 2019
    alexeyzakharov and Peter77 like this.