Search Unity

When do Assets actually get written to disk after being marked as dirty?

Discussion in 'Immediate Mode GUI (IMGUI)' started by SonicBloomEric, Jun 22, 2015.

  1. SonicBloomEric

    SonicBloomEric

    Joined:
    Sep 11, 2014
    Posts:
    1,088
    When does Unity actually serialize data that's been modified in the Editor (within a custom EditorWindow, for instance)?

    Let's say you have a reference to a CustomAsset with a type that extends ScriptableObject. You modify a serializable property of that CustomAsset in your EditorWindow and call EditorUtility.SetDirty(myCustomAsset).

    When does the actual disk-representation of the Asset get serialized?

    The following example was taken from the EditorUtility.SetDirty documentation but shows an example of the situation I'd like to better understand.

    Code (CSharp):
    1.     // This example shows a custom inspector for an
    2.     // object "MyPlayerEditor", which has two variables, speed and ammo.
    3.     [CustomEditor(typeof(MyPlayer))]
    4.     class MyPlayerEditor : Editor
    5.     {
    6.         void OnInspectorGUI()
    7.         {
    8.             MyPlayer targetPlayer = target as MyPlayer;
    9.             EditorGUILayout.LabelField ("Some help", "Some other text");
    10.  
    11.             GUI.changed = false;
    12.             targetPlayer.speed = EditorGUILayout.Slider ("Speed", targetPlayer.speed, 0, 100);
    13.             if (GUI.changed)
    14.                 EditorUtility.SetDirty(targetPlayer);
    15.         }
    16.     }
    Note: I am not speaking about the SerializedObject/Property system.
     
  2. BMayne

    BMayne

    Joined:
    Aug 4, 2014
    Posts:
    186
    All the freaking time.

    The best way for you to see when it happens is to inherit from ISerializationCallbackReceiver as it really depends on the type of object and the current editor mode. If you put logs in there you should see every time it happens.

    Cheers,
     
    SonicBloomEric and liortal like this.
  3. SonicBloomEric

    SonicBloomEric

    Joined:
    Sep 11, 2014
    Posts:
    1,088
    So what's weird is that I threw a Debug statement into my OnBeforeSerialize callback (already had one implemented) and that helped me locate a situation wherein I was calling SetDirty when I didn't need to. Cool.

    What's weird is that it would be called whenever I called SetDirty as the result of an operation in my code but would also get called again when selecting File→Save Project. But only the first time. So what's going on here?

    ...

    I just attached a Debugger and found something weird. OnBeforeSerialize is called somewhere in the process kicked off by Undo.RecordObject(). It then gets called again somewhere later (perhaps the flag set by SetDirty?), only this time there's no callstack (so likely called from C++ code).

    Essentially I'm seeing the following:
    Code (CSharp):
    1. Undo.RecordObject(objToModify);  //  ---> OnBeforeSerialize called here.
    2. objToModify.myInt++;
    3. EditorUtility.SetDirty(objToModify);
    4. ...
    5. // OnBeforeSerialize called again with no callstack well after the OnGUI function returns, and possibly a number of times!
    I then select Save Project from the File menu a number of times in a row but only the first will actually trigger the OnBeforeSerialize callback...

    Any thoughts on what's going on here?
     
    BMayne likes this.
  4. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    I would guess the the first call happens since the undo system is serializing the object for recording.

    Every call to Dirty will probably create a serialization cache of some sort, without saving the asset.

    Save project will actually save the file to disk, which will call the serialization again.

    If you call multiple Save Project without making a change, then it won't actually serialize anything to the disk since there wasn't a change made.
     
    SonicBloomEric likes this.
  5. SonicBloomEric

    SonicBloomEric

    Joined:
    Sep 11, 2014
    Posts:
    1,088
    That all sounds extremely likely. Fantastic writeup. The one thing I'm not clear on is the purpose of that "Serialization cache" when SetDirty is called but it certainly fits with what I'm seeing. Haven't tried removing the SetDirty to see if that's what's going on yet or not, though...

    Although, perhaps it's a follow-up call to Undo.RecordObject? As I understand it, an Undo record is only made when a change is actually detected... so perhaps this is related to that? To make sure that a change actually did occur and needs to be stored?
     
  6. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    Possibly.

    The "Serialization Cache" is probably just a set of flags as to which objects have changed, so that they may be stored later. And yes, it is possible that the OnBeforeSerialize is called just to be able to compare the cache with the actual object state now. I'd assume that the intention there is that this function is called so that you can do some cleanup on the data before it gets serialized, and Unity would like to give you the opportunity to have the latest and most clean object ready before doing any comparison between the cache and the current instance.
     
    BMayne and SonicBloomEric like this.