I have been searching around for a solution for this problem, but there does not appear to be much information about this online so here it goes:
I have a monobehaviour that stores data in a NativeArray such that it can be used in the job system. This works fine if the script only runs in play mode. However, now I want to enable the script in edit mode using the ExecuteAlways or ExecuteInEditMode attributes. See the following example class:
[ExecuteAlways]
public class Tets : MonoBehaviour, ISerializationCallbackReceiver
{
//This data will be used in-game but must also be present in edit mode after deserialization.
private NativeArray<byte> data;
//This is only used for serialization in editor
[SerializeField]
private byte[] serializedData;
public void OnAfterDeserialize()
{
serializedData = data.ToArray();
//Cannot do this here because this function will be called twice and will override serializedData with an empty array!!
//data.Dispose();
}
public void OnBeforeSerialize()
{
data = new NativeArray<byte>(serializedData, Allocator.Persistent);
}
}
Now comes the problem: the NativeArray is not serialized property by default in Unity. I tried to remedy this by using the ISerializationCallbackReceiver and copying over the NativeArray contents to a managed array and move is back during deserialization.
Now I need to Dispose the NativeArray on the right time, but this doesn’t seem to be as straight forward, because:
According to the diagram this thread , OnBeforeSerialize is called twice when domain reloading is not disabled. So I cannot dispose the NativeArray here since serialization will break for the second time OnBeforeSerialize is called. The obvious solution would be to dispose the NativeArray during OnAfterDeserialization instead. HOWEVER, doing this leads to the ‘A native collection has not been disposed’ error when entering playmode, because apparently I must have disposed the NativeArray before the OnBeforeSerialize callback is going to be called (and I assume that is, before AppDomain.Unload is called).
So my question to you, and ESPECIALLY to the Unity staff is, how on earth am I supposed to handle this scenario and properly serialize/deserialize NativeArrays such that I can make use of the ExecuteAlways/ExecuteInEditMode attributes?? @alexeyzakharov , @Joachim_Ante_1
You can create skip the ISerializationCallbackReceiver and just use OnEnable/OnDisable! I’m doing the same thing, heres @Joachim_Ante_1 suggesting it here:
Thank you, this worked to get the data to stay in edit mode. However I am still wondering how this work when serializing/saving the scene or prefab. When do I need to store my data to the managed array if I want to save the scene?
Oh right, I totally misunderstood your needs here. I would’ve approached the problem the same way as you. Except maybe try this?
public void OnAfterDeserialize()
{
if (data.IsCreated)
{
serializedData = data.ToArray();
data.Dispose();
}
}
public void OnBeforeSerialize()
{
if (!data.IsCreated)
data = new NativeArray<byte>(serializedData, Allocator.Persistent);
}
I think you have your logic backwards? After deserialization you want to move the just-deserialized data into your NativeArray, not the other way around. And right before you serialize data you want to store the data into the array that is about to be serialized.
I think the better approach is probably keep your native containers in a system let it manage their lifecycle. Then make calls into that system to do something with the container.
You can call DefaultWorldInitialization.DefaultLazyEditModeInitialize() in edit mode to ensure that the default world is created before accessing systems.
Hi!
Alternatively I would try to dispose NativeArray in a finalizer. Something like:
~Tets()
{
data.Dispose();
}
I would treat NativeArray as an unmanaged resource and lifecycle similar to IDisposable pattern.
The dispose check happens after domain is unloaded and finalizer is one of the last pieces that get called during domain reload.
I would expect so - disposing NativeArray in a finalizer would prolong its lifecycle management to a lifecycle of a managed shell of UnityEngine.Object. Then if domain reload happens finalizer would be a last point where NativeArray can be disposed for a ScriptableObject. It is not ideal scenario I would say, but can’t think of other options
public void OnAfterDeserialize()
{
if (!data.IsCreated)
data = new NativeArray<byte>(serializedData, Allocator.Persistent);
}
public void OnDisable()
{
if (data.IsCreated)
data.Dispose();
}
void ~Test()
{
if (data.IsCreated)
data.Dispose();
}