diff --git a/Source/Editor/Content/Import/TextureImportEntry.cs b/Source/Editor/Content/Import/TextureImportEntry.cs index 4288a56c1..29bb62c96 100644 --- a/Source/Editor/Content/Import/TextureImportEntry.cs +++ b/Source/Editor/Content/Import/TextureImportEntry.cs @@ -368,7 +368,7 @@ namespace FlaxEditor.Content.Import MaxSize = managed.MaxSize, TextureGroup = managed.TextureGroup, Size = managed.Size, - SpriteAreas = managed.SpriteAreas != IntPtr.Zero ? ((ManagedArray)ManagedHandle.FromIntPtr(managed.SpriteAreas).Target).GetArray() : null, + SpriteAreas = managed.SpriteAreas != IntPtr.Zero ? ((ManagedArray)ManagedHandle.FromIntPtr(managed.SpriteAreas).Target).ToArray() : null, SpriteNames = managed.SpriteNames != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray((ManagedArray)ManagedHandle.FromIntPtr(managed.SpriteNames).Target) : null, }; } diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index afe68f222..ad04a623d 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -166,8 +166,8 @@ namespace FlaxEngine public unsafe class ManagedArray { private ManagedHandle pinnedArrayHandle; - private ManagedHandle elementTypeHandle; private IntPtr unmanagedData; + private Type elementType; private int elementSize; private int length; @@ -186,9 +186,11 @@ namespace FlaxEngine return managedArray; } - internal static ManagedArray AllocateNewArray(int length, int elementSize, Type elementType) => new ManagedArray((IntPtr)NativeInterop.NativeAlloc(length, elementSize), length, elementSize, elementType); + internal static ManagedArray AllocateNewArray(int length, Type elementType) + => new ManagedArray((IntPtr)NativeInterop.NativeAlloc(length, Marshal.SizeOf(elementType)), length, elementType); - internal static ManagedArray AllocateNewArray(IntPtr ptr, int length, int elementSize, Type elementType) => new ManagedArray(ptr, length, elementSize, elementType); + internal static ManagedArray AllocateNewArray(IntPtr ptr, int length, Type elementType) + => new ManagedArray(ptr, length, elementType); /// /// Returns an instance of ManagedArray from shared pool. @@ -212,7 +214,7 @@ namespace FlaxEngine public static ManagedArray AllocatePooledArray(int length) where T : unmanaged { ManagedArray managedArray = ManagedArrayPool.Get(); - managedArray.Allocate((IntPtr)NativeInterop.NativeAlloc(length, Unsafe.SizeOf()), length, Unsafe.SizeOf(), typeof(T)); + managedArray.Allocate((IntPtr)NativeInterop.NativeAlloc(length, Unsafe.SizeOf()), length, typeof(T)); return managedArray; } @@ -220,33 +222,32 @@ namespace FlaxEngine internal void WrapArray(Array arr) { - var elementType = arr.GetType().GetElementType(); pinnedArrayHandle = ManagedHandle.Alloc(arr, GCHandleType.Pinned); - elementTypeHandle = NativeInterop.GetTypeGCHandle(elementType); unmanagedData = Marshal.UnsafeAddrOfPinnedArrayElement(arr, 0); length = arr.Length; + elementType = arr.GetType().GetElementType(); elementSize = Marshal.SizeOf(elementType); } internal void Allocate(T* ptr, int length) where T : unmanaged { unmanagedData = new IntPtr(ptr); - elementTypeHandle = NativeInterop.GetTypeGCHandle(typeof(T)); this.length = length; + elementType = typeof(T); elementSize = Unsafe.SizeOf(); } - internal void Allocate(IntPtr ptr, int length, int elementSize, Type elementType) + internal void Allocate(IntPtr ptr, int length, Type elementType) { unmanagedData = ptr; - elementTypeHandle = NativeInterop.GetTypeGCHandle(elementType); this.length = length; - this.elementSize = elementSize; + this.elementType = elementType; + elementSize = Marshal.SizeOf(elementType); } private ManagedArray() { } - private ManagedArray(IntPtr ptr, int length, int elementSize, Type elementType) => Allocate(ptr, length, elementSize, elementType); + private ManagedArray(IntPtr ptr, int length, Type elementType) => Allocate(ptr, length, elementType); ~ManagedArray() { @@ -275,24 +276,45 @@ namespace FlaxEngine ManagedArrayPool.Put(this); } - internal IntPtr GetPointer => unmanagedData; + internal IntPtr Pointer => unmanagedData; internal int Length => length; internal int ElementSize => elementSize; - internal Type ElementType => Unsafe.As(elementTypeHandle.Target); + public Span ToSpan() where T : struct => new Span(unmanagedData.ToPointer(), length); - public Span GetSpan() where T : struct => new Span(unmanagedData.ToPointer(), length); + public T[] ToArray() where T : struct => new Span(unmanagedData.ToPointer(), length).ToArray(); - public T[] GetArray() where T : struct => new Span(unmanagedData.ToPointer(), length).ToArray(); + public Array ToArray() => ArrayCast.ToArray(new Span(unmanagedData.ToPointer(), length * elementSize), elementType); - public Array GeSystemArray() + /// + /// Creates an Array of the specified type from span of bytes. + /// + private static class ArrayCast { - Type elementType = Unsafe.As(elementTypeHandle.Target); - Type arrayType = elementType.MakeArrayType(); - IntPtr thisPtr = ManagedHandle.ToIntPtr(ManagedHandle.Alloc(this)); - return (Array)NativeInterop.MarshalToManaged(thisPtr, arrayType); + delegate Array GetDelegate(Span span); + [ThreadStatic] private static Dictionary delegates; + + internal static Array ToArray(Span span, Type type) + { + if (delegates == null) + delegates = new(); + if (!delegates.TryGetValue(type, out var deleg)) + { + deleg = typeof(ArrayCastInternal<>).MakeGenericType(type).GetMethod(nameof(ArrayCastInternal.ToArray), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate(); + delegates.Add(type, deleg); + } + return deleg(span); + } + + private static class ArrayCastInternal where T : struct + { + internal static Array ToArray(Span span) + { + return MemoryMarshal.Cast(span).ToArray(); + } + } } /// @@ -703,8 +725,7 @@ namespace FlaxEngine [CustomMarshaller(typeof(Array), MarshalMode.UnmanagedToManagedOut, typeof(SystemArrayMarshaller.ManagedToNative))] [CustomMarshaller(typeof(Array), MarshalMode.ManagedToUnmanagedOut, typeof(SystemArrayMarshaller.NativeToManaged))] [CustomMarshaller(typeof(Array), MarshalMode.UnmanagedToManagedIn, typeof(SystemArrayMarshaller.NativeToManaged))] - [CustomMarshaller(typeof(Array), MarshalMode.ElementOut, typeof(SystemArrayMarshaller.NativeToManaged))] - public static class SystemArrayMarshaller + public static unsafe class SystemArrayMarshaller { public struct ManagedToNative { @@ -727,17 +748,42 @@ namespace FlaxEngine public void Free() { - if (managedArray != null) - { - managedArray.FreePooled(); - handle.Free(); - } + if (managedArray == null) + return; + managedArray.FreePooled(); + handle.Free(); } } - public static class NativeToManaged + + public struct NativeToManaged { - public static Array ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As(ManagedHandle.FromIntPtr(unmanaged).Target).GeSystemArray() : null; + ManagedHandle handle; + ManagedArray managedArray; + + public void FromUnmanaged(IntPtr unmanaged) + { + if (unmanaged == IntPtr.Zero) + return; + handle = ManagedHandle.FromIntPtr(unmanaged); + } + + public Array ToManaged() + { + if (!handle.IsAllocated) + return null; + managedArray = Unsafe.As(handle.Target); + return managedArray.ToArray(); + } + + public void Free() + { + if (!handle.IsAllocated) + return; + managedArray.Free(); + handle.Free(); + } } + } [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ManagedToUnmanagedIn, typeof(DictionaryMarshaller<,>.ManagedToNative))] @@ -827,7 +873,7 @@ namespace FlaxEngine if (unmanaged == null) return ReadOnlySpan.Empty; ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); - return managedArray.GetSpan(); + return managedArray.ToSpan(); } public static void Free(TUnmanagedElement* unmanaged) @@ -844,7 +890,7 @@ namespace FlaxEngine if (unmanaged == null) return Span.Empty; ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); - return managedArray.GetSpan(); + return managedArray.ToSpan(); } } @@ -870,7 +916,7 @@ namespace FlaxEngine if (unmanaged == null) return Span.Empty; ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); - return managedArray.GetSpan(); + return managedArray.ToSpan(); } public static void Free(TUnmanagedElement* unmanaged) @@ -904,7 +950,7 @@ namespace FlaxEngine { if (unmanagedArray == null) return Span.Empty; - return unmanagedArray.GetSpan(); + return unmanagedArray.ToSpan(); } public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)ManagedHandle.ToIntPtr(handle); @@ -920,7 +966,7 @@ namespace FlaxEngine { if (unmanagedArray == null) return ReadOnlySpan.Empty; - return unmanagedArray.GetSpan(); + return unmanagedArray.ToSpan(); } public Span GetManagedValuesDestination(int numElements) => managedArray; @@ -954,7 +1000,7 @@ namespace FlaxEngine if (unmanaged == null) return Span.Empty; ManagedArray unmanagedArray = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); - return unmanagedArray.GetSpan(); + return unmanagedArray.ToSpan(); } public static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) => unmanaged is null ? null : new T[numElements]; @@ -966,7 +1012,7 @@ namespace FlaxEngine if (unmanaged == null) return ReadOnlySpan.Empty; ManagedArray array = Unsafe.As(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target); - return array.GetSpan(); + return array.ToSpan(); } public static void Free(TUnmanagedElement* unmanaged) @@ -1161,7 +1207,7 @@ namespace FlaxEngine internal static T[] GCHandleArrayToManagedArray(ManagedArray ptrArray) where T : class { - Span span = ptrArray.GetSpan(); + Span span = ptrArray.ToSpan(); T[] managedArray = new T[ptrArray.Length]; for (int i = 0; i < managedArray.Length; i++) managedArray[i] = span[i] != IntPtr.Zero ? (T)ManagedHandle.FromIntPtr(span[i]).Target : default; @@ -1476,7 +1522,7 @@ namespace FlaxEngine internal static Array ToManagedArray(ManagedArray nativeArray) { T[] arr = new T[nativeArray.Length]; - IntPtr nativePtr = nativeArray.GetPointer; + IntPtr nativePtr = nativeArray.Pointer; for (int i = 0; i < arr.Length; i++) { toManagedTypedMarshaller(ref arr[i], nativePtr, false); @@ -1683,11 +1729,11 @@ namespace FlaxEngine { ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target); if (ArrayFactory.GetMarshalledType(elementType) == elementType) - managedValue = Unsafe.As(managedArray.GetArray()); + managedValue = Unsafe.As(managedArray.ToArray()); else if (elementType.IsValueType) managedValue = Unsafe.As(MarshalHelper.ToManagedArray(managedArray)); else - managedValue = Unsafe.As(MarshalHelper.ToManagedArray(managedArray.GetSpan())); + managedValue = Unsafe.As(MarshalHelper.ToManagedArray(managedArray.ToSpan())); } else managedValue = null; @@ -1751,7 +1797,7 @@ namespace FlaxEngine if (nativePtr != IntPtr.Zero) { ManagedArray managedArray = Unsafe.As(ManagedHandle.FromIntPtr(nativePtr).Target); - managedValue = Unsafe.As(MarshalHelper.ToManagedArray(managedArray.GetSpan())); + managedValue = Unsafe.As(MarshalHelper.ToManagedArray(managedArray.ToSpan())); } else managedValue = null; @@ -1790,8 +1836,8 @@ namespace FlaxEngine else if (elementType.IsValueType) { // Convert array of custom structures into internal native layout - managedArray = ManagedArray.AllocateNewArray(arr.Length, Marshal.SizeOf(marshalledType), marshalledType); - IntPtr managedArrayPtr = managedArray.GetPointer; + managedArray = ManagedArray.AllocateNewArray(arr.Length, marshalledType); + IntPtr managedArrayPtr = managedArray.Pointer; for (int i = 0; i < arr.Length; i++) { MarshalToNative(arr.GetValue(i), managedArrayPtr, elementType); @@ -2246,7 +2292,7 @@ namespace FlaxEngine Type marshalledType = ArrayFactory.GetMarshalledType(type); if (marshalledType.IsValueType) { - ManagedArray managedArray = ManagedArray.AllocateNewArray((int)size, Marshal.SizeOf(marshalledType), marshalledType); + ManagedArray managedArray = ManagedArray.AllocateNewArray((int)size, marshalledType); return ManagedHandle.Alloc(managedArray/*, GCHandleType.Weak*/); } else @@ -2266,7 +2312,7 @@ namespace FlaxEngine if (managedArray.Length == 0) return IntPtr.Zero; Assert.IsTrue(index >= 0 && index < managedArray.Length); - return IntPtr.Add(managedArray.GetPointer, size * index); + return IntPtr.Add(managedArray.Pointer, size * index); } [UnmanagedCallersOnly] @@ -2486,8 +2532,8 @@ namespace FlaxEngine internal static void SetArrayValueReference(ManagedHandle arrayHandle, IntPtr elementPtr, IntPtr valueHandle) { ManagedArray managedArray = Unsafe.As(arrayHandle.Target); - int index = (int)(elementPtr.ToInt64() - managedArray.GetPointer.ToInt64()) / managedArray.ElementSize; - managedArray.GetSpan()[index] = valueHandle; + int index = (int)(elementPtr.ToInt64() - managedArray.Pointer.ToInt64()) / managedArray.ElementSize; + managedArray.ToSpan()[index] = valueHandle; } [UnmanagedCallersOnly] diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index d51d7e7f1..cc7ca5171 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -1526,23 +1526,23 @@ namespace Flax.Build.Bindings // Marshal blittable array elements back to original non-blittable elements string originalElementTypeMarshaller = originalElementType + "Marshaller"; string internalElementType = $"{originalElementTypeMarshaller}.{originalElementType}Internal"; - toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.NativeArrayToManagedArray<{originalElementType}, {internalElementType}>(((ManagedArray)ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target).GetSpan<{internalElementType}>(), {originalElementTypeMarshaller}.ToManaged) : null"); + toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.NativeArrayToManagedArray<{originalElementType}, {internalElementType}>(((ManagedArray)ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target).ToSpan<{internalElementType}>(), {originalElementTypeMarshaller}.ToManaged) : null"); toNativeContent.Append($"ManagedHandle.ToIntPtr(ManagedHandle.Alloc(ManagedArray.WrapNewArray(managed.{fieldInfo.Name}), GCHandleType.Weak))"); - freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = ((ManagedArray)handle.Target).GetSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} ((ManagedArray)handle.Target).Free(); handle.Free(); }}"); - freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = ((ManagedArray)handle.Target).GetSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} ((ManagedArray)handle.Target).Free(); handle.Free(); }}"); + freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = ((ManagedArray)handle.Target).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} ((ManagedArray)handle.Target).Free(); handle.Free(); }}"); + freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = ((ManagedArray)handle.Target).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} ((ManagedArray)handle.Target).Free(); handle.Free(); }}"); } else if (fieldInfo.Type.GenericArgs[0].IsObjectRef) { // Array elements passed as GCHandles toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>((ManagedArray)ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target) : null"); toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedHandle.Alloc(ManagedArray.WrapNewArray(NativeInterop.ManagedArrayToGCHandleArray(managed.{fieldInfo.Name})), GCHandleType.Weak)) : IntPtr.Zero"); - freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span ptrs = ((ManagedArray)handle.Target).GetSpan(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} ((ManagedArray)handle.Target).Free(); handle.Free(); }}"); - freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span ptrs = ((ManagedArray)handle.Target).GetSpan(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} ((ManagedArray)handle.Target).Free(); handle.Free(); }}"); + freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span ptrs = ((ManagedArray)handle.Target).ToSpan(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} ((ManagedArray)handle.Target).Free(); handle.Free(); }}"); + freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span ptrs = ((ManagedArray)handle.Target).ToSpan(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} ((ManagedArray)handle.Target).Free(); handle.Free(); }}"); } else { // Blittable array elements - toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? ((ManagedArray)ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target).GetArray<{originalElementType}>() : null"); + toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? ((ManagedArray)ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target).ToArray<{originalElementType}>() : null"); toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedHandle.Alloc(ManagedArray.WrapNewArray(managed.{fieldInfo.Name}), GCHandleType.Weak)) : IntPtr.Zero"); freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); ((ManagedArray)handle.Target).Free(); handle.Free(); }}"); freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); ((ManagedArray)handle.Target).Free(); handle.Free(); }}");