Skip to content
This repository has been archived by the owner on Jul 30, 2020. It is now read-only.

Commit

Permalink
Add BaseBehavior.SharedStatePrefabInstantiation.
Browse files Browse the repository at this point in the history
This allows an object to skip serialization cloning when being instantiated.
References are shared between the original and duplicate object, however.
  • Loading branch information
jacobdufault committed Oct 9, 2016
1 parent 2ca9c62 commit 2e738ac
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 3 deletions.
37 changes: 36 additions & 1 deletion Assets/FullInspector2/Core/BaseBehavior.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using FullInspector.Internal;
using UnityEngine;
using UnityObject = UnityEngine.Object;
Expand Down Expand Up @@ -49,6 +50,40 @@ public abstract class BaseBehavior<TSerializer> :
}
}

/// <summary>
/// If true, when an object is cloned via an Instantiate call a fast
/// path will get used, where the cloned object will use the same
/// values as the original object. **This means that object references
/// will be shared between the prefab and the instance, such as
/// dictionaries.**
///
/// **WARNING**: This feature is risky! You may get unexpected results.
///
/// You should only use this feature if you find object cloning is a
/// performance problem. Enabling this will almost certainly cause
/// headache if it is not done carefully.
///
/// This value can only be set to true while in play-mode or in a
/// player.
/// </summary>
public bool SharedStatePrefabInstantiation {
get {
return !string.IsNullOrEmpty(_sharedStateGuid);
}
set {
if (Application.isPlaying && value) {
_sharedStateGuid = Guid.NewGuid().ToString();
} else {
_sharedStateGuid = string.Empty;
}
}
}
string ISerializedObject.SharedStateGuid {
get { return _sharedStateGuid; }
}
[SerializeField, HideInInspector]
private string _sharedStateGuid;

/// <summary>
/// Save the state of component so that it can go through Unity serialization correctly.
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions Assets/FullInspector2/Core/BaseNetworkBehavior.cs
Expand Up @@ -55,6 +55,10 @@ public abstract class BaseNetworkBehavior :
fiISerializedObjectUtility.RestoreState<FullSerializerSerializer>(this);
}

string ISerializedObject.SharedStateGuid {
get { return string.Empty; }
}

/// <summary>
/// Serializing references derived from UnityObject is tricky for a number of reasons, so we
/// just let Unity handle it. The object can be modified in the inspector and be deleted, or
Expand Down
4 changes: 4 additions & 0 deletions Assets/FullInspector2/Core/BaseScriptableObject.cs
Expand Up @@ -56,6 +56,10 @@ public abstract class BaseScriptableObject<TSerializer> :
fiISerializedObjectUtility.RestoreState<TSerializer>(this);
}

string ISerializedObject.SharedStateGuid {
get { return string.Empty; }
}

/// <summary>
/// Serializing references derived from UnityObject is tricky for a number of reasons, so we
/// just let Unity handle it. The object can be modified in the inspector and be deleted, or
Expand Down
5 changes: 5 additions & 0 deletions Assets/FullInspector2/Core/ISerializedObject.cs
Expand Up @@ -22,6 +22,11 @@ public interface ISerializedObject {
/// </summary>
bool IsRestored { get; set; }

/// <summary>
/// Shared-state guid using for faster prefab instantiation.
/// </summary>
string SharedStateGuid { get; }

/// <summary>
/// This list contains a set of object references that were encountered during the
/// serialization process in this object graph. These need to persist through a Unity
Expand Down
70 changes: 68 additions & 2 deletions Assets/FullInspector2/Core/fiISerializedObjectUtility.cs
@@ -1,6 +1,7 @@
using FullSerializer.Internal;
using System;
using System;
using System.Collections.Generic;
using FullSerializer;
using FullSerializer.Internal;
using UnityEngine;
using UnityObject = UnityEngine.Object;

Expand All @@ -9,6 +10,45 @@ namespace FullInspector.Internal {
/// Helper methods for actually serializing objects that extend ISerializedObject. This works via reflection.
/// </summary>
public static class fiISerializedObjectUtility {
private static Dictionary<string, ISerializedObject> _skipSerializationQueue = new Dictionary<string, ISerializedObject>();

private static void SkipCloningValues(ISerializedObject obj) {
lock (_skipSerializationQueue) {
if (_skipSerializationQueue.ContainsKey(obj.SharedStateGuid))
return;

_skipSerializationQueue[obj.SharedStateGuid] = obj;
}
}

private static bool TryToCopyValues(ISerializedObject newInstance) {
if (string.IsNullOrEmpty(newInstance.SharedStateGuid))
return false;

ISerializedObject originalInstance = null;
lock (_skipSerializationQueue) {
if (!_skipSerializationQueue.TryGetValue(newInstance.SharedStateGuid, out originalInstance))
return false;
_skipSerializationQueue.Remove(newInstance.SharedStateGuid);
}

// After a prefab is instantiated Unity will call a full
// serialize/deserialize cycle on the object. We don't need to copy
// values if the object references are the same.
if (ReferenceEquals(newInstance, originalInstance)) {
return true;
}

var inspectedType = InspectedType.Get(originalInstance.GetType());
for (int i = 0; i < originalInstance.SerializedStateKeys.Count; ++i) {
InspectedProperty property =
inspectedType.GetPropertyByName(originalInstance.SerializedStateKeys[i]) ??
inspectedType.GetPropertyByFormerlySerializedName(originalInstance.SerializedStateKeys[i]);
property.Write(newInstance, property.Read(originalInstance));
}
return true;
}

private static bool SaveStateForProperty(ISerializedObject obj, InspectedProperty property, BaseSerializer serializer, ISerializationOperator serializationOperator, out string serializedValue, ref bool success) {
object currentValue = property.Read(obj);

Expand Down Expand Up @@ -49,6 +89,13 @@ public static bool SaveState<TSerializer>(ISerializedObject obj)
var callbacks = obj as ISerializationCallbacks;
if (callbacks != null) callbacks.OnBeforeSerialize();

// Skip serialization entirely if requested.
if (!string.IsNullOrEmpty(obj.SharedStateGuid)) {
SkipCloningValues(obj);
if (callbacks != null) callbacks.OnAfterSerialize();
return true;
}

// fetch the selected serializer
var serializer = fiSingletons.Get<TSerializer>();

Expand Down Expand Up @@ -170,6 +217,25 @@ public static bool RestoreState<TSerializer>(ISerializedObject obj)
try {
if (callbacks != null) callbacks.OnBeforeDeserialize();

// Use fast-path that does not do object serialization if the
// user requested it.
if (!string.IsNullOrEmpty(obj.SharedStateGuid)) {
if (obj.IsRestored)
return true;

if (TryToCopyValues(obj)) {
fiLog.Log(typeof(fiISerializedObjectUtility),
"-- note: Used fast path when deserializing object of type {0}", obj.GetType());
obj.IsRestored = true;
if (callbacks != null) callbacks.OnAfterDeserialize();
return true;
}
else {
Debug.LogError("Shared state deserialization failed for object of type " + obj.GetType().CSharpName(),
obj as UnityObject);
}
}

// ensure references are initialized
if (obj.SerializedStateKeys == null) obj.SerializedStateKeys = new List<string>();
if (obj.SerializedStateValues == null) obj.SerializedStateValues = new List<string>();
Expand Down
4 changes: 4 additions & 0 deletions Assets/FullInspector2/Modules/fiValue/BaseObject.cs
Expand Up @@ -12,6 +12,10 @@ namespace FullInspector {
/// <remarks>Because BaseObject requires Unity's ISerializationCallbackReceiver for serialization support, only Full Serializer
/// is supported for the serialization engine (it is the only serializer to support serialization off of the main thread).</remarks>
public abstract class BaseObject : fiValueProxyEditor, fiIValueProxyAPI, ISerializedObject, ISerializationCallbackReceiver {
string ISerializedObject.SharedStateGuid {
get { return string.Empty; }
}

/// <summary>
/// Serializing references derived from UnityObject is tricky for a number of reasons, so we
/// just let Unity handle it. The object can be modified in the inspector and be deleted, or
Expand Down
1 change: 1 addition & 0 deletions Assets/FullInspector2/changelog.txt
Expand Up @@ -7,6 +7,7 @@ New:
- Experimental multi-object editing. There are major known issues.
- Added fiSettings.EnableGlobalOrdering to make [InspectorOrder] global.
- [InspectorTooltip] can now be added to a class. The tooltip will be shown on the "Name" property that every inspector starts with.
- Added BaseBehavior.SharedStatePrefabInstantiation, which allows a behavior to skip serialization cloning when being cloned as part of an UnityObject.Instantiate call. This can significantly speedup object duplication, but state is now shared between the instances.

Changes:
- Don't auto-serialize prefabs while playing.
Expand Down

0 comments on commit 2e738ac

Please sign in to comment.