Various fixes and improvements to auto-generated bindings usability

This commit is contained in:
Wojtek Figat
2023-06-12 14:35:11 +02:00
parent 14d12418b6
commit 6b8d8b7913
4 changed files with 66 additions and 28 deletions

View File

@@ -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);

View File

@@ -182,12 +182,20 @@ namespace FlaxEngine.Interop
return ManagedArray.WrapNewArray(pointerArray, array.GetType());
}
internal static T[] NativeArrayToManagedArray<T, U>(Span<U> nativeSpan, Func<U, T> toManagedFunc)
internal static TDst[] ConvertArray<TSrc, TDst>(Span<TSrc> src, Func<TSrc, TDst> 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, TDst>(TSrc[] src, Func<TSrc, TDst> 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<Type, UnboxerDelegate> unboxers = new ConcurrentDictionary<Type, UnboxerDelegate>(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);

View File

@@ -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<ManagedArray>(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<ManagedArray>(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<ManagedArray>(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} (Unsafe.As<ManagedArray>(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<ManagedArray>(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
}

View File

@@ -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<FieldInfo> 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++)