diff --git a/Source/Engine/Engine/NativeInterop.Managed.cs b/Source/Engine/Engine/NativeInterop.Managed.cs index 95146a95c..0a2ce5e31 100644 --- a/Source/Engine/Engine/NativeInterop.Managed.cs +++ b/Source/Engine/Engine/NativeInterop.Managed.cs @@ -29,6 +29,8 @@ namespace FlaxEngine.Interop private int _elementSize; private int _length; + [ThreadStatic] private static Dictionary pooledArrayHandles; + public static ManagedArray WrapNewArray(Array arr) => new ManagedArray(arr, arr.GetType()); public static ManagedArray WrapNewArray(Array arr, Type arrayType) => new ManagedArray(arr, arrayType); @@ -37,22 +39,38 @@ namespace FlaxEngine.Interop /// Returns an instance of ManagedArray from shared pool. /// /// The resources must be released by calling FreePooled() instead of Free()-method. - public static ManagedArray WrapPooledArray(Array arr) + public static (ManagedHandle managedHandle, ManagedArray managedArray) WrapPooledArray(Array arr) { ManagedArray managedArray = ManagedArrayPool.Get(); managedArray.WrapArray(arr, arr.GetType()); - return managedArray; + + if (pooledArrayHandles == null) + pooledArrayHandles = new(); + if (!pooledArrayHandles.TryGetValue(managedArray, out ManagedHandle handle)) + { + handle = ManagedHandle.Alloc(managedArray); + pooledArrayHandles.Add(managedArray, handle); + } + return (handle, managedArray); } /// /// Returns an instance of ManagedArray from shared pool. /// /// The resources must be released by calling FreePooled() instead of Free()-method. - public static ManagedArray WrapPooledArray(Array arr, Type arrayType) + public static ManagedHandle WrapPooledArray(Array arr, Type arrayType) { ManagedArray managedArray = ManagedArrayPool.Get(arr.Length * NativeInterop.GetTypeSize(arr.GetType().GetElementType())); managedArray.WrapArray(arr, arrayType); - return managedArray; + + if (pooledArrayHandles == null) + pooledArrayHandles = new(); + if (!pooledArrayHandles.TryGetValue(managedArray, out ManagedHandle handle)) + { + handle = ManagedHandle.Alloc(managedArray); + pooledArrayHandles.Add(managedArray, handle); + } + return handle; } internal static ManagedArray AllocateNewArray(int length, Type arrayType, Type elementType) @@ -64,12 +82,20 @@ namespace FlaxEngine.Interop /// /// Returns an instance of ManagedArray from shared pool. /// - /// The resources must be released by calling FreePooled() instead of Free()-method. - public static ManagedArray AllocatePooledArray(int length) where T : unmanaged + /// The resources must be released by calling FreePooled() instead of Free()-method. Do not release the returned ManagedHandle. + public static (ManagedHandle managedHandle, ManagedArray managedArray) AllocatePooledArray(int length) where T : unmanaged { ManagedArray managedArray = ManagedArrayPool.Get(length * Unsafe.SizeOf()); managedArray.Allocate(length); - return managedArray; + + if (pooledArrayHandles == null) + pooledArrayHandles = new(); + if (!pooledArrayHandles.TryGetValue(managedArray, out ManagedHandle handle)) + { + handle = ManagedHandle.Alloc(managedArray); + pooledArrayHandles.Add(managedArray, handle); + } + return (handle, managedArray); } public ManagedArray(Array arr, Type elementType) => WrapArray(arr, elementType); diff --git a/Source/Engine/Engine/NativeInterop.Marshallers.cs b/Source/Engine/Engine/NativeInterop.Marshallers.cs index 2e3bc9183..0f7d238bc 100644 --- a/Source/Engine/Engine/NativeInterop.Marshallers.cs +++ b/Source/Engine/Engine/NativeInterop.Marshallers.cs @@ -200,14 +200,13 @@ namespace FlaxEngine.Interop public void FromManaged(Array managed) { if (managed != null) - managedArray = ManagedArray.WrapPooledArray(managed); + (handle, managedArray) = ManagedArray.WrapPooledArray(managed); } public IntPtr ToUnmanaged() { if (managedArray == null) return IntPtr.Zero; - handle = ManagedHandle.Alloc(managedArray, GCHandleType.Weak); return ManagedHandle.ToIntPtr(handle); } @@ -216,7 +215,6 @@ namespace FlaxEngine.Interop if (managedArray == null) return; managedArray.FreePooled(); - //handle.Free(); // No need to free weak handles } } @@ -335,7 +333,6 @@ namespace FlaxEngine.Interop #endif [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))] @@ -388,38 +385,28 @@ namespace FlaxEngine.Interop #if FLAX_EDITOR [HideInEditor] #endif - public static class ManagedToNative + public ref struct ManagedToNative { - public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[] managed, out int numElements) + T[] sourceArray; + ManagedArray managedArray; + ManagedHandle managedHandle; + + public void FromManaged(T[] managed) { if (managed is null) - { - numElements = 0; - return null; - } - numElements = managed.Length; - ManagedArray managedArray = ManagedArray.AllocatePooledArray(managed.Length); - return (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedArray, GCHandleType.Normal); - } - - public static ReadOnlySpan GetManagedValuesSource(T[] managed) => managed; - - public static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements) - { - if (unmanaged == null) - return Span.Empty; - ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); - return managedArray.ToSpan(); - } - - public static void Free(TUnmanagedElement* unmanaged) - { - if (unmanaged == null) return; - ManagedHandle handle = ManagedHandle.FromIntPtr(new IntPtr(unmanaged)); - (Unsafe.As(handle.Target)).FreePooled(); - //handle.Free(); // No need to free weak handles + + sourceArray = managed; + (managedHandle, managedArray) = ManagedArray.AllocatePooledArray(managed.Length); } + + public ReadOnlySpan GetManagedValuesSource() => sourceArray; + + public Span GetUnmanagedValuesDestination() => managedArray.ToSpan(); + + public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedHandle); + + public void Free() => managedArray.FreePooled(); } #if FLAX_EDITOR @@ -427,26 +414,25 @@ namespace FlaxEngine.Interop #endif public struct Bidirectional { - T[] managedArray; - ManagedArray unmanagedArray; + T[] sourceArray; + ManagedArray managedArray; ManagedHandle handle; public void FromManaged(T[] managed) { if (managed == null) return; - managedArray = managed; - unmanagedArray = ManagedArray.AllocatePooledArray(managed.Length); - handle = ManagedHandle.Alloc(unmanagedArray); + sourceArray = managed; + (handle, managedArray) = ManagedArray.AllocatePooledArray(managed.Length); } - public ReadOnlySpan GetManagedValuesSource() => managedArray; + public ReadOnlySpan GetManagedValuesSource() => sourceArray; public Span GetUnmanagedValuesDestination() { - if (unmanagedArray == null) + if (managedArray == null) return Span.Empty; - return unmanagedArray.ToSpan(); + return managedArray.ToSpan(); } public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)ManagedHandle.ToIntPtr(handle); @@ -454,26 +440,22 @@ namespace FlaxEngine.Interop public void FromUnmanaged(TUnmanagedElement* unmanaged) { ManagedArray arr = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); - if (managedArray == null || managedArray.Length != arr.Length) - managedArray = new T[arr.Length]; + if (sourceArray == null || sourceArray.Length != arr.Length) + sourceArray = new T[arr.Length]; } public ReadOnlySpan GetUnmanagedValuesSource(int numElements) { - if (unmanagedArray == null) + if (managedArray == null) return ReadOnlySpan.Empty; - return unmanagedArray.ToSpan(); + return managedArray.ToSpan(); } - public Span GetManagedValuesDestination(int numElements) => managedArray; + public Span GetManagedValuesDestination(int numElements) => sourceArray; - public T[] ToManaged() => managedArray; + public T[] ToManaged() => sourceArray; - public void Free() - { - unmanagedArray.FreePooled(); - handle.Free(); - } + public void Free() => managedArray.FreePooled(); } public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[] managed, out int numElements) @@ -484,9 +466,8 @@ namespace FlaxEngine.Interop return null; } numElements = managed.Length; - ManagedArray managedArray = ManagedArray.AllocatePooledArray(managed.Length); - IntPtr handle = ManagedHandle.ToIntPtr(managedArray); - return (TUnmanagedElement*)handle; + (ManagedHandle managedArrayHandle, _) = ManagedArray.AllocatePooledArray(managed.Length); + return (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedArrayHandle); } public static ReadOnlySpan GetManagedValuesSource(T[] managed) => managed; @@ -517,7 +498,6 @@ namespace FlaxEngine.Interop return; ManagedHandle handle = ManagedHandle.FromIntPtr(new IntPtr(unmanaged)); Unsafe.As(handle.Target).FreePooled(); - handle.Free(); } }