diff --git a/Source/Editor/Content/Import/TextureImportEntry.cs b/Source/Editor/Content/Import/TextureImportEntry.cs index 3e8006f73..c08ef6e8f 100644 --- a/Source/Editor/Content/Import/TextureImportEntry.cs +++ b/Source/Editor/Content/Import/TextureImportEntry.cs @@ -391,8 +391,8 @@ namespace FlaxEditor.Content.Import MaxSize = managed.MaxSize, TextureGroup = managed.TextureGroup, Size = managed.Size, - SpriteAreas = managed.SpriteAreas?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(NativeInterop.ManagedArrayToGCHandleArray(managed.SpriteAreas))) : IntPtr.Zero, - SpriteNames = managed.SpriteNames?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(NativeInterop.ManagedArrayToGCHandleArray(managed.SpriteNames))) : IntPtr.Zero, + SpriteAreas = managed.SpriteAreas?.Length > 0 ? ManagedHandle.ToIntPtr(NativeInterop.ManagedArrayToGCHandleWrappedArray(managed.SpriteAreas)) : IntPtr.Zero, + SpriteNames = managed.SpriteNames?.Length > 0 ? ManagedHandle.ToIntPtr(NativeInterop.ManagedArrayToGCHandleWrappedArray(managed.SpriteNames)) : IntPtr.Zero, }; } internal static void Free(InternalOptionsNative unmanaged) diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 8a5ffcf9c..5bc40a03f 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -157,39 +157,49 @@ namespace FlaxEngine /// public unsafe class ManagedArray { - private ManagedHandle pinnedArrayHandle; - private IntPtr unmanagedData; - private Type elementType; - private int elementSize; - private int length; + private ManagedHandle _pinnedArrayHandle; + private IntPtr _unmanagedData; + private Type _arrayType; + private Type _elementType; + private int _elementSize; + private int _length; - public static ManagedArray WrapNewArray(Array arr) => new ManagedArray(arr); + public static ManagedArray WrapNewArray(Array arr) => new ManagedArray(arr, arr.GetType()); + + public static ManagedArray WrapNewArray(Array arr, Type arrayType) => new ManagedArray(arr, arrayType); /// /// Returns an instance of ManagedArray from shared pool. /// - /// - /// The resources must be released by calling FreePooled() instead of Free()-method. - /// + /// The resources must be released by calling FreePooled() instead of Free()-method. public static ManagedArray WrapPooledArray(Array arr) { ManagedArray managedArray = ManagedArrayPool.Get(); - managedArray.WrapArray(arr); + managedArray.WrapArray(arr, arr.GetType()); return managedArray; } - 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, Type elementType) - => new ManagedArray(ptr, length, elementType); - /// /// Returns an instance of ManagedArray from shared pool. /// - /// - /// The resources must be released by calling FreePooled() instead of Free()-method. - /// + /// The resources must be released by calling FreePooled() instead of Free()-method. + public static ManagedArray WrapPooledArray(Array arr, Type arrayType) + { + ManagedArray managedArray = ManagedArrayPool.Get(); + managedArray.WrapArray(arr, arrayType); + return managedArray; + } + + internal static ManagedArray AllocateNewArray(int length, Type arrayType, Type elementType) + => new ManagedArray((IntPtr)NativeInterop.NativeAlloc(length, Marshal.SizeOf(elementType)), length, arrayType, elementType); + + internal static ManagedArray AllocateNewArray(IntPtr ptr, int length, Type arrayType, Type elementType) + => new ManagedArray(ptr, length, arrayType, elementType); + + /// + /// Returns an instance of ManagedArray from shared pool. + /// + /// The resources must be released by calling FreePooled() instead of Free()-method. public static ManagedArray AllocatePooledArray(T* ptr, int length) where T : unmanaged { ManagedArray managedArray = ManagedArrayPool.Get(); @@ -200,67 +210,67 @@ namespace FlaxEngine /// /// Returns an instance of ManagedArray from shared pool. /// - /// - /// The resources must be released by calling FreePooled() instead of Free()-method. - /// + /// The resources must be released by calling FreePooled() instead of Free()-method. public static ManagedArray AllocatePooledArray(int length) where T : unmanaged { ManagedArray managedArray = ManagedArrayPool.Get(); - managedArray.Allocate((IntPtr)NativeInterop.NativeAlloc(length, Unsafe.SizeOf()), length, typeof(T)); + managedArray.Allocate((T*)NativeInterop.NativeAlloc(length, Unsafe.SizeOf()), length); return managedArray; } - public ManagedArray(Array arr) => WrapArray(arr); + public ManagedArray(Array arr, Type elementType) => WrapArray(arr, elementType); - internal void WrapArray(Array arr) + internal void WrapArray(Array arr, Type arrayType) { - pinnedArrayHandle = ManagedHandle.Alloc(arr, GCHandleType.Pinned); - unmanagedData = Marshal.UnsafeAddrOfPinnedArrayElement(arr, 0); - length = arr.Length; - elementType = arr.GetType().GetElementType(); - elementSize = Marshal.SizeOf(elementType); + _pinnedArrayHandle = ManagedHandle.Alloc(arr, GCHandleType.Pinned); + _unmanagedData = Marshal.UnsafeAddrOfPinnedArrayElement(arr, 0); + _length = arr.Length; + _arrayType = arrayType; + _elementType = arr.GetType().GetElementType(); + _elementSize = Marshal.SizeOf(_elementType); } internal void Allocate(T* ptr, int length) where T : unmanaged { - unmanagedData = new IntPtr(ptr); - this.length = length; - elementType = typeof(T); - elementSize = Unsafe.SizeOf(); - } - - internal void Allocate(IntPtr ptr, int length, Type elementType) - { - unmanagedData = ptr; - this.length = length; - this.elementType = elementType; - elementSize = Marshal.SizeOf(elementType); + _unmanagedData = new IntPtr(ptr); + _length = length; + _arrayType = typeof(T).MakeArrayType(); + _elementType = typeof(T); + _elementSize = Unsafe.SizeOf(); } private ManagedArray() { } - private ManagedArray(IntPtr ptr, int length, Type elementType) => Allocate(ptr, length, elementType); + private ManagedArray(IntPtr ptr, int length, Type arrayType, Type elementType) + { + Assert.IsTrue(arrayType.IsArray); + _unmanagedData = ptr; + _length = length; + _arrayType = arrayType; + _elementType = elementType; + _elementSize = Marshal.SizeOf(elementType); + } ~ManagedArray() { - if (unmanagedData != IntPtr.Zero) + if (_unmanagedData != IntPtr.Zero) Free(); } public void Free() { GC.SuppressFinalize(this); - if (pinnedArrayHandle.IsAllocated) + if (_pinnedArrayHandle.IsAllocated) { - pinnedArrayHandle.Free(); - unmanagedData = IntPtr.Zero; + _pinnedArrayHandle.Free(); + _unmanagedData = IntPtr.Zero; } - if (unmanagedData != IntPtr.Zero) + if (_unmanagedData != IntPtr.Zero) { - NativeInterop.NativeFree(unmanagedData.ToPointer()); - unmanagedData = IntPtr.Zero; + NativeInterop.NativeFree(_unmanagedData.ToPointer()); + _unmanagedData = IntPtr.Zero; } } @@ -270,17 +280,21 @@ namespace FlaxEngine ManagedArrayPool.Put(this); } - internal IntPtr Pointer => unmanagedData; + internal IntPtr Pointer => _unmanagedData; - internal int Length => length; + internal int Length => _length; - internal int ElementSize => elementSize; + internal int ElementSize => _elementSize; - public Span ToSpan() where T : struct => new Span(unmanagedData.ToPointer(), length); + internal Type ElementType => _elementType; - public T[] ToArray() where T : struct => new Span(unmanagedData.ToPointer(), length).ToArray(); + internal Type ArrayType => _arrayType; - public Array ToArray() => ArrayCast.ToArray(new Span(unmanagedData.ToPointer(), length * elementSize), elementType); + public Span ToSpan() where T : struct => new Span(_unmanagedData.ToPointer(), _length); + + public T[] ToArray() where T : struct => new Span(_unmanagedData.ToPointer(), _length).ToArray(); + + public Array ToArray() => ArrayCast.ToArray(new Span(_unmanagedData.ToPointer(), _length * _elementSize), _elementType); /// /// Creates an Array of the specified type from span of bytes. @@ -1251,6 +1265,8 @@ namespace FlaxEngine internal static IntPtr[] ManagedArrayToGCHandleArray(Array array) { + if (array.Length == 0) + return Array.Empty(); IntPtr[] pointerArray = new IntPtr[array.Length]; for (int i = 0; i < pointerArray.Length; i++) { @@ -1261,6 +1277,12 @@ namespace FlaxEngine return pointerArray; } + internal static ManagedArray ManagedArrayToGCHandleWrappedArray(Array array) + { + IntPtr[] pointerArray = ManagedArrayToGCHandleArray(array); + return ManagedArray.WrapNewArray(pointerArray, array.GetType()); + } + internal static T[] NativeArrayToManagedArray(Span nativeSpan, Func toManagedFunc) { T[] managedArray = new T[nativeSpan.Length]; @@ -1877,11 +1899,11 @@ namespace FlaxEngine var marshalledType = ArrayFactory.GetMarshalledType(elementType); ManagedArray managedArray; if (marshalledType == elementType) - managedArray = ManagedArray.WrapNewArray(arr); + managedArray = ManagedArray.WrapNewArray(arr, type); else if (elementType.IsValueType) { // Convert array of custom structures into internal native layout - managedArray = ManagedArray.AllocateNewArray(arr.Length, marshalledType); + managedArray = ManagedArray.AllocateNewArray(arr.Length, type, marshalledType); IntPtr managedArrayPtr = managedArray.Pointer; for (int i = 0; i < arr.Length; i++) { @@ -1890,7 +1912,7 @@ namespace FlaxEngine } } else - managedArray = ManagedArray.WrapNewArray(ManagedArrayToGCHandleArray(arr)); + managedArray = ManagedArrayToGCHandleWrappedArray(arr); managedPtr = ManagedHandle.ToIntPtr(managedArray, GCHandleType.Weak); } } @@ -2325,21 +2347,30 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static ManagedHandle NewArray(ManagedHandle typeHandle, long size) { - Type type = Unsafe.As(typeHandle.Target); - Type marshalledType = ArrayFactory.GetMarshalledType(type); + Type elementType = Unsafe.As(typeHandle.Target); + Type marshalledType = ArrayFactory.GetMarshalledType(elementType); + Type arrayType = elementType.MakeArrayType(); if (marshalledType.IsValueType) { - ManagedArray managedArray = ManagedArray.AllocateNewArray((int)size, marshalledType); + ManagedArray managedArray = ManagedArray.AllocateNewArray((int)size, arrayType, marshalledType); return ManagedHandle.Alloc(managedArray); } else { - Array arr = ArrayFactory.CreateArray(type, size); - ManagedArray managedArray = ManagedArray.WrapNewArray(arr); + Array arr = ArrayFactory.CreateArray(elementType, size); + ManagedArray managedArray = ManagedArray.WrapNewArray(arr, arrayType); return ManagedHandle.Alloc(managedArray); } } + [UnmanagedCallersOnly] + internal static unsafe ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle) + { + Type elementType = Unsafe.As(elementTypeHandle.Target); + Type classType = elementType.MakeArrayType(); + return GetTypeGCHandle(classType); + } + [UnmanagedCallersOnly] internal static unsafe IntPtr GetArrayPointer(ManagedHandle arrayHandle) { @@ -2392,8 +2423,10 @@ namespace FlaxEngine [UnmanagedCallersOnly] internal static ManagedHandle GetObjectType(ManagedHandle handle) { - var obj = handle.Target; + object obj = handle.Target; Type classType = obj.GetType(); + if (classType == typeof(ManagedArray)) + classType = ((ManagedArray)obj).ArrayType; return GetTypeGCHandle(classType); } @@ -2688,15 +2721,17 @@ namespace FlaxEngine } [UnmanagedCallersOnly] - internal static unsafe int NativeSizeOf(ManagedHandle typeHandle, uint* align) + internal static unsafe int NativeSizeOf(ManagedHandle typeHandle) { Type type = Unsafe.As(typeHandle.Target); Type nativeType = GetInternalType(type) ?? type; if (nativeType == typeof(Version)) nativeType = typeof(VersionNative); - - int size = Marshal.SizeOf(nativeType); - *align = (uint)size; // Is this correct? + int size; + if (nativeType.IsClass) + size = sizeof(IntPtr); + else + size = Marshal.SizeOf(nativeType); return size; } @@ -2927,6 +2962,7 @@ namespace FlaxEngine break; } case Type _ when type.IsArray: + case Type _ when type == typeof(ManagedArray): monoType = MTypes.Array; break; case Type _ when type.IsValueType && !type.IsEnum && !type.IsPrimitive: diff --git a/Source/Engine/Engine/NativeInterop_Invoker.cs b/Source/Engine/Engine/NativeInterop_Invoker.cs index b9b793cd5..e7f4f5be2 100644 --- a/Source/Engine/Engine/NativeInterop_Invoker.cs +++ b/Source/Engine/Engine/NativeInterop_Invoker.cs @@ -88,7 +88,7 @@ namespace FlaxEngine var elementType = typeof(TRet).GetElementType(); if (ArrayFactory.GetMarshalledType(elementType) == elementType) return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As(returnValue)), GCHandleType.Weak); - return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(ManagedArrayToGCHandleArray(Unsafe.As(returnValue))), GCHandleType.Weak); + return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As(returnValue)), GCHandleType.Weak); } return ManagedHandle.ToIntPtr(returnValue, GCHandleType.Weak); } @@ -108,7 +108,7 @@ namespace FlaxEngine if (returnType.IsArray && ArrayFactory.GetMarshalledType(returnType.GetElementType()) == returnType.GetElementType()) return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As(returnObject)), GCHandleType.Weak); if (returnType.IsArray) - return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(ManagedArrayToGCHandleArray(Unsafe.As(returnObject))), GCHandleType.Weak); + return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As(returnObject)), GCHandleType.Weak); return ManagedHandle.ToIntPtr(returnObject, GCHandleType.Weak); } @@ -129,7 +129,7 @@ namespace FlaxEngine var elementType = typeof(TRet).GetElementType(); if (ArrayFactory.GetMarshalledType(elementType) == elementType) return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As(returnValue)), GCHandleType.Weak); - return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(ManagedArrayToGCHandleArray(Unsafe.As(returnValue))), GCHandleType.Weak); + return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As(returnValue)), GCHandleType.Weak); } // Match Mono bindings and pass value as pointer to prevent boxing it if (typeof(TRet) == typeof(System.Boolean)) @@ -166,7 +166,7 @@ namespace FlaxEngine var elementType = returnType.GetElementType(); if (ArrayFactory.GetMarshalledType(elementType) == elementType) return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As(returnObject)), GCHandleType.Weak); - return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(ManagedArrayToGCHandleArray(Unsafe.As(returnObject))), GCHandleType.Weak); + return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As(returnObject)), GCHandleType.Weak); } // Match Mono bindings and pass value as pointer to prevent boxing it if (returnType == typeof(System.Boolean)) diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index 1238d2548..3232605f8 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -1200,9 +1200,10 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp { // Box instance into C# object MObject* instanceObject = MUtils::BoxVariant(instance); + const MClass* instanceObjectClass = MCore::Object::GetClass(instanceObject); // Validate instance - if (!instanceObject || !MCore::Object::GetClass(instanceObject)->IsSubClassOf(mMethod->GetParentClass(), withInterfaces)) + if (!instanceObject || !instanceObjectClass->IsSubClassOf(mMethod->GetParentClass(), withInterfaces)) { if (!instanceObject) LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) without object instance", String(mMethod->GetParentClass()->GetFullName()), String(mMethod->GetName()), parametersCount); @@ -1212,7 +1213,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp } // For value-types instance is the actual boxed object data, not te object itself - mInstance = MCore::Object::GetClass(instanceObject)->IsValueType() ? MCore::Object::Unbox(instanceObject) : instanceObject; + mInstance = instanceObjectClass->IsValueType() ? MCore::Object::Unbox(instanceObject) : instanceObject; } // Marshal parameters diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp index 188135611..1022ab5a1 100644 --- a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp @@ -389,7 +389,8 @@ Variant MUtils::UnboxVariant(MObject* value) case MTypes::Array: { void* ptr = MCore::Array::GetAddress((MArray*)value); - if (klass->GetElementClass() == MCore::TypeCache::Byte) + MClass* elementClass = klass->GetElementClass(); + if (elementClass == MCore::TypeCache::Byte) { Variant v; v.SetBlob(ptr, MCore::Array::GetLength((MArray*)value)); @@ -401,7 +402,6 @@ Variant MUtils::UnboxVariant(MObject* value) auto& array = v.AsArray(); array.Resize(MCore::Array::GetLength((MArray*)value)); const StringAnsiView elementTypename(*fullname, fullname.Length() - 2); - MClass* elementClass = klass->GetElementClass(); const int32 elementSize = elementClass->GetInstanceSize(); if (elementClass->IsEnum()) { diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 271b69137..d50865988 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -374,8 +374,9 @@ MArray* MCore::Array::New(const MClass* elementKlass, int32 length) MClass* MCore::Array::GetClass(MClass* elementKlass) { - MISSING_CODE("TODO: MCore::Array::GetClass"); // TODO: MCore::Object::GetClass - return nullptr; + static void* GetArrayLengthPtr = GetStaticMethodPointer(TEXT("GetArrayTypeFromElementType")); + void* classHandle = CallStaticMethod(GetArrayLengthPtr, elementKlass->_handle); + return GetOrCreateClass((void*)classHandle); } int32 MCore::Array::GetLength(const MArray* obj) @@ -806,9 +807,8 @@ uint32 MClass::GetInstanceSize() const { if (_size != 0) return _size; - uint32 align; static void* NativeSizeOfPtr = GetStaticMethodPointer(TEXT("NativeSizeOf")); - _size = CallStaticMethod(NativeSizeOfPtr, _handle, &align); + _size = CallStaticMethod(NativeSizeOfPtr, _handle); return _size; } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 6b9f3d410..85264cb09 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -1541,7 +1541,7 @@ namespace Flax.Build.Bindings { // 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(ManagedArray.WrapNewArray(NativeInterop.ManagedArrayToGCHandleArray(managed.{fieldInfo.Name})), GCHandleType.Weak) : IntPtr.Zero"); + toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(NativeInterop.ManagedArrayToGCHandleWrappedArray(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).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(); }}"); }