Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i233 Deep Migration Warning #237

Merged
merged 4 commits into from
May 2, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 38 additions & 17 deletions Scripts/Runtime/Entities/Lifecycle/Migration/MigrationUtil.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Anvil.CSharp.Logging;
using System;
using System.Reflection;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
Expand Down Expand Up @@ -36,14 +37,14 @@ private static void Init()
SharedTypeOffsetInfo.REF.Data = s_TypeOffsetsLookup;
UpdateSharedStatics();
}

private static void CurrentDomain_OnDomainUnload(object sender, EventArgs e)
{
SharedTypeOffsetInfo.REF.Data = default;
SharedEntityOffsetInfo.REF.Data = default;
SharedBlobAssetRefInfo.REF.Data = default;
SharedWeakAssetRefInfo.REF.Data = default;

if (s_TypeOffsetsLookup.IsCreated)
{
s_TypeOffsetsLookup.Dispose();
Expand All @@ -61,7 +62,7 @@ private static void CurrentDomain_OnDomainUnload(object sender, EventArgs e)
s_WeakAssetRefOffsetList.Dispose();
}
}

/// <summary>
/// Registers the Type that may contain Entity references so that it can be used with
/// <see cref="PatchEntityReferences{T}"/> to remap Entity references.
Expand All @@ -72,7 +73,7 @@ public static void RegisterTypeForEntityPatching<T>()
{
RegisterTypeForEntityPatching(typeof(T));
}

/// <inheritdoc cref="RegisterTypeForEntityPatching{T}"/>
/// <exception cref="InvalidOperationException">
/// Occurs when the Type is not a Value type.
Expand All @@ -84,6 +85,8 @@ public static void RegisterTypeForEntityPatching(Type type)
throw new InvalidOperationException($"Type {type.GetReadableName()} must be a value type in order to register for Entity Patching.");
}

ScanForCollections(string.Empty, type);

long typeHash = BurstRuntime.GetHashCode64(type);
//We've already added this type, no need to do so again
if (s_TypeOffsetsLookup.ContainsKey(typeHash))
Expand Down Expand Up @@ -113,23 +116,43 @@ public static void RegisterTypeForEntityPatching(Type type)
new TypeOffsetInfo(
entityOffsetStartIndex,
s_EntityOffsetList.Length));

//The size of the underlying data could have changed such that we re-allocated the memory, so we'll update
//our shared statics
UpdateSharedStatics();
}

private static void ScanForCollections(string parentPathString, Type type)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a safety only operation? I don't think this would come up uniquely in release

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes for now, but I've noted that it won't be in the full implementation of #233 because we need to store the type offset information.

{
//One of these fields might be a collection or contain a collection, best way to tell is to scan it and see if it has a pointer
FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (FieldInfo field in fields)
{
Type fieldType = field.FieldType;
if (fieldType.IsPrimitive)
{
continue;
}
if (fieldType.IsPointer)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know where you want to put this for future reference but we discussed how there may be cases where a pointer is being stored that we do not want to dive into an migrate. Maybe that memory address has already been migrated or something.

We want to figure out how to handle these situations. Maybe require developers to annotate unsafe fields with a [RemapUnsafeEntityDataOnMove] or something. Would need an [ExcludeUnsafeEntityDataOnMove] (names WIP)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, logged in #233

{
Debug.LogWarning($"{parentPathString}/{type.GetReadableName()} has a field named {field.Name} which is a pointer. This is probably a collection. As a result, we cannot automatically patch any entity references inside this collection. It would need to be handled manually until automatic work can handle in from https://github.com/decline-cookies/anvil-unity-dots/issues/233.");
mbaker3 marked this conversation as resolved.
Show resolved Hide resolved
continue;
}
ScanForCollections($"{parentPathString}/{type.GetReadableName()}", fieldType);
}
}

private static unsafe void UpdateSharedStatics()
{
SharedEntityOffsetInfo.REF.Data = new IntPtr(s_EntityOffsetList.GetUnsafePtr());
SharedBlobAssetRefInfo.REF.Data = new IntPtr(s_BlobAssetRefOffsetList.GetUnsafePtr());
SharedWeakAssetRefInfo.REF.Data = new IntPtr(s_WeakAssetRefOffsetList.GetUnsafePtr());
}

//*************************************************************************************************************
// BURST RUNTIME CALLS
//*************************************************************************************************************

/// <summary>
/// Checks if the Entity was remapped by Unity during a world transfer.
/// </summary>
Expand Down Expand Up @@ -194,7 +217,7 @@ public static unsafe void PatchEntityReferences<T>(this ref T instance, ref Nati
*entityPtr = EntityRemapUtility.RemapEntity(ref remapArray, *entityPtr);
}
}

//*************************************************************************************************************
// HELPER TYPES
//*************************************************************************************************************
Expand All @@ -215,35 +238,33 @@ public TypeOffsetInfo(int entityOffsetStartIndex, int entityOffsetEndIndex)
EntityOffsetEndIndex = entityOffsetEndIndex;
}
}

//*************************************************************************************************************
// SHARED STATIC REQUIREMENTS
//*************************************************************************************************************

// ReSharper disable once ConvertToStaticClass
// ReSharper disable once ClassNeverInstantiated.Local
private sealed class MigrationUtilContext
{
private MigrationUtilContext()
{
}
private MigrationUtilContext() { }
}

private sealed class SharedTypeOffsetInfo
{
public static readonly SharedStatic<UnsafeParallelHashMap<long, TypeOffsetInfo>> REF = SharedStatic<UnsafeParallelHashMap<long, TypeOffsetInfo>>.GetOrCreate<MigrationUtilContext, SharedTypeOffsetInfo>();
}

private sealed class SharedEntityOffsetInfo
{
public static readonly SharedStatic<IntPtr> REF = SharedStatic<IntPtr>.GetOrCreate<MigrationUtilContext, SharedEntityOffsetInfo>();
}

private sealed class SharedBlobAssetRefInfo
{
public static readonly SharedStatic<IntPtr> REF = SharedStatic<IntPtr>.GetOrCreate<MigrationUtilContext, SharedBlobAssetRefInfo>();
}

private sealed class SharedWeakAssetRefInfo
{
public static readonly SharedStatic<IntPtr> REF = SharedStatic<IntPtr>.GetOrCreate<MigrationUtilContext, SharedWeakAssetRefInfo>();
Expand Down