Search Unity

ObjectField Memory Leak?

Discussion in 'Scripting' started by Leslie-Young, Aug 9, 2015.

  1. Leslie-Young

    Leslie-Young

    Joined:
    Dec 24, 2008
    Posts:
    1,148
    Did anyone run into a memory leak like this and found how to fix it? Tested under Unity 5.1.1f1 (Windows 10)

    Add this to an editor script, place a sprite in the sprite field and see Unity memory usage increase with each frame.

    My actual problem is a bit more complicated than this and does not even use the scene view to render the UI but a very custom UI rendered in the OnGUI() of an EditorWindow. This was just the easiest way to demonstrate this problem with ObjectField.

    Code (CSharp):
    1.  [InitializeOnLoad]
    2. public class GlobalStaticTest
    3. {
    4.      private static Sprite test;
    5.      static GlobalStaticTest()
    6.      {
    7.          SceneView.onSceneGUIDelegate += OnSceneGUI;
    8.      }
    9.      private static void OnSceneGUI(SceneView sceneView)
    10.      {
    11.          Handles.BeginGUI();
    12.          GUILayout.BeginArea(new Rect(0, 0, 200, 200));
    13.          {
    14.              test = (Sprite)EditorGUILayout.ObjectField("test", test, typeof(Sprite), false);
    15.          }
    16.          GUILayout.EndArea();
    17.          Handles.EndGUI();
    18.      }
    19. }
    Here is another example. It even happens in a test like this. If the EditorGUILayout.ObjectField() was in the OnInspectorGUI() then it would be fine.

    Code (CSharp):
    1.      public class TEST_OBJECT : MonoBehaviour
    2.      {
    3.          public Sprite test;
    4.      }
    5.    
    6.      // ....
    7.    
    8.      [CustomEditor(typeof(TEST_OBJECT))]
    9.      public class TEST_OBJECT_Inspector : Editor
    10.      {
    11.          public override void OnInspectorGUI()
    12.          {
    13.          }
    14.    
    15.          protected void OnSceneGUI()
    16.          {
    17.              Handles.BeginGUI();
    18.              GUILayout.BeginArea(new Rect(0, 0, 200, 200));
    19.              {
    20.                  TEST_OBJECT Target = (TEST_OBJECT)target;
    21.                  Target.test = (Sprite)EditorGUILayout.ObjectField("test", Target.test, typeof(Sprite), false);
    22.              }
    23.              GUILayout.EndArea();
    24.              Handles.EndGUI();
    25.          }
    26.      }
    Doing it in the normal/ basic way, like this, causes no problems.

    Code (CSharp):
    1.  public class TestWindow : EditorWindow
    2. {
    3.      private static Sprite test;
    4.      [MenuItem("Window/Test")]
    5.      public static void Show_TestWindow()
    6.      {
    7.          EditorWindow.GetWindow<TestWindow>("Test");
    8.      }
    9.      protected void OnGUI()
    10.      {
    11.          test = (Sprite)EditorGUILayout.ObjectField("test", test, typeof(Sprite), false);
    12.      }
    13. }
     
  2. Leslie-Young

    Leslie-Young

    Joined:
    Dec 24, 2008
    Posts:
    1,148
    In my actual code (not the samples above) I've tracked it down to a Repaint() being called from OnGUI()

    I'll probably never have a use for doing an ObjectField() in OnSceneGUI() since it can just as well then be done in an OnGUI() of EditorWindow or the Inspector. So the original question is not really a problem in such a case though I am curious why this memory leak happens.
     
  3. Nederseth

    Nederseth

    Joined:
    Sep 18, 2013
    Posts:
    4
    Same problem here :( and it is a big problem for the tool I am building.
    Has anyone a solution?
     
  4. Leslie-Young

    Leslie-Young

    Joined:
    Dec 24, 2008
    Posts:
    1,148
    Seems to be only when Sprite is used. Texture2D does not cause this problem.

    This cause no memory leak.

    Code (CSharp):
    1. [InitializeOnLoad]
    2. public class GlobalStaticTest
    3. {
    4.     private static Texture2D test2;
    5.  
    6.     static GlobalStaticTest()
    7.     {
    8.         SceneView.onSceneGUIDelegate += OnSceneGUI;
    9.     }
    10.  
    11.     private static void OnSceneGUI(SceneView sceneView)
    12.     {
    13.         Handles.BeginGUI();
    14.         test2 = (Texture2D)EditorGUI.ObjectField(new Rect(0, 0, 200, 200), test2, typeof(Texture2D), false);
    15.         Handles.EndGUI();
    16.     }
    17. }
    18.  
    while this does ...

    Code (CSharp):
    1. [InitializeOnLoad]
    2. public class GlobalStaticTest
    3. {
    4.     private static Sprite test1;
    5.  
    6.     static GlobalStaticTest()
    7.     {
    8.         SceneView.onSceneGUIDelegate += OnSceneGUI;
    9.     }
    10.  
    11.     private static void OnSceneGUI(SceneView sceneView)
    12.     {
    13.         Handles.BeginGUI();
    14.         test1 = (Sprite)EditorGUI.ObjectField(new Rect(0, 0, 200, 200), test1, typeof(Sprite), false);
    15.         Handles.EndGUI();
    16.     }
    17. }
    18.  
     
  5. Leslie-Young

    Leslie-Young

    Joined:
    Dec 24, 2008
    Posts:
    1,148
    Digged around in the editor dll and tracked it down to the call
    Code (CSharp):
    1. EditorGUIUtility.s_ObjectContent.image = AssetPreview.GetMiniThumbnail(obj);
    GetMiniThumbnail() is in the C++ side of Unity so I can't go any further.
    I am going to submit this as a bug.

    Simple test ...
    Add a texture and things are stable. Add a sprite and see memory usage increase.

    Code (CSharp):
    1.  
    2. public class TEST_OBJECT : MonoBehaviour
    3. {
    4.    public Sprite test1;
    5.    public Texture2D test2;
    6. }
    7.  
    8. ......
    9.  
    10. [CustomEditor(typeof(TEST_OBJECT))]
    11. public class TEST_OBJECT_Inspector : Editor
    12. {
    13.    public override void OnInspectorGUI()
    14.    {
    15.      TEST_OBJECT Target = (TEST_OBJECT)target;
    16.      Target.test1 = (Sprite)EditorGUILayout.ObjectField("sprite", Target.test1, typeof(Sprite), false);
    17.      Target.test2 = (Texture2D)EditorGUILayout.ObjectField("texture", Target.test2, typeof(Texture2D), false);
    18.    }
    19.  
    20.    protected void OnSceneGUI()
    21.    {
    22.      TEST_OBJECT Target = (TEST_OBJECT)target;
    23.      Handles.BeginGUI();
    24.      GUILayout.BeginArea(new Rect(0, 0, 100, 200));
    25.      {
    26.        if (Event.current.type == EventType.Repaint)
    27.        {
    28.          // sprite
    29.          if (Target.test1 != null)
    30.          {
    31.            Texture2D img = AssetPreview.GetMiniThumbnail(Target.test1);
    32.            GUIContent content = new GUIContent(img);
    33.            GUIStyle.none.Draw(new Rect(0, 0, 100, 100), content.image, false, false, false, false);
    34.          }
    35.  
    36.          // texture
    37.          if (Target.test2 != null)
    38.          {
    39.            Texture2D img = AssetPreview.GetMiniThumbnail(Target.test2);
    40.            GUIContent content = new GUIContent(img);
    41.            GUIStyle.none.Draw(new Rect(0, 100, 100, 100), content.image, false, false, false, false);
    42.          }
    43.        }
    44.      }
    45.      GUILayout.EndArea();
    46.      Handles.EndGUI();
    47.    }
    48. }
    49.