diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs
index 4614a78c1..34daf9fcf 100644
--- a/Source/Engine/Engine/NativeInterop.cs
+++ b/Source/Engine/Engine/NativeInterop.cs
@@ -201,8 +201,7 @@ namespace FlaxEngine
///
/// Wrapper for managed arrays that needs to be pinned.
///
- [StructLayout(LayoutKind.Sequential)]
- internal struct ManagedArray
+ internal class ManagedArray
{
internal Array array = null;
internal GCHandle handle;
@@ -215,6 +214,12 @@ namespace FlaxEngine
array = arr;
}
+ ~ManagedArray()
+ {
+ if (array != null)
+ Release();
+ }
+
internal void Release()
{
handle.Free();
@@ -239,11 +244,15 @@ namespace FlaxEngine
internal static class ManagedString
{
+ internal static GCHandle EmptyStringHandle = GCHandle.Alloc(string.Empty);
+
[System.Diagnostics.DebuggerStepThrough]
internal static unsafe IntPtr ToNative(string str)
{
if (str == null)
return IntPtr.Zero;
+ else if (str == string.Empty)
+ return GCHandle.ToIntPtr(EmptyStringHandle);
#if false
// HACK: Pin the string and pass the address to it including the length, no marshalling required
GCHandle handle = GCHandle.Alloc(str, GCHandleType.Pinned);
@@ -258,7 +267,7 @@ namespace FlaxEngine
// We assume the string content is copied in native side before GC frees it.
IntPtr ptr = (Unsafe.Read(Unsafe.AsPointer(ref str)) + sizeof(int) * 2);
#endif
- IntPtr ptr = GCHandle.ToIntPtr(GCHandle.Alloc(str));
+ IntPtr ptr = GCHandle.ToIntPtr(GCHandle.Alloc(str, GCHandleType.Weak));
return ptr;
}
@@ -277,7 +286,11 @@ namespace FlaxEngine
if (ptr == IntPtr.Zero)
return;
- GCHandle.FromIntPtr(ptr).Free();
+ GCHandle handle = GCHandle.FromIntPtr(ptr);
+ if (handle == EmptyStringHandle)
+ return;
+
+ handle.Free();
}
}
@@ -450,11 +463,160 @@ namespace FlaxEngine
}
}
- [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.Default, typeof(ArrayMarshaller<,>))]
+ [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedIn, typeof(ArrayMarshaller<,>.ManagedToNative))]
+ [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedOut, typeof(ArrayMarshaller<,>.ManagedToNative))]
+ [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementIn, typeof(ArrayMarshaller<,>.ManagedToNative))]
+ [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedOut, typeof(ArrayMarshaller<,>.NativeToManaged))]
+ [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedIn, typeof(ArrayMarshaller<,>.NativeToManaged))]
+ [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementOut, typeof(ArrayMarshaller<,>.NativeToManaged))]
+ [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedRef, typeof(ArrayMarshaller<,>.Bidirectional))]
+ [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedRef, typeof(ArrayMarshaller<,>.Bidirectional))]
+ [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementRef, typeof(ArrayMarshaller<,>))]
[ContiguousCollectionMarshaller]
internal static unsafe class ArrayMarshaller
where TUnmanagedElement : unmanaged
{
+ public static class NativeToManaged
+ {
+ internal static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements)
+ {
+ if (unmanaged is null)
+ return null;
+
+ ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target;
+ return new T[numElements];
+ }
+
+ internal static Span GetManagedValuesDestination(T[]? managed)
+ => managed;
+
+ internal static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanagedValue, int numElements)
+ {
+ if (unmanagedValue == null)
+ return ReadOnlySpan.Empty;
+
+ ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanagedValue)).Target;
+ return new ReadOnlySpan(array.array as TUnmanagedElement[]);
+ }
+
+ internal static void Free(TUnmanagedElement* unmanaged)
+ {
+ if (unmanaged == null)
+ return;
+
+ GCHandle handle = GCHandle.FromIntPtr(new IntPtr(unmanaged));
+ ((ManagedArray)handle.Target).Release();
+ handle.Free();
+ }
+
+ internal static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
+ {
+ if (unmanaged == null)
+ return Span.Empty;
+
+ ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target;
+ return new Span(array.array as TUnmanagedElement[]);
+ }
+ }
+ public static class ManagedToNative
+ {
+ internal static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements)
+ {
+ if (managed is null)
+ {
+ numElements = 0;
+ return null;
+ }
+
+ numElements = managed.Length;
+
+ ManagedArray array = ManagedArray.Get(new TUnmanagedElement[numElements]);
+ var ptr = GCHandle.ToIntPtr(GCHandle.Alloc(array));
+
+ return (TUnmanagedElement*)ptr;
+ }
+
+ internal static ReadOnlySpan GetManagedValuesSource(T[]? managed)
+ => managed;
+
+ internal static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
+ {
+ if (unmanaged == null)
+ return Span.Empty;
+
+ ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target;
+ return new Span(array.array as TUnmanagedElement[]);
+ }
+
+ internal static void Free(TUnmanagedElement* unmanaged)
+ {
+ if (unmanaged == null)
+ return;
+
+ GCHandle handle = GCHandle.FromIntPtr(new IntPtr(unmanaged));
+ ((ManagedArray)handle.Target).Release();
+ handle.Free();
+ }
+ }
+ public struct Bidirectional
+ {
+ T[] managedArray;
+ ManagedArray unmanagedArray;
+ GCHandle handle;
+
+ public void FromManaged(T[]? managed)
+ {
+ if (managed == null)
+ return;
+
+ managedArray = managed;
+ unmanagedArray = ManagedArray.Get(new TUnmanagedElement[managed.Length]);
+ handle = GCHandle.Alloc(unmanagedArray);
+ }
+
+ public ReadOnlySpan GetManagedValuesSource()
+ => managedArray;
+
+ public Span GetUnmanagedValuesDestination()
+ {
+ if (unmanagedArray == null)
+ return Span.Empty;
+
+ return new Span(unmanagedArray.array as TUnmanagedElement[]);
+ }
+
+ public TUnmanagedElement* ToUnmanaged()
+ {
+ return (TUnmanagedElement*)GCHandle.ToIntPtr(handle);
+ }
+
+ public void FromUnmanaged(TUnmanagedElement* value)
+ {
+ ManagedArray arr = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(value)).Target;
+ managedArray = new T[arr.Length];
+ }
+
+ public ReadOnlySpan GetUnmanagedValuesSource(int numElements)
+ {
+ if (unmanagedArray == null)
+ return ReadOnlySpan.Empty;
+
+ return new ReadOnlySpan(unmanagedArray.array as TUnmanagedElement[]);
+ }
+
+ public Span GetManagedValuesDestination(int numElements)
+ => managedArray;
+
+ public T[] ToManaged()
+ => managedArray;
+
+ public void Free()
+ {
+ unmanagedArray.Release();
+ handle.Free();
+ }
+ }
+
internal static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements)
{
if (managed is null)
@@ -510,7 +672,7 @@ namespace FlaxEngine
return;
GCHandle handle = GCHandle.FromIntPtr(new IntPtr(unmanaged));
- Unsafe.Unbox(handle.Target).Release();
+ ((ManagedArray)handle.Target).Release();
handle.Free();
}
@@ -584,7 +746,7 @@ namespace FlaxEngine
internal static IntPtr ToNative(string managed) => ManagedString.ToNative(managed);
}
-#endregion
+ #endregion
///
/// Provides a Mono-like API for native code to access managed runtime.
@@ -610,6 +772,7 @@ namespace FlaxEngine
private static Dictionary typeHandleCacheCollectible = new Dictionary();
private static List fieldHandleCache = new();
private static List fieldHandleCacheCollectible = new();
+ private static Dictionary