Skip to content

Commit

Permalink
[Assets] Added a new KeepReferences flag to AssetCloner
Browse files Browse the repository at this point in the history
  • Loading branch information
xen2 committed May 23, 2019
1 parent f1183af commit 2f396b1
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 8 deletions.
17 changes: 17 additions & 0 deletions sources/assets/Xenko.Core.Assets.Tests/TestAssetCloner.cs
Expand Up @@ -11,6 +11,15 @@

namespace Xenko.Core.Assets.Tests
{
[DataContract, ReferenceSerializer, DataSerializerGlobal(typeof(ReferenceSerializer<TestContent>), Profile = "Content")]
public class TestContent { }

[DataContract("TestAssetClonerContent")]
public class TestAssetClonerContent
{
public TestContent Content;
}

[DataContract("TestAssetClonerObject")]
public class TestAssetClonerObject
{
Expand Down Expand Up @@ -38,6 +47,14 @@ public class TestObjectReference

public class TestAssetCloner
{
[Fact]
public void TestAssetClonerContent()
{
var obj1 = new TestAssetClonerContent { Content = new TestContent() };
var obj2 = AssetCloner.Clone(obj1, AssetClonerFlags.KeepReferences);
Assert.Equal(obj1.Content, obj2.Content);
}

[Fact]
public void TestHash()
{
Expand Down
28 changes: 20 additions & 8 deletions sources/assets/Xenko.Core.Assets/AssetCloner.cs
Expand Up @@ -31,6 +31,7 @@ public class AssetCloner
private Dictionary<Guid, Guid> cloningIdRemapping;
public static SerializerSelector ClonerSelector { get; internal set; }
public static PropertyKey<List<object>> InvariantObjectListProperty = new PropertyKey<List<object>>("InvariantObjectList", typeof(AssetCloner));
private List<object> cloneReferences;

static AssetCloner()
{
Expand Down Expand Up @@ -62,11 +63,8 @@ private AssetCloner(object value, AssetClonerFlags flags, IEnumerable<IIdentifia
var stream = new MemoryStream();
var writer = new BinarySerializationWriter(stream);
writer.Context.SerializerSelector = ClonerSelector;
var refFlag = (flags & AssetClonerFlags.ReferenceAsNull) != 0
? ContentSerializerContext.AttachedReferenceSerialization.AsNull
: ContentSerializerContext.AttachedReferenceSerialization.AsSerializableVersion;
writer.Context.Set(InvariantObjectListProperty, invariantObjects);
writer.Context.Set(ContentSerializerContext.SerializeAttachedReferenceProperty, refFlag);
writer.Context.Set(ContentSerializerContext.SerializeAttachedReferenceProperty, GenerateContentSerializerFlags(flags));
if (externalIdentifiables != null)
{
this.externalIdentifiables = new Dictionary<Guid, IIdentifiable>();
Expand All @@ -80,6 +78,9 @@ private AssetCloner(object value, AssetClonerFlags flags, IEnumerable<IIdentifia
writer.SerializeExtended(value, ArchiveMode.Serialize);
writer.Flush();

if ((flags & AssetClonerFlags.KeepReferences) != 0)
cloneReferences = writer.Context.Get(ReferenceSerializer.CloneReferences);

// Retrieve back all object references that were discovered while serializing
// They will be used layer by OnObjectDeserialized when cloning ShadowObject datas
var objectRefs = writer.Context.Get(MemberSerializer.ObjectSerializeReferences);
Expand Down Expand Up @@ -113,18 +114,17 @@ private object Clone([NotNull] out Dictionary<Guid, Guid> idRemapping)
stream.Position = 0;
var reader = new BinarySerializationReader(stream);
reader.Context.SerializerSelector = ClonerSelector;
var refFlag = (flags & AssetClonerFlags.ReferenceAsNull) != 0
? ContentSerializerContext.AttachedReferenceSerialization.AsNull
: ContentSerializerContext.AttachedReferenceSerialization.AsSerializableVersion;
reader.Context.Set(InvariantObjectListProperty, invariantObjects);
reader.Context.Set(ContentSerializerContext.SerializeAttachedReferenceProperty, refFlag);
reader.Context.Set(ContentSerializerContext.SerializeAttachedReferenceProperty, GenerateContentSerializerFlags(flags));
if (externalIdentifiables != null)
{
if ((flags & AssetClonerFlags.ClearExternalReferences) != 0)
externalIdentifiables.Clear();

reader.Context.Set(MemberSerializer.ExternalIdentifiables, externalIdentifiables);
}
if ((flags & AssetClonerFlags.KeepReferences) != 0)
reader.Context.Set(ReferenceSerializer.CloneReferences, cloneReferences);
reader.Context.Set(MemberSerializer.ObjectDeserializeCallback, OnObjectDeserialized);
object newObject = null;
reader.SerializeExtended(ref newObject, ArchiveMode.Deserialize);
Expand Down Expand Up @@ -317,6 +317,18 @@ internal static ObjectId ComputeHash(object asset, AssetClonerFlags flags = Asse
return result;
}

private static ContentSerializerContext.AttachedReferenceSerialization GenerateContentSerializerFlags(AssetClonerFlags flags)
{
ContentSerializerContext.AttachedReferenceSerialization refFlag;
if ((flags & AssetClonerFlags.ReferenceAsNull) != 0)
refFlag = ContentSerializerContext.AttachedReferenceSerialization.AsNull;
else if ((flags & AssetClonerFlags.KeepReferences) != 0)
refFlag = ContentSerializerContext.AttachedReferenceSerialization.Clone;
else
refFlag = ContentSerializerContext.AttachedReferenceSerialization.AsSerializableVersion;
return refFlag;
}

private class UnloadableCloneSerializer<T> : DataSerializer<T> where T : class, IUnloadable
{
private DataSerializer parentSerializer;
Expand Down
5 changes: 5 additions & 0 deletions sources/assets/Xenko.Core.Assets/AssetClonerFlags.cs
Expand Up @@ -40,5 +40,10 @@ public enum AssetClonerFlags
/// Clears any external references in the cloned object
/// </summary>
ClearExternalReferences = 16,

/// <summary>
/// Attached references will be kept as is
/// </summary>
KeepReferences = 32,
}
}
Expand Up @@ -22,6 +22,7 @@ public enum AttachedReferenceSerialization
Unset,
AsSerializableVersion,
AsNull,
Clone,
}

public static PropertyKey<ContentSerializerContext> ContentSerializerContextProperty = new PropertyKey<ContentSerializerContext>("ContentSerializerContext", typeof(ContentSerializerContext));
Expand Down
Expand Up @@ -2,11 +2,17 @@
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

using System;
using System.Collections.Generic;
using Xenko.Core.Assets;
using Xenko.Core.Reflection;

namespace Xenko.Core.Serialization.Contents
{
public static class ReferenceSerializer
{
public static readonly PropertyKey<List<object>> CloneReferences = new PropertyKey<List<object>>("CloneReferences", typeof(SerializerExtensions), DefaultValueMetadata.Delegate(delegate { return new List<object>(); }));
}

/// <summary>
/// Serialize object with its underlying Id and Location, and use <see cref="ContentManager"/> to generate a separate chunk.
/// </summary>
Expand Down Expand Up @@ -95,6 +101,19 @@ public override void Serialize(ref T obj, ArchiveMode mode, SerializationStream
obj = (T)AttachedReferenceManager.CreateProxyObject(type, id, url);
}
}
else if (referenceSerialization == ContentSerializerContext.AttachedReferenceSerialization.Clone)
{
var cloneReferences = stream.Context.Get(ReferenceSerializer.CloneReferences);
if (mode == ArchiveMode.Serialize)
{
stream.Write(cloneReferences.Count);
cloneReferences.Add(obj);
}
else
{
obj = (T)cloneReferences[stream.ReadInt32()];
}
}
else
{
// This case will happen when serializing build engine command hashes: we still want Location to still be written
Expand Down

0 comments on commit 2f396b1

Please sign in to comment.