From 6b8d8b7913ada0d73cc3fbbd4c5c9d0144c6b1cf Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 12 Jun 2023 14:35:11 +0200 Subject: [PATCH] Various fixes and improvements to auto-generated bindings usability --- Source/Engine/Engine/NativeInterop.Managed.cs | 14 ++++--- Source/Engine/Engine/NativeInterop.cs | 19 ++++++--- .../Bindings/BindingsGenerator.CSharp.cs | 22 +++++++++-- .../Bindings/BindingsGenerator.Cpp.cs | 39 ++++++++++++------- 4 files changed, 66 insertions(+), 28 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Managed.cs b/Source/Engine/Engine/NativeInterop.Managed.cs index 538703d4e..7eda1cdec 100644 --- a/Source/Engine/Engine/NativeInterop.Managed.cs +++ b/Source/Engine/Engine/NativeInterop.Managed.cs @@ -21,7 +21,7 @@ namespace FlaxEngine.Interop #endif public unsafe class ManagedArray { - private ManagedHandle _pinnedArrayHandle; + private ManagedHandle _managedHandle; private IntPtr _unmanagedData; private int _unmanagedAllocationSize; private Type _arrayType; @@ -78,7 +78,9 @@ namespace FlaxEngine.Interop { if (_unmanagedData != IntPtr.Zero) NativeInterop.NativeFree(_unmanagedData.ToPointer()); - _pinnedArrayHandle = ManagedHandle.Alloc(arr, GCHandleType.Pinned); + if (_managedHandle.IsAllocated) + _managedHandle.Free(); + _managedHandle = ManagedHandle.Alloc(arr, GCHandleType.Pinned); _unmanagedData = Marshal.UnsafeAddrOfPinnedArrayElement(arr, 0); _unmanagedAllocationSize = 0; _length = arr.Length; @@ -127,9 +129,9 @@ namespace FlaxEngine.Interop public void Free() { GC.SuppressFinalize(this); - if (_pinnedArrayHandle.IsAllocated) + if (_managedHandle.IsAllocated) { - _pinnedArrayHandle.Free(); + _managedHandle.Free(); _unmanagedData = IntPtr.Zero; } if (_unmanagedData != IntPtr.Zero) @@ -142,9 +144,9 @@ namespace FlaxEngine.Interop public void FreePooled() { - if (_pinnedArrayHandle.IsAllocated) + if (_managedHandle.IsAllocated) { - _pinnedArrayHandle.Free(); + _managedHandle.Free(); _unmanagedData = IntPtr.Zero; } ManagedArrayPool.Put(this); diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index f901a6708..b33e4bcb6 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -182,12 +182,20 @@ namespace FlaxEngine.Interop return ManagedArray.WrapNewArray(pointerArray, array.GetType()); } - internal static T[] NativeArrayToManagedArray(Span nativeSpan, Func toManagedFunc) + internal static TDst[] ConvertArray(Span src, Func convertFunc) { - T[] managedArray = new T[nativeSpan.Length]; - for (int i = 0; i < nativeSpan.Length; i++) - managedArray[i] = toManagedFunc(nativeSpan[i]); - return managedArray; + TDst[] dst = new TDst[src.Length]; + for (int i = 0; i < src.Length; i++) + dst[i] = convertFunc(src[i]); + return dst; + } + + internal static TDst[] ConvertArray(TSrc[] src, Func convertFunc) + { + TDst[] dst = new TDst[src.Length]; + for (int i = 0; i < src.Length; i++) + dst[i] = convertFunc(src[i]); + return dst; } private static Type FindType(string typeName) @@ -995,6 +1003,7 @@ namespace FlaxEngine.Interop internal static class ValueTypeUnboxer { private delegate IntPtr UnboxerDelegate(object value); + private static ConcurrentDictionary unboxers = new ConcurrentDictionary(1, 3); private static MethodInfo unboxerMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointer), BindingFlags.Static | BindingFlags.NonPublic); private static MethodInfo unboxerToNativeMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointerWithConverter), BindingFlags.Static | BindingFlags.NonPublic); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 3250a3262..cd6bdf6ab 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -377,7 +377,15 @@ namespace Flax.Build.Bindings var typeName = typeInfo.Type.Replace("::", "."); if (apiType != null) { + // Add reference to the namespace CSharpUsedNamespaces.Add(apiType.Namespace); + var apiTypeParent = apiType.Parent; + while (apiTypeParent != null) + { + CSharpUsedNamespaces.Add(apiTypeParent.Namespace); + apiTypeParent = apiTypeParent.Parent; + } + if (apiType.IsScriptingObject || apiType.IsInterface) return typeName; if (typeInfo.IsPtr && apiType.IsPod) @@ -403,6 +411,15 @@ namespace Flax.Build.Bindings var apiType = FindApiTypeInfo(buildData, typeInfo, caller); if (apiType != null) { + // Add reference to the namespace + CSharpUsedNamespaces.Add(apiType.Namespace); + var apiTypeParent = apiType.Parent; + while (apiTypeParent != null) + { + CSharpUsedNamespaces.Add(apiTypeParent.Namespace); + apiTypeParent = apiTypeParent.Parent; + } + if (apiType.IsScriptingObject || apiType.IsInterface) return "IntPtr"; } @@ -1401,7 +1418,6 @@ namespace Flax.Build.Bindings { // Native struct begin // TODO: skip using this utility structure if the auto-generated C# struct is already the same as XXXInternal here below - GenerateCSharpAttributes(buildData, contents, indent, structureInfo, true); if (buildData.Target != null & buildData.Target.IsEditor) contents.Append(indent).AppendLine("[HideInEditor]"); contents.Append(indent).AppendLine("[StructLayout(LayoutKind.Sequential)]"); @@ -1565,8 +1581,8 @@ 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}>((Unsafe.As(ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target)).ToSpan<{internalElementType}>(), {originalElementTypeMarshaller}.ToManaged) : null"); - toNativeContent.Append($"ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(managed.{fieldInfo.Name}), GCHandleType.Weak)"); + toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.ConvertArray((Unsafe.As(ManagedHandle.FromIntPtr(managed.{fieldInfo.Name}).Target)).ToSpan<{internalElementType}>(), {originalElementTypeMarshaller}.ToManaged) : null"); + toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(NativeInterop.ConvertArray(managed.{fieldInfo.Name}, {originalElementTypeMarshaller}.ToNative)), GCHandleType.Weak) : IntPtr.Zero"); freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = (Unsafe.As(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} (Unsafe.As(handle.Target)).Free(); handle.Free(); }}"); freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = (Unsafe.As(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} (Unsafe.As(handle.Target)).Free(); handle.Free(); }}"); } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 2c2c9b341..dfd5daf73 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -431,6 +431,17 @@ namespace Flax.Build.Bindings return $"{GenerateCppGetMClass(buildData, typeInfo, caller, null)}->GetType()"; } + private static string GenerateCppManagedWrapperName(ApiTypeInfo type) + { + var result = type.NativeName + "Managed"; + while (type.Parent is ClassStructInfo) + { + result = type.Parent.NativeName + '_' + result; + type = type.Parent; + } + return result; + } + private static string GenerateCppWrapperNativeToManaged(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller, out string type, FunctionInfo functionInfo) { // Use dynamic array as wrapper container for fixed-size native arrays @@ -587,10 +598,9 @@ namespace Flax.Build.Bindings // Use wrapper structure that represents the memory layout of the managed data if (!CppUsedNonPodTypes.Contains(apiType)) CppUsedNonPodTypes.Add(apiType); + type = GenerateCppManagedWrapperName(apiType); if (functionInfo != null) - type = apiType.Name + "Managed*"; - else - type = apiType.Name + "Managed"; + type += '*'; return "ToManaged({0})"; } @@ -794,7 +804,7 @@ namespace Flax.Build.Bindings needLocalVariable = true; if (!CppUsedNonPodTypes.Contains(apiType)) CppUsedNonPodTypes.Add(apiType); - type = apiType.Name + "Managed"; + type = GenerateCppManagedWrapperName(apiType); if (functionInfo != null) return "ToNative(*{0})"; return "ToNative({0})"; @@ -2372,7 +2382,7 @@ namespace Flax.Build.Bindings if (structureInfo.IsPod) contents.AppendLine($" Platform::MemoryCopy(ptr, MCore::Object::Unbox(managed), sizeof({structureTypeNameNative}));"); else - contents.AppendLine($" *({structureTypeNameNative}*)ptr = ToNative(*({structureInfo.NativeName}Managed*)MCore::Object::Unbox(managed));"); + contents.AppendLine($" *({structureTypeNameNative}*)ptr = ToNative(*({GenerateCppManagedWrapperName(structureInfo)}*)MCore::Object::Unbox(managed));"); contents.AppendLine(" }").AppendLine(); } else @@ -2811,6 +2821,7 @@ namespace Flax.Build.Bindings foreach (var apiType in CppUsedNonPodTypesList) { header.AppendLine(); + var wrapperName = GenerateCppManagedWrapperName(apiType); var structureInfo = apiType as StructureInfo; var classInfo = apiType as ClassInfo; List fields; @@ -2828,7 +2839,7 @@ namespace Flax.Build.Bindings if (structureInfo != null) { // Generate managed type memory layout - header.Append("struct ").Append(apiType.Name).Append("Managed").AppendLine(); + header.Append("struct ").Append(wrapperName).AppendLine(); header.Append('{').AppendLine(); if (classInfo != null) header.AppendLine(" MObject obj;"); @@ -2857,8 +2868,8 @@ namespace Flax.Build.Bindings // Generate forward declarations of structure converting functions header.AppendLine(); header.AppendLine("namespace {"); - header.AppendFormat("{0}Managed ToManaged(const {1}& value);", apiType.Name, fullName).AppendLine(); - header.AppendFormat("{1} ToNative(const {0}Managed& value);", apiType.Name, fullName).AppendLine(); + header.AppendFormat("{0} ToManaged(const {1}& value);", wrapperName, fullName).AppendLine(); + header.AppendFormat("{1} ToNative(const {0}& value);", wrapperName, fullName).AppendLine(); header.AppendLine("}"); // Generate MConverter for a structure @@ -2872,12 +2883,12 @@ namespace Flax.Build.Bindings header.Append(" }").AppendLine(); header.AppendFormat(" void Unbox({0}& result, MObject* data)", fullName).AppendLine(); header.Append(" {").AppendLine(); - header.AppendFormat(" result = ToNative(*reinterpret_cast<{0}Managed*>(MCore::Object::Unbox(data)));", apiType.Name).AppendLine(); + header.AppendFormat(" result = ToNative(*reinterpret_cast<{0}*>(MCore::Object::Unbox(data)));", wrapperName).AppendLine(); header.Append(" }").AppendLine(); header.AppendFormat(" void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.AppendFormat(" MClass* klass = {0}::TypeInitializer.GetClass();", fullName).AppendLine(); - header.AppendFormat(" {0}Managed* resultPtr = ({0}Managed*)MCore::Array::GetAddress(result);", apiType.Name).AppendLine(); + header.AppendFormat(" {0}* resultPtr = ({0}*)MCore::Array::GetAddress(result);", wrapperName).AppendLine(); header.Append(" for (int32 i = 0; i < data.Length(); i++)").AppendLine(); header.Append(" {").AppendLine(); header.Append(" auto managed = ToManaged(data[i]);").AppendLine(); @@ -2886,7 +2897,7 @@ namespace Flax.Build.Bindings header.Append(" }").AppendLine(); header.AppendFormat(" void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine(); header.Append(" {").AppendLine(); - header.AppendFormat(" {0}Managed* dataPtr = ({0}Managed*)MCore::Array::GetAddress(data);", apiType.Name).AppendLine(); + header.AppendFormat(" {0}* dataPtr = ({0}*)MCore::Array::GetAddress(data);", wrapperName).AppendLine(); header.Append(" for (int32 i = 0; i < result.Length(); i++)").AppendLine(); header.Append(" result[i] = ToNative(dataPtr[i]);").AppendLine(); header.Append(" }").AppendLine(); @@ -2895,9 +2906,9 @@ namespace Flax.Build.Bindings // Generate converting function native -> managed header.AppendLine(); header.AppendLine("namespace {"); - header.AppendFormat("{0}Managed ToManaged(const {1}& value)", apiType.Name, fullName).AppendLine(); + header.AppendFormat("{0} ToManaged(const {1}& value)", wrapperName, fullName).AppendLine(); header.Append('{').AppendLine(); - header.AppendFormat(" {0}Managed result;", apiType.Name).AppendLine(); + header.AppendFormat(" {0} result;", wrapperName).AppendLine(); for (var i = 0; i < fields.Count; i++) { var fieldInfo = fields[i]; @@ -2927,7 +2938,7 @@ namespace Flax.Build.Bindings // Generate converting function managed -> native header.AppendLine(); - header.AppendFormat("{1} ToNative(const {0}Managed& value)", apiType.Name, fullName).AppendLine(); + header.AppendFormat("{1} ToNative(const {0}& value)", wrapperName, fullName).AppendLine(); header.Append('{').AppendLine(); header.AppendFormat(" {0} result;", fullName).AppendLine(); for (var i = 0; i < fields.Count; i++)