This commit is contained in:
2025-12-09 21:49:37 +02:00
parent 2cc48301c2
commit 2ba4457e8e
19 changed files with 920 additions and 190 deletions

View File

@@ -15,7 +15,7 @@ if errorlevel 1 goto BuildToolFailed
:: Build bindings for all editor configurations :: Build bindings for all editor configurations
echo Building C# bindings... echo Building C# bindings...
Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor,FlaxGame
popd popd
echo Done! echo Done!

View File

@@ -334,7 +334,7 @@ Window* ManagedEditor::GetMainWindow()
ASSERT(HasManagedInstance()); ASSERT(HasManagedInstance());
const auto method = GetClass()->GetMethod("GetMainWindowPtr"); const auto method = GetClass()->GetMethod("GetMainWindowPtr");
ASSERT(method); ASSERT(method);
return (Window*)MUtils::Unbox<void*>(method->Invoke(GetManagedInstance(), nullptr, nullptr)); return (Window*)MUtils::Unbox<void*>(method->Invoke(GetManagedInstance(), nullptr, nullptr), true);
} }
bool ManagedEditor::CanReloadScripts() bool ManagedEditor::CanReloadScripts()
@@ -346,7 +346,7 @@ bool ManagedEditor::CanReloadScripts()
Internal_CanReloadScripts = GetClass()->GetMethod("Internal_CanReloadScripts"); Internal_CanReloadScripts = GetClass()->GetMethod("Internal_CanReloadScripts");
ASSERT(Internal_CanReloadScripts); ASSERT(Internal_CanReloadScripts);
} }
return MUtils::Unbox<bool>(Internal_CanReloadScripts->Invoke(GetManagedInstance(), nullptr, nullptr)); return MUtils::Unbox<bool>(Internal_CanReloadScripts->Invoke(GetManagedInstance(), nullptr, nullptr), true);
} }
bool ManagedEditor::CanAutoBuildCSG() bool ManagedEditor::CanAutoBuildCSG()
@@ -365,7 +365,7 @@ bool ManagedEditor::CanAutoBuildCSG()
Internal_CanAutoBuildCSG = GetClass()->GetMethod("Internal_CanAutoBuildCSG"); Internal_CanAutoBuildCSG = GetClass()->GetMethod("Internal_CanAutoBuildCSG");
ASSERT(Internal_CanAutoBuildCSG); ASSERT(Internal_CanAutoBuildCSG);
} }
return MUtils::Unbox<bool>(Internal_CanAutoBuildCSG->Invoke(GetManagedInstance(), nullptr, nullptr)); return MUtils::Unbox<bool>(Internal_CanAutoBuildCSG->Invoke(GetManagedInstance(), nullptr, nullptr), true);
} }
bool ManagedEditor::CanAutoBuildNavMesh() bool ManagedEditor::CanAutoBuildNavMesh()
@@ -384,7 +384,7 @@ bool ManagedEditor::CanAutoBuildNavMesh()
Internal_CanAutoBuildNavMesh = GetClass()->GetMethod("Internal_CanAutoBuildNavMesh"); Internal_CanAutoBuildNavMesh = GetClass()->GetMethod("Internal_CanAutoBuildNavMesh");
ASSERT(Internal_CanAutoBuildNavMesh); ASSERT(Internal_CanAutoBuildNavMesh);
} }
return MUtils::Unbox<bool>(Internal_CanAutoBuildNavMesh->Invoke(GetManagedInstance(), nullptr, nullptr)); return MUtils::Unbox<bool>(Internal_CanAutoBuildNavMesh->Invoke(GetManagedInstance(), nullptr, nullptr), true);
} }
bool ManagedEditor::HasGameViewportFocus() const bool ManagedEditor::HasGameViewportFocus() const
@@ -397,7 +397,7 @@ bool ManagedEditor::HasGameViewportFocus() const
Internal_HasGameViewportFocus = GetClass()->GetMethod("Internal_HasGameViewportFocus"); Internal_HasGameViewportFocus = GetClass()->GetMethod("Internal_HasGameViewportFocus");
ASSERT(Internal_HasGameViewportFocus); ASSERT(Internal_HasGameViewportFocus);
} }
result = MUtils::Unbox<bool>(Internal_HasGameViewportFocus->Invoke(GetManagedInstance(), nullptr, nullptr)); result = MUtils::Unbox<bool>(Internal_HasGameViewportFocus->Invoke(GetManagedInstance(), nullptr, nullptr), true);
} }
return result; return result;
} }
@@ -495,7 +495,7 @@ bool ManagedEditor::OnAppExit()
Internal_OnAppExit = GetClass()->GetMethod("Internal_OnAppExit"); Internal_OnAppExit = GetClass()->GetMethod("Internal_OnAppExit");
ASSERT(Internal_OnAppExit); ASSERT(Internal_OnAppExit);
} }
return MUtils::Unbox<bool>(Internal_OnAppExit->Invoke(GetManagedInstance(), nullptr, nullptr)); return MUtils::Unbox<bool>(Internal_OnAppExit->Invoke(GetManagedInstance(), nullptr, nullptr), true);
} }
void ManagedEditor::RequestStartPlayOnEditMode() void ManagedEditor::RequestStartPlayOnEditMode()

View File

@@ -12,6 +12,7 @@ using FlaxEditor.GUI;
using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Tabs; using FlaxEditor.GUI.Tabs;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.Assertions;
using FlaxEngine.GUI; using FlaxEngine.GUI;
using FlaxEngine.Json; using FlaxEngine.Json;
@@ -972,6 +973,9 @@ namespace FlaxEditor.Windows
_cloneProjectButton.Clicked -= OnCloneProjectButtonClicked; _cloneProjectButton.Clicked -= OnCloneProjectButtonClicked;
PluginManager.PluginsChanged -= OnPluginsChanged; PluginManager.PluginsChanged -= OnPluginsChanged;
Assert.IsTrue(!_entries.Any());
_entries.Clear();
base.OnDestroy(); base.OnDestroy();
} }
} }

View File

@@ -71,7 +71,7 @@ bool AccessVariant(Variant& instance, const StringAnsiView& member, Variant& val
if (set) if (set)
mField->SetValue(instanceObject, MUtils::VariantToManagedArgPtr(value, mField->GetType(), failed)); mField->SetValue(instanceObject, MUtils::VariantToManagedArgPtr(value, mField->GetType(), failed));
else else
value = MUtils::UnboxVariant(mField->GetValueBoxed(instanceObject)); value = MUtils::UnboxVariant(mField->GetValueBoxed(instanceObject), true);
return !failed; return !failed;
} }
else if (const auto mProperty = mClass->GetProperty(member.Get())) else if (const auto mProperty = mClass->GetProperty(member.Get()))
@@ -79,7 +79,7 @@ bool AccessVariant(Variant& instance, const StringAnsiView& member, Variant& val
if (set) if (set)
mProperty->SetValue(instanceObject, MUtils::VariantToManagedArgPtr(value, mProperty->GetType(), failed), nullptr); mProperty->SetValue(instanceObject, MUtils::VariantToManagedArgPtr(value, mProperty->GetType(), failed), nullptr);
else else
value = MUtils::UnboxVariant(mProperty->GetValue(instanceObject, nullptr)); value = MUtils::UnboxVariant(mProperty->GetValue(instanceObject, nullptr), true);
return !failed; return !failed;
} }
} }

View File

@@ -134,7 +134,7 @@ void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value&
} }
// Extract result // Extract result
value = MUtils::UnboxVariant(result); value = MUtils::UnboxVariant(result, true);
context.ValueCache.Add(boxBase, value); context.ValueCache.Add(boxBase, value);
#endif #endif
} }

View File

@@ -74,7 +74,7 @@ namespace FlaxEngine.Interop
internal static IntPtr MarshalReturnValueString(ref string returnValue) internal static IntPtr MarshalReturnValueString(ref string returnValue)
{ {
return returnValue != null ? ManagedString.ToNativeWeak(returnValue) : IntPtr.Zero; return returnValue != null ? ManagedString.ToNative/*Weak*/(returnValue) : IntPtr.Zero;
} }
internal static IntPtr MarshalReturnValueManagedHandle(ref ManagedHandle returnValue) internal static IntPtr MarshalReturnValueManagedHandle(ref ManagedHandle returnValue)
@@ -157,7 +157,7 @@ namespace FlaxEngine.Interop
if (returnObject == null) if (returnObject == null)
return IntPtr.Zero; return IntPtr.Zero;
if (returnType == typeof(string)) if (returnType == typeof(string))
return ManagedString.ToNativeWeak(Unsafe.As<string>(returnObject)); return ManagedString.ToNative/*Weak*/(Unsafe.As<string>(returnObject));
if (returnType == typeof(ManagedHandle)) if (returnType == typeof(ManagedHandle))
return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnObject); return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnObject);
if (returnType == typeof(bool)) if (returnType == typeof(bool))
@@ -168,7 +168,7 @@ namespace FlaxEngine.Interop
return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As<Array>(returnObject)), GCHandleType.Weak); return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As<Array>(returnObject)), GCHandleType.Weak);
if (returnType.IsArray) if (returnType.IsArray)
return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As<Array>(returnObject)), GCHandleType.Weak); return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As<Array>(returnObject)), GCHandleType.Weak);
return ManagedHandle.ToIntPtr(returnObject, GCHandleType.Weak); return ManagedHandle.ToIntPtr(returnObject/*, GCHandleType.Weak*/);
} }
internal static IntPtr MarshalReturnValueThunk<TRet>(ref TRet returnValue) internal static IntPtr MarshalReturnValueThunk<TRet>(ref TRet returnValue)
@@ -181,7 +181,7 @@ namespace FlaxEngine.Interop
if (returnObject == null) if (returnObject == null)
return IntPtr.Zero; return IntPtr.Zero;
if (returnType == typeof(string)) if (returnType == typeof(string))
return ManagedString.ToNativeWeak(Unsafe.As<string>(returnObject)); return ManagedString.ToNative/*Weak*/(Unsafe.As<string>(returnObject));
if (returnType == typeof(IntPtr)) if (returnType == typeof(IntPtr))
return (IntPtr)(object)returnObject; return (IntPtr)(object)returnObject;
if (returnType == typeof(ManagedHandle)) if (returnType == typeof(ManagedHandle))
@@ -210,7 +210,7 @@ namespace FlaxEngine.Interop
return (IntPtr)new UIntPtr((ulong)(System.UInt32)(object)returnObject); return (IntPtr)new UIntPtr((ulong)(System.UInt32)(object)returnObject);
if (returnType == typeof(System.UInt64)) if (returnType == typeof(System.UInt64))
return (IntPtr)new UIntPtr((ulong)(System.UInt64)(object)returnObject); return (IntPtr)new UIntPtr((ulong)(System.UInt64)(object)returnObject);
return ManagedHandle.ToIntPtr(returnObject, GCHandleType.Weak); return ManagedHandle.ToIntPtr(returnObject/*, GCHandleType.Weak*/);
} }
#if !USE_AOT #if !USE_AOT

View File

@@ -1,5 +1,8 @@
// Copyright (c) Wojciech Figat. All rights reserved. // Copyright (c) Wojciech Figat. All rights reserved.
#define USE_CONCURRENT_DICT
#define USE_GCHANDLE
#if USE_NETCORE #if USE_NETCORE
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -8,6 +11,7 @@ using System.Runtime.InteropServices;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using FlaxEngine.Assertions; using FlaxEngine.Assertions;
using System.Collections.Concurrent;
#pragma warning disable 1591 #pragma warning disable 1591
@@ -356,18 +360,77 @@ namespace FlaxEngine.Interop
#endif #endif
public struct ManagedHandle public struct ManagedHandle
{ {
#if USE_GCHANDLE
private GCHandle handle;
private static HashSet<IntPtr> _weakHandles = new HashSet<nint>();
private static ConcurrentDictionary<IntPtr, string> _handles = new();
private static ConcurrentDictionary<IntPtr, string> _handles2 = new();
private ManagedHandle(IntPtr handle) => this.handle = GCHandle.FromIntPtr(handle);
private ManagedHandle(object value, GCHandleType type) //=> handle = GCHandle.Alloc(value, type);
{
if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection)
{
type = GCHandleType.Normal;
}
handle = GCHandle.Alloc(value, type);
if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection)
_weakHandles.Add((IntPtr)handle);
else if (_handles.Count < 14000)
_handles.TryAdd((IntPtr)handle, value?.GetType().FullName ?? "");
else
{
if (_handles2.Count > 12)
type = type;
if (value?.GetType() == typeof(string))
type = type;
_handles2.TryAdd((IntPtr)handle, value?.GetType().FullName ?? "");
}
}
#else
private IntPtr handle; private IntPtr handle;
private ManagedHandle(IntPtr handle) => this.handle = handle; private ManagedHandle(IntPtr handle) => this.handle = handle;
private ManagedHandle(object value, GCHandleType type) => handle = ManagedHandlePool.AllocateHandle(value, type); private ManagedHandle(object value, GCHandleType type) => handle = ManagedHandlePool.AllocateHandle(value, type);
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ManagedHandle Alloc(object value) => new ManagedHandle(value, GCHandleType.Normal); public static ManagedHandle Alloc(object value) => new ManagedHandle(value, GCHandleType.Normal);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ManagedHandle Alloc(object value, GCHandleType type) => new ManagedHandle(value, type); public static ManagedHandle Alloc(object value, GCHandleType type) => new ManagedHandle(value, type);
#if USE_GCHANDLE
public void Free()// => handle.Free();
{
if ((IntPtr)handle == IntPtr.Zero)
return;
if (_weakHandles.Remove((IntPtr)handle))
{
if (!handle.IsAllocated)
handle = handle;
handle = handle;
}
else if (_handles.Remove((IntPtr)handle, out _))
{
handle = handle;
}
else if (_handles2.Remove((IntPtr)handle, out _))
{
handle = handle;
}
handle.Free();
}
public object Target => handle.Target;
public bool IsAllocated => handle.IsAllocated;
#else
public void Free() public void Free()
{ {
if (handle == IntPtr.Zero) if (handle == IntPtr.Zero)
@@ -383,6 +446,7 @@ namespace FlaxEngine.Interop
} }
public bool IsAllocated => handle != IntPtr.Zero; public bool IsAllocated => handle != IntPtr.Zero;
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator ManagedHandle(IntPtr value) => FromIntPtr(value); public static explicit operator ManagedHandle(IntPtr value) => FromIntPtr(value);
@@ -393,6 +457,16 @@ namespace FlaxEngine.Interop
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator IntPtr(ManagedHandle value) => ToIntPtr(value); public static explicit operator IntPtr(ManagedHandle value) => ToIntPtr(value);
#if USE_GCHANDLE
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToIntPtr(object value) => (IntPtr)Alloc(value, GCHandleType.Normal).handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToIntPtr(object value, GCHandleType type) => (IntPtr)Alloc(value, type).handle;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToIntPtr(ManagedHandle value) => (IntPtr)value.handle;
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToIntPtr(object value) => ManagedHandlePool.AllocateHandle(value, GCHandleType.Normal); public static IntPtr ToIntPtr(object value) => ManagedHandlePool.AllocateHandle(value, GCHandleType.Normal);
@@ -401,6 +475,7 @@ namespace FlaxEngine.Interop
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ToIntPtr(ManagedHandle value) => value.handle; public static IntPtr ToIntPtr(ManagedHandle value) => value.handle;
#endif
public override int GetHashCode() => handle.GetHashCode(); public override int GetHashCode() => handle.GetHashCode();
@@ -412,6 +487,15 @@ namespace FlaxEngine.Interop
public static bool operator !=(ManagedHandle a, ManagedHandle b) => a.handle != b.handle; public static bool operator !=(ManagedHandle a, ManagedHandle b) => a.handle != b.handle;
#if USE_GCHANDLE
internal static class ManagedHandlePool
{
internal static void TryCollectWeakHandles(bool force = false)
{
}
}
#else
internal static class ManagedHandlePool internal static class ManagedHandlePool
{ {
private const int WeakPoolCollectionSizeThreshold = 10000000; private const int WeakPoolCollectionSizeThreshold = 10000000;
@@ -425,17 +509,31 @@ namespace FlaxEngine.Interop
// Dictionaries for storing the valid handles. // Dictionaries for storing the valid handles.
// Note: Using locks seems to be generally the fastest when adding or fetching from the dictionary. // Note: Using locks seems to be generally the fastest when adding or fetching from the dictionary.
// Concurrent dictionaries could also be considered, but they perform much slower when adding to the dictionary. // Concurrent dictionaries could also be considered, but they perform much slower when adding to the dictionary.
#if USE_CONCURRENT_DICT
private static ConcurrentDictionary<IntPtr, object> persistentPool = new();
private static ConcurrentDictionary<IntPtr, GCHandle> pinnedPool = new();
#else
private static Dictionary<IntPtr, object> persistentPool = new(); private static Dictionary<IntPtr, object> persistentPool = new();
private static Dictionary<IntPtr, GCHandle> pinnedPool = new(); private static Dictionary<IntPtr, GCHandle> pinnedPool = new();
private static Lock persistentPoolLock = new();
private static Lock pinnedPoolLock = new();
#endif
// TODO: Performance of pinned handles are poor at the moment due to GCHandle wrapping. // TODO: Performance of pinned handles are poor at the moment due to GCHandle wrapping.
// TODO: .NET8: Experiment with pinned arrays for faster pinning: https://github.com/dotnet/runtime/pull/89293 // TODO: .NET8: Experiment with pinned arrays for faster pinning: https://github.com/dotnet/runtime/pull/89293
// Manage double-buffered pool for weak handles in order to avoid collecting in-flight handles. // Manage double-buffered pool for weak handles in order to avoid collecting in-flight handles.
// Periodically when the pools are being accessed and conditions are met, the other pool is cleared and swapped. // Periodically when the pools are being accessed and conditions are met, the other pool is cleared and swapped.
private static Dictionary<IntPtr, object> weakPool = new(); private static int weakPoolSize = 1;
private static Dictionary<IntPtr, object> weakPoolOther = new(); private static int weakPoolOtherSize = 1;
private static object weakPoolLock = new object(); #if USE_CONCURRENT_DICT
private static ConcurrentDictionary<IntPtr, object> weakPool = new(-1, weakPoolSize);
private static ConcurrentDictionary<IntPtr, object> weakPoolOther = new(-1, weakPoolOtherSize);
#else
private static Dictionary<IntPtr, object> weakPool = new(weakPoolSize);
private static Dictionary<IntPtr, object> weakPoolOther = new(weakPoolOtherSize);
#endif
private static Lock weakPoolLock = new();
private static ulong nextWeakPoolCollection; private static ulong nextWeakPoolCollection;
private static int nextWeakPoolGCCollection; private static int nextWeakPoolGCCollection;
private static long lastWeakPoolCollectionTime; private static long lastWeakPoolCollectionTime;
@@ -466,6 +564,19 @@ namespace FlaxEngine.Interop
// Swap the pools and release the oldest pool for GC // Swap the pools and release the oldest pool for GC
(weakPool, weakPoolOther) = (weakPoolOther, weakPool); (weakPool, weakPoolOther) = (weakPoolOther, weakPool);
(weakPoolSize, weakPoolOtherSize) = (weakPoolOtherSize, weakPoolSize);
#if USE_CONCURRENT_DICT
if (weakPool.Count > weakPoolSize)
{
var newMax = Math.Max(Math.Max(weakPoolSize, weakPoolOtherSize), weakPool.Count);
//FlaxEditor.Editor.Log($"growth from {weakPoolSize} to {weakPool.Count}, max {newMax}");
weakPoolSize = newMax;
weakPool = new(-1, weakPoolSize);
}
//else if (weakPool.Count > 0)
// weakPool.Clear();
//else
#endif
weakPool.Clear(); weakPool.Clear();
} }
@@ -485,14 +596,43 @@ namespace FlaxEngine.Interop
internal static IntPtr AllocateHandle(object value, GCHandleType type) internal static IntPtr AllocateHandle(object value, GCHandleType type)
{ {
IntPtr handle = NewHandle(type); IntPtr handle = NewHandle(type);
#if USE_CONCURRENT_DICT
switch (type) switch (type)
{ {
case GCHandleType.Normal: case GCHandleType.Normal:
lock (persistentPool) //lock (persistentPoolLock)
persistentPool.Add(handle, value); persistentPool.TryAdd(handle, value);
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
// value = value;
break; break;
case GCHandleType.Pinned: case GCHandleType.Pinned:
lock (pinnedPool) //lock (pinnedPoolLock)
pinnedPool.TryAdd(handle, GCHandle.Alloc(value, GCHandleType.Pinned));
break;
case GCHandleType.Weak:
case GCHandleType.WeakTrackResurrection:
lock (weakPoolLock)
{
TryCollectWeakHandles();
//weakPool.TryAdd(handle, value);
weakPool[handle] = value;
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
// value = value;
}
break;
}
#else
switch (type)
{
case GCHandleType.Normal:
lock (persistentPoolLock)
persistentPool.Add(handle, value);
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
// value = value;
break;
case GCHandleType.Pinned:
lock (pinnedPoolLock)
pinnedPool.Add(handle, GCHandle.Alloc(value, GCHandleType.Pinned)); pinnedPool.Add(handle, GCHandle.Alloc(value, GCHandleType.Pinned));
break; break;
case GCHandleType.Weak: case GCHandleType.Weak:
@@ -501,25 +641,36 @@ namespace FlaxEngine.Interop
{ {
TryCollectWeakHandles(); TryCollectWeakHandles();
weakPool.Add(handle, value); weakPool.Add(handle, value);
//weakPool[handle] = value;
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
// value = value;
} }
break; break;
} }
#endif
return handle; return handle;
} }
internal static object GetObject(IntPtr handle) internal static object GetObject(IntPtr handle)
{ {
switch (GetHandleType(handle)) GCHandleType type = GetHandleType(handle);
switch (type)
{ {
case GCHandleType.Normal: case GCHandleType.Normal:
lock (persistentPool) #if !USE_CONCURRENT_DICT
lock (persistentPoolLock)
#endif
{ {
if (persistentPool.TryGetValue(handle, out object value)) if (persistentPool.TryGetValue(handle, out object value))
return value; return value;
} }
break; break;
case GCHandleType.Pinned: case GCHandleType.Pinned:
lock (pinnedPool) #if !USE_CONCURRENT_DICT
lock (pinnedPoolLock)
#endif
{ {
if (pinnedPool.TryGetValue(handle, out GCHandle gcHandle)) if (pinnedPool.TryGetValue(handle, out GCHandle gcHandle))
return gcHandle.Target; return gcHandle.Target;
@@ -537,15 +688,98 @@ namespace FlaxEngine.Interop
} }
break; break;
} }
throw new NativeInteropException("Invalid ManagedHandle"); throw new NativeInteropException($"Invalid ManagedHandle of type '{type}'");
} }
internal static void SetObject(IntPtr handle, object value) internal static void SetObject(IntPtr handle, object value)
{ {
switch (GetHandleType(handle)) GCHandleType type = GetHandleType(handle);
#if USE_CONCURRENT_DICT
switch (type)
{ {
case GCHandleType.Normal: case GCHandleType.Normal:
lock (persistentPool) //lock (persistentPoolLock)
{
//ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(persistentPool, handle);
if (persistentPool.TryGetValue(handle, out var oldValue))
{
if (persistentPool.TryUpdate(handle, value, oldValue))
//if (!Unsafe.IsNullRef(ref obj))
{
//obj = value;
return;
}
}
}
break;
case GCHandleType.Pinned:
//lock (pinnedPoolLock)
{
/*ref GCHandle gcHandle = ref CollectionsMarshal.GetValueRefOrNullRef(pinnedPool, handle);
if (!Unsafe.IsNullRef(ref gcHandle))
{
gcHandle.Target = value;
return;
}*/
if (pinnedPool.TryGetValue(handle, out var gcHandle))
{
gcHandle.Target = value;
//if (pinnedPool.TryUpdate(handle, value, oldValue))
//if (!Unsafe.IsNullRef(ref obj))
//{
//obj = value;
return;
}
//}
}
break;
case GCHandleType.Weak:
case GCHandleType.WeakTrackResurrection:
lock (weakPoolLock)
{
TryCollectWeakHandles();
if (weakPool.TryGetValue(handle, out var oldValue))
{
if (weakPool.TryUpdate(handle, value, oldValue))
//if (!Unsafe.IsNullRef(ref obj))
{
//obj = value;
return;
}
}
if (weakPoolOther.TryGetValue(handle, out oldValue))
{
if (weakPoolOther.TryUpdate(handle, value, oldValue))
//if (!Unsafe.IsNullRef(ref obj))
{
//obj = value;
return;
}
}
/*{
ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(weakPool, handle);
if (!Unsafe.IsNullRef(ref obj))
{
obj = value;
return;
}
}
{
ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(weakPoolOther, handle);
if (!Unsafe.IsNullRef(ref obj))
{
obj = value;
return;
}
}*/
}
break;
}
#else
switch (type)
{
case GCHandleType.Normal:
lock (persistentPoolLock)
{ {
ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(persistentPool, handle); ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(persistentPool, handle);
if (!Unsafe.IsNullRef(ref obj)) if (!Unsafe.IsNullRef(ref obj))
@@ -556,7 +790,7 @@ namespace FlaxEngine.Interop
} }
break; break;
case GCHandleType.Pinned: case GCHandleType.Pinned:
lock (pinnedPool) lock (pinnedPoolLock)
{ {
ref GCHandle gcHandle = ref CollectionsMarshal.GetValueRefOrNullRef(pinnedPool, handle); ref GCHandle gcHandle = ref CollectionsMarshal.GetValueRefOrNullRef(pinnedPool, handle);
if (!Unsafe.IsNullRef(ref gcHandle)) if (!Unsafe.IsNullRef(ref gcHandle))
@@ -590,22 +824,32 @@ namespace FlaxEngine.Interop
} }
break; break;
} }
throw new NativeInteropException("Invalid ManagedHandle"); #endif
throw new NativeInteropException($"Invalid ManagedHandle of type '{type}'");
} }
internal static void FreeHandle(IntPtr handle) internal static void FreeHandle(IntPtr handle)
{ {
switch (GetHandleType(handle)) GCHandleType type = GetHandleType(handle);
switch (type)
{ {
case GCHandleType.Normal: case GCHandleType.Normal:
lock (persistentPool) #if !USE_CONCURRENT_DICT
lock (persistentPoolLock)
#endif
{ {
if (persistentPool.Remove(handle)) if (persistentPool.Remove(handle, out _))
{
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
// value = value;
return; return;
}
} }
break; break;
case GCHandleType.Pinned: case GCHandleType.Pinned:
lock (pinnedPool) #if !USE_CONCURRENT_DICT
lock (pinnedPoolLock)
#endif
{ {
if (pinnedPool.Remove(handle, out GCHandle gcHandle)) if (pinnedPool.Remove(handle, out GCHandle gcHandle))
{ {
@@ -617,12 +861,19 @@ namespace FlaxEngine.Interop
case GCHandleType.Weak: case GCHandleType.Weak:
case GCHandleType.WeakTrackResurrection: case GCHandleType.WeakTrackResurrection:
lock (weakPoolLock) lock (weakPoolLock)
{
TryCollectWeakHandles(); TryCollectWeakHandles();
return; if (weakPool.Remove(handle, out _))
return;
else if (weakPoolOther.Remove(handle, out _))
return;
return;
}
} }
throw new NativeInteropException("Invalid ManagedHandle"); throw new NativeInteropException($"Invalid ManagedHandle of type '{type}'");
} }
} }
#endif
} }
} }

View File

@@ -59,8 +59,9 @@ namespace FlaxEngine.Interop
#endif #endif
public struct ManagedToNativeState public struct ManagedToNativeState
{ {
ManagedArray managedArray; ManagedArray _pooledManagedArray;
IntPtr handle; ManagedArray _managedArray;
ManagedHandle handle;
public void FromManaged(object managed) public void FromManaged(object managed)
{ {
@@ -73,29 +74,30 @@ namespace FlaxEngine.Interop
if (NativeInterop.ArrayFactory.GetMarshalledType(elementType) == elementType) if (NativeInterop.ArrayFactory.GetMarshalledType(elementType) == elementType)
{ {
// Use pooled managed array wrapper to be passed around as handle to it // Use pooled managed array wrapper to be passed around as handle to it
(ManagedHandle tmp, managedArray) = ManagedArray.WrapPooledArray(arr); (handle, _pooledManagedArray) = ManagedArray.WrapPooledArray(arr);
handle = ManagedHandle.ToIntPtr(tmp);
} }
else else
{ {
// Convert array contents to be properly accessed by the native code (as GCHandles array) // Convert array contents to be properly accessed by the native code (as GCHandles array)
managedArray = NativeInterop.ManagedArrayToGCHandleWrappedArray(arr); _managedArray = NativeInterop.ManagedArrayToGCHandleWrappedArray(arr);
handle = ManagedHandle.ToIntPtr(ManagedHandle.Alloc(managedArray)); handle = ManagedHandle.Alloc(_pooledManagedArray);
managedArray = null; // It's not pooled
} }
} }
else else
handle = ManagedHandle.ToIntPtr(managed, GCHandleType.Weak); handle = ManagedHandle.Alloc(managed/*, GCHandleType.Weak*/);
} }
public IntPtr ToUnmanaged() public IntPtr ToUnmanaged() => ManagedHandle.ToIntPtr(handle);
{
return handle;
}
public void Free() public void Free()
{ {
managedArray?.FreePooled(); if (_pooledManagedArray != null)
_pooledManagedArray.FreePooled();
else
{
handle.Free();
_managedArray?.Free();
}
} }
} }
@@ -109,6 +111,7 @@ namespace FlaxEngine.Interop
public static void Free(IntPtr unmanaged) public static void Free(IntPtr unmanaged)
{ {
ManagedHandle.FromIntPtr(unmanaged).Free();
} }
} }
@@ -409,7 +412,7 @@ namespace FlaxEngine.Interop
public static void Free(IntPtr unmanaged) public static void Free(IntPtr unmanaged)
{ {
//DictionaryMarshaller<T, U>.Free(unmanaged); // No need to free weak handles DictionaryMarshaller<T, U>.Free(unmanaged); // No need to free weak handles
} }
} }
@@ -678,7 +681,7 @@ namespace FlaxEngine.Interop
public static class NativeToManaged public static class NativeToManaged
{ {
public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed, GCHandleType.Weak); public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed/*, GCHandleType.Weak*/);
public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged); public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged);
} }
@@ -688,11 +691,11 @@ namespace FlaxEngine.Interop
public static class ManagedToNative public static class ManagedToNative
{ {
public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed, GCHandleType.Weak); public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed/*, GCHandleType.Weak*/);
public static void Free(IntPtr unmanaged) public static void Free(IntPtr unmanaged)
{ {
//ManagedString.Free(unmanaged); // No need to free weak handles ManagedString.Free(unmanaged); // No need to free weak handles
} }
} }

View File

@@ -9,6 +9,8 @@ using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Loader; using System.Runtime.Loader;
using System.Threading;
using FlaxEngine.Assertions;
using FlaxEngine.Utilities; using FlaxEngine.Utilities;
#pragma warning disable 1591 #pragma warning disable 1591
@@ -602,13 +604,13 @@ namespace FlaxEngine.Interop
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
internal static IntPtr NewStringUTF16(char* text, int length) internal static IntPtr NewStringUTF16(char* text, int length)
{ {
return ManagedString.ToNativeWeak(new string(new ReadOnlySpan<char>(text, length))); return ManagedString.ToNative/*Weak*/(new string(new ReadOnlySpan<char>(text, length)));
} }
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
internal static IntPtr NewStringUTF8(sbyte* text, int length) internal static IntPtr NewStringUTF8(sbyte* text, int length)
{ {
return ManagedString.ToNativeWeak(new string(text, 0, length, System.Text.Encoding.UTF8)); return ManagedString.ToNative/*Weak*/(new string(text, 0, length, System.Text.Encoding.UTF8));
} }
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
@@ -661,7 +663,7 @@ namespace FlaxEngine.Interop
{ {
Type type = Unsafe.As<TypeHolder>(typeHandle.Target); Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
object value = MarshalToManaged(valuePtr, type); object value = MarshalToManaged(valuePtr, type);
return ManagedHandle.Alloc(value, GCHandleType.Weak); return ManagedHandle.Alloc(value/*, GCHandleType.Weak*/);
} }
/// <summary> /// <summary>
@@ -714,7 +716,7 @@ namespace FlaxEngine.Interop
internal static IntPtr InvokeMethod(ManagedHandle instanceHandle, ManagedHandle methodHandle, IntPtr paramPtr, IntPtr exceptionPtr) internal static IntPtr InvokeMethod(ManagedHandle instanceHandle, ManagedHandle methodHandle, IntPtr paramPtr, IntPtr exceptionPtr)
{ {
MethodHolder methodHolder = Unsafe.As<MethodHolder>(methodHandle.Target); MethodHolder methodHolder = Unsafe.As<MethodHolder>(methodHandle.Target);
#if !USE_AOT #if false//#if !USE_AOT
if (methodHolder.TryGetDelegate(out var methodDelegate, out var methodDelegateContext)) if (methodHolder.TryGetDelegate(out var methodDelegate, out var methodDelegateContext))
{ {
// Fast path, invoke the method with minimal allocations // Fast path, invoke the method with minimal allocations
@@ -737,7 +739,7 @@ namespace FlaxEngine.Interop
// Slow path, method parameters needs to be stored in heap // Slow path, method parameters needs to be stored in heap
object returnObject; object returnObject;
int numParams = methodHolder.parameterTypes.Length; int numParams = methodHolder.parameterTypes.Length;
object[] methodParameters = new object[numParams]; object[] methodParameters = new object[numParams];//ObjectArrayPool.Rent(numParams);//new object[numParams];
for (int i = 0; i < numParams; i++) for (int i = 0; i < numParams; i++)
{ {
@@ -748,6 +750,17 @@ namespace FlaxEngine.Interop
try try
{ {
returnObject = methodHolder.method.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, methodParameters); returnObject = methodHolder.method.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, methodParameters);
// Marshal reference parameters back to original unmanaged references
for (int i = 0; i < numParams; i++)
{
Type parameterType = methodHolder.parameterTypes[i];
if (parameterType.IsByRef)
{
IntPtr nativePtr = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * i)).ToPointer());
MarshalToNative(methodParameters[i], nativePtr, parameterType.GetElementType());
}
}
} }
catch (Exception exception) catch (Exception exception)
{ {
@@ -762,19 +775,11 @@ namespace FlaxEngine.Interop
throw realException; throw realException;
return IntPtr.Zero; return IntPtr.Zero;
} }
finally
// Marshal reference parameters back to original unmanaged references
for (int i = 0; i < numParams; i++)
{ {
Type parameterType = methodHolder.parameterTypes[i]; //ObjectArrayPool.Return(methodParameters);
if (parameterType.IsByRef)
{
IntPtr nativePtr = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * i)).ToPointer());
MarshalToNative(methodParameters[i], nativePtr, parameterType.GetElementType());
}
} }
// Return value
return Invoker.MarshalReturnValueGeneric(methodHolder.returnType, returnObject); return Invoker.MarshalReturnValueGeneric(methodHolder.returnType, returnObject);
} }
} }
@@ -908,7 +913,7 @@ namespace FlaxEngine.Interop
if (File.Exists(pdbPath)) if (File.Exists(pdbPath))
{ {
// Load including debug symbols // Load including debug symbols
using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open); using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open, FileAccess.Read);
assembly = scriptingAssemblyLoadContext.LoadFromStream(stream, pdbStream); assembly = scriptingAssemblyLoadContext.LoadFromStream(stream, pdbStream);
} }
else else
@@ -998,6 +1003,8 @@ namespace FlaxEngine.Interop
Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Scripting AssemblyLoadContext was not unloaded."); Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Scripting AssemblyLoadContext was not unloaded.");
weakRef.Free(); weakRef.Free();
Assert.IsFalse(AssemblyLoadContext.All.Any(x => x.Name == "Flax"));
static bool IsHandleAlive(GCHandle weakRef) static bool IsHandleAlive(GCHandle weakRef)
{ {
// Checking the target in scope somehow holds a reference to it...? // Checking the target in scope somehow holds a reference to it...?
@@ -1026,6 +1033,7 @@ namespace FlaxEngine.Interop
// Clear all caches which might hold references to assemblies in collectible ALC // Clear all caches which might hold references to assemblies in collectible ALC
cachedDelegatesCollectible.Clear(); cachedDelegatesCollectible.Clear();
cachedDelegatesCollectible = new();
foreach (var pair in managedTypesCollectible) foreach (var pair in managedTypesCollectible)
pair.Value.handle.Free(); pair.Value.handle.Free();
managedTypesCollectible.Clear(); managedTypesCollectible.Clear();
@@ -1115,6 +1123,9 @@ namespace FlaxEngine.Interop
} }
} }
GC.Collect();
GC.WaitForPendingFinalizers();
// Unload the ALC // Unload the ALC
scriptingAssemblyLoadContext.Unload(); scriptingAssemblyLoadContext.Unload();
scriptingAssemblyLoadContext.Resolving -= OnScriptingAssemblyLoadContextResolving; scriptingAssemblyLoadContext.Resolving -= OnScriptingAssemblyLoadContextResolving;

View File

@@ -18,6 +18,7 @@ using System.Collections.Concurrent;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Buffers;
namespace FlaxEngine.Interop namespace FlaxEngine.Interop
{ {
@@ -1244,7 +1245,7 @@ namespace FlaxEngine.Interop
internal static void ToNativeString(ref string managedValue, IntPtr nativePtr) internal static void ToNativeString(ref string managedValue, IntPtr nativePtr)
{ {
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), ManagedString.ToNativeWeak(managedValue)); Unsafe.Write<IntPtr>(nativePtr.ToPointer(), ManagedString.ToNative/*Weak*/(managedValue));
} }
internal static void ToNativeType(ref Type managedValue, IntPtr nativePtr) internal static void ToNativeType(ref Type managedValue, IntPtr nativePtr)
@@ -1291,14 +1292,14 @@ namespace FlaxEngine.Interop
} }
else else
managedArray = ManagedArrayToGCHandleWrappedArray(arr); managedArray = ManagedArrayToGCHandleWrappedArray(arr);
managedPtr = ManagedHandle.ToIntPtr(managedArray, GCHandleType.Weak); managedPtr = ManagedHandle.ToIntPtr(managedArray/*, GCHandleType.Weak*/);
} }
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), managedPtr); Unsafe.Write<IntPtr>(nativePtr.ToPointer(), managedPtr);
} }
internal static void ToNative(ref T managedValue, IntPtr nativePtr) internal static void ToNative(ref T managedValue, IntPtr nativePtr)
{ {
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), managedValue != null ? ManagedHandle.ToIntPtr(managedValue, GCHandleType.Weak) : IntPtr.Zero); Unsafe.Write<IntPtr>(nativePtr.ToPointer(), managedValue != null ? ManagedHandle.ToIntPtr(managedValue/*, GCHandleType.Weak*/) : IntPtr.Zero);
} }
} }
@@ -1529,11 +1530,12 @@ namespace FlaxEngine.Interop
private static GCHandle[] pinnedBoxedValues = new GCHandle[256]; private static GCHandle[] pinnedBoxedValues = new GCHandle[256];
private static uint pinnedBoxedValuesPointer = 0; private static uint pinnedBoxedValuesPointer = 0;
private static (IntPtr ptr, int size)[] pinnedAllocations = new (IntPtr ptr, int size)[256]; private static (IntPtr ptr, int size)[] pinnedAllocations = new (IntPtr ptr, int size)[256];
private static Action[] pinnedNativeFreeDelegates = new Action[256];
private static uint pinnedAllocationsPointer = 0; private static uint pinnedAllocationsPointer = 0;
private delegate IntPtr UnboxerDelegate(object value, object converter); private delegate IntPtr UnboxerDelegate(object value, object toNativeConverter, object nativeFree);
private static ConcurrentDictionary<Type, (UnboxerDelegate deleg, object toNativeDeleg)> unboxers = new(1, 3); private static ConcurrentDictionary<Type, (UnboxerDelegate deleg, object/*Delegate*/ toNativeConverter, object nativeFree)> unboxers = new(1, 3);
private static MethodInfo unboxerMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointer), BindingFlags.Static | BindingFlags.NonPublic); 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); private static MethodInfo unboxerToNativeMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointerWithConverter), BindingFlags.Static | BindingFlags.NonPublic);
@@ -1546,8 +1548,12 @@ namespace FlaxEngine.Interop
var toNativeMethod = attr?.NativeType.GetMethod("ToNative", BindingFlags.Static | BindingFlags.NonPublic); var toNativeMethod = attr?.NativeType.GetMethod("ToNative", BindingFlags.Static | BindingFlags.NonPublic);
if (toNativeMethod != null) if (toNativeMethod != null)
{ {
tuple.deleg = unboxerToNativeMethod.MakeGenericMethod(type, toNativeMethod.ReturnType).CreateDelegate<UnboxerDelegate>(); Type internalType = toNativeMethod.ReturnType;
tuple.toNativeDeleg = toNativeMethod.CreateDelegate(typeof(ToNativeDelegate<,>).MakeGenericType(type, toNativeMethod.ReturnType)); tuple.deleg = unboxerToNativeMethod.MakeGenericMethod(type, internalType).CreateDelegate<UnboxerDelegate>();
tuple.toNativeConverter = toNativeMethod.CreateDelegate(typeof(ToNativeDelegate<,>).MakeGenericType(type, internalType));
MethodInfo freeNativeMethod = attr.NativeType.GetMethod("Free", BindingFlags.Static | BindingFlags.NonPublic);
tuple.nativeFree = freeNativeMethod?.CreateDelegate(typeof(Action<>).MakeGenericType(internalType));
} }
else else
{ {
@@ -1555,7 +1561,7 @@ namespace FlaxEngine.Interop
} }
tuple = unboxers.GetOrAdd(type, tuple); tuple = unboxers.GetOrAdd(type, tuple);
} }
return tuple.deleg(value, tuple.toNativeDeleg); return tuple.deleg(value, tuple.toNativeConverter, tuple.nativeFree);
} }
private static void PinValue(object value) private static void PinValue(object value)
@@ -1569,12 +1575,15 @@ namespace FlaxEngine.Interop
handle = GCHandle.Alloc(value, GCHandleType.Pinned); handle = GCHandle.Alloc(value, GCHandleType.Pinned);
} }
private static IntPtr PinValue<T>(T value) where T : struct private static IntPtr PinValue<T>(T value, out uint index) where T : struct
{ {
// Store the converted value in unmanaged memory so it will not be relocated by the garbage collector. // Store the converted value in unmanaged memory so it will not be relocated by the garbage collector.
int size = TypeHelpers<T>.MarshalSize; int size = TypeHelpers<T>.MarshalSize;
uint index = Interlocked.Increment(ref pinnedAllocationsPointer) % (uint)pinnedAllocations.Length; index = Interlocked.Increment(ref pinnedAllocationsPointer) % (uint)pinnedAllocations.Length;
ref (IntPtr ptr, int size) alloc = ref pinnedAllocations[index]; ref (IntPtr ptr, int size) alloc = ref pinnedAllocations[index];
Action freeNativeDelegate = pinnedNativeFreeDelegates[index];
if (freeNativeDelegate != null)
freeNativeDelegate();
if (alloc.size < size) if (alloc.size < size)
{ {
if (alloc.ptr != IntPtr.Zero) if (alloc.ptr != IntPtr.Zero)
@@ -1586,7 +1595,7 @@ namespace FlaxEngine.Interop
return alloc.ptr; return alloc.ptr;
} }
private static IntPtr UnboxPointer<T>(object value, object converter) where T : struct private static IntPtr UnboxPointer<T>(object value, object toNativeConverter, object nativeFreeDelegate) where T : struct
{ {
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) // Cannot pin structure with references if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) // Cannot pin structure with references
return IntPtr.Zero; return IntPtr.Zero;
@@ -1594,11 +1603,16 @@ namespace FlaxEngine.Interop
return new IntPtr(Unsafe.AsPointer(ref Unsafe.Unbox<T>(value))); return new IntPtr(Unsafe.AsPointer(ref Unsafe.Unbox<T>(value)));
} }
private static IntPtr UnboxPointerWithConverter<T, TInternal>(object value, object converter) where T : struct private static IntPtr UnboxPointerWithConverter<T, TInternal>(
where TInternal : struct object value, object nativeConverterDelegate, object nativeFreeDelegate) where T : struct
where TInternal : struct
{ {
ToNativeDelegate<T, TInternal> toNative = Unsafe.As<ToNativeDelegate<T, TInternal>>(converter); ToNativeDelegate<T, TInternal> toNative = Unsafe.As<ToNativeDelegate<T, TInternal>>(nativeConverterDelegate);
return PinValue<TInternal>(toNative(Unsafe.Unbox<T>(value))); TInternal nativeValue = toNative(Unsafe.Unbox<T>(value));
IntPtr pinnedPtr = PinValue<TInternal>(nativeValue, out uint index);
Action<TInternal> freeNative = Unsafe.As<Action<TInternal>>(nativeFreeDelegate);
//pinnedNativeFreeDelegates[index] = () => freeNative(nativeValue);
return pinnedPtr;
} }
} }
@@ -1743,25 +1757,37 @@ namespace FlaxEngine.Interop
internal static void InitMethods() internal static void InitMethods()
{ {
return;
MakeNewCustomDelegateFunc = MakeNewCustomDelegateFunc =
typeof(Expression).Assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers") typeof(Expression).Assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers")
.GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate<Func<Type[], Type>>(); .GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate<Func<Type[], Type>>();
MakeNewCustomDelegateFunc(new[] { typeof(void) });
#if FLAX_EDITOR #if FLAX_EDITOR
// Load System.Linq.Expressions assembly to collectible ALC. // Load System.Linq.Expressions assembly to collectible ALC.
// The dynamic assembly where delegates are stored is cached in the DelegateHelpers class, so we should // The dynamic assembly where delegates are stored is cached in the DelegateHelpers class, so we should
// use the DelegateHelpers in collectible ALC to make sure the delegates are also stored in the same ALC. // use the DelegateHelpers in collectible ALC to make sure the delegates are also stored in the same ALC.
Assembly assembly = scriptingAssemblyLoadContext.LoadFromAssemblyPath(typeof(Expression).Assembly.Location); //using var _ = scriptingAssemblyLoadContext.EnterContextualReflection();
//Debug.Logger.LogHandler.LogWrite(LogType.Warning, "InitMethods.");
var asdfa = AssemblyLoadContext.All.ToArray();
var asma = typeof(Expression).Assembly;
var loc = asma.Location;
using FileStream stream = new FileStream(loc, FileMode.Open, FileAccess.Read);
Assembly assembly = scriptingAssemblyLoadContext.LoadFromAssemblyPath(loc);
MakeNewCustomDelegateFuncCollectible = MakeNewCustomDelegateFuncCollectible =
assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers") assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers")
.GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate<Func<Type[], Type>>(); .GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate<Func<Type[], Type>>();
// Create dummy delegates to force the dynamic Snippets assembly to be loaded in correcet ALCs Debug.Logger.LogHandler.LogWrite(LogType.Warning, "InitMethods ok .");
MakeNewCustomDelegateFunc(new[] { typeof(void) });
{ {
// Ensure the new delegate is placed in the collectible ALC // Ensure any future delegates is placed in the collectible ALC
using var ctx = scriptingAssemblyLoadContext.EnterContextualReflection(); using var ctx = scriptingAssemblyLoadContext.EnterContextualReflection();
MakeNewCustomDelegateFuncCollectible(new[] { typeof(void) }); var ret = MakeNewCustomDelegateFuncCollectible(new[] { typeof(void) });
Assert.IsTrue(ret.IsCollectible);
} }
#endif #endif
} }
@@ -1785,6 +1811,102 @@ namespace FlaxEngine.Interop
#endif #endif
} }
internal static class ObjectArrayPool
{
[ThreadStatic]
private static List<(bool InUse, object[] Array)> _pool;
[ThreadStatic]
private static List<(bool InUse, object[] Array)> _pool2;
/// <summary>
/// Rents an array from the pool.
/// </summary>
/// <param name="size">Exact size of the array.</param>
internal static object[] Rent(int size)
{
if (size == 0)
return Array.Empty<object>();
if (_pool == null)
_pool = new(16);
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool))
{
if (tuple.InUse)
continue;
if (tuple.Array.Length != size)
continue;
tuple.InUse = true;
return tuple.Array;
}
var newTuple = (InUse: true, Array: new object[size]);
_pool.Add(newTuple);
return newTuple.Array;
}
internal static object[] Rent2()
{
if (_pool2 == null)
_pool2 = new(16);
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool2))
{
if (tuple.InUse)
continue;
tuple.InUse = true;
return tuple.Array;
}
var newTuple = (InUse: true, Array: new object[8]);
_pool2.Add(newTuple);
return newTuple.Array;
}
/// <summary>
/// Returns the rented object array back to the pool.
/// </summary>
/// <param name="array">The array rented from the pool.</param>
internal static void Return(object[] array)
{
if (array.Length == 0)
return;
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool))
{
if (tuple.Array != array)
continue;
tuple.InUse = false;
for (int i = 0; i < array.Length; i++)
array[i] = null;
return;
}
throw new NativeInteropException("Tried to free non-pooled object array as pooled ManagedArray");
}
internal static void Return2(object[] array)
{
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool2))
{
if (tuple.Array != array)
continue;
tuple.InUse = false;
for (int i = 0; i < array.Length; i++)
array[i] = null;
return;
}
throw new NativeInteropException("Tried to free non-pooled object array as pooled ManagedArray");
}
}
#if !USE_AOT #if !USE_AOT
/// <summary> /// <summary>
/// Wrapper class for invoking function pointers from unmanaged code. /// Wrapper class for invoking function pointers from unmanaged code.
@@ -1792,13 +1914,18 @@ namespace FlaxEngine.Interop
internal class ThunkContext internal class ThunkContext
{ {
internal MethodInfo method; internal MethodInfo method;
internal MethodInvoker invoker;
internal Type[] parameterTypes; internal Type[] parameterTypes;
internal Invoker.InvokeThunkDelegate methodDelegate; internal Invoker.InvokeThunkDelegate methodDelegate;
internal object methodDelegateContext; internal object methodDelegateContext;
internal static object[] objectPool = new object[128];
internal static int objectPoolIndex = 0;
internal ThunkContext(MethodInfo method) internal ThunkContext(MethodInfo method)
{ {
this.method = method; this.method = method;
invoker = MethodInvoker.Create(method);
parameterTypes = method.GetParameterTypes(); parameterTypes = method.GetParameterTypes();
// Thunk delegates don't support IsByRef parameters (use generic invocation that handles 'out' and 'ref' prams) // Thunk delegates don't support IsByRef parameters (use generic invocation that handles 'out' and 'ref' prams)
@@ -1826,6 +1953,7 @@ namespace FlaxEngine.Interop
genericParamTypes.Add(type); genericParamTypes.Add(type);
} }
#if false
string invokerTypeName = $"{typeof(Invoker).FullName}+Invoker{(method.IsStatic ? "Static" : "")}{(method.ReturnType != typeof(void) ? "Ret" : "NoRet")}{parameterTypes.Length}{(genericParamTypes.Count > 0 ? "`" + genericParamTypes.Count : "")}"; string invokerTypeName = $"{typeof(Invoker).FullName}+Invoker{(method.IsStatic ? "Static" : "")}{(method.ReturnType != typeof(void) ? "Ret" : "NoRet")}{parameterTypes.Length}{(genericParamTypes.Count > 0 ? "`" + genericParamTypes.Count : "")}";
Type invokerType = Type.GetType(invokerTypeName); Type invokerType = Type.GetType(invokerTypeName);
if (invokerType != null) if (invokerType != null)
@@ -1838,11 +1966,13 @@ namespace FlaxEngine.Interop
if (methodDelegate != null) if (methodDelegate != null)
Assert.IsTrue(methodDelegateContext != null); Assert.IsTrue(methodDelegateContext != null);
#endif
} }
public IntPtr InvokeThunk(ManagedHandle instanceHandle, IntPtr param1, IntPtr param2, IntPtr param3, IntPtr param4, IntPtr param5, IntPtr param6, IntPtr param7) public IntPtr InvokeThunk(ManagedHandle instanceHandle, IntPtr param1, IntPtr param2, IntPtr param3, IntPtr param4, IntPtr param5, IntPtr param6, IntPtr param7)
{ {
IntPtr* nativePtrs = stackalloc IntPtr[] { param1, param2, param3, param4, param5, param6, param7 }; IntPtr* nativePtrs = stackalloc IntPtr[] { param1, param2, param3, param4, param5, param6, param7 };
#if false
if (methodDelegate != null) if (methodDelegate != null)
{ {
IntPtr returnValue; IntPtr returnValue;
@@ -1861,36 +1991,69 @@ namespace FlaxEngine.Interop
return returnValue; return returnValue;
} }
else else
#endif
{ {
// The parameters are wrapped (boxed) in GCHandles // The parameters are wrapped (boxed) in GCHandles
object returnObject; object returnObject;
int numParams = parameterTypes.Length; int numParams = parameterTypes.Length;
object[] methodParameters = new object[numParams]; //object[] methodParameters = ObjectArrayPool.Rent(numParams);//new object[numParams]; // TODO: object array pool
if (objectPoolIndex + numParams > 128)
objectPoolIndex = 0;
var paramSpan = objectPool.AsSpan(objectPoolIndex, numParams);
objectPoolIndex += numParams;
for (int i = 0; i < numParams; i++) for (int i = 0; i < numParams; i++)
{ {
IntPtr nativePtr = nativePtrs[i]; IntPtr nativePtr = nativePtrs[i];
object managed = null;
if (nativePtr != IntPtr.Zero) if (nativePtr != IntPtr.Zero)
{ {
object managed = null;
Type type = parameterTypes[i]; Type type = parameterTypes[i];
Type elementType = type.GetElementType();
if (type.IsByRef) if (type.IsByRef)
{ {
// References use indirection to support value returning // References use indirection to support value returning
nativePtr = Unsafe.Read<IntPtr>(nativePtr.ToPointer()); nativePtr = Unsafe.Read<IntPtr>(nativePtr.ToPointer());
type = elementType; type = type.GetElementType();
} }
if (type.IsArray) if (type.IsArray)
managed = MarshalToManaged(nativePtr, type); // Array might be in internal format of custom structs so unbox if need to managed = MarshalToManaged(nativePtr, type); // Array might be in internal format of custom structs so unbox if need to
else if (type == typeof(Type))
managed = Unsafe.As<TypeHolder>(ManagedHandle.FromIntPtr(nativePtr).Target).type;
else else
managed = ManagedHandle.FromIntPtr(nativePtr).Target; managed = ManagedHandle.FromIntPtr(nativePtr).Target;
paramSpan[i] = managed;
} }
methodParameters[i] = managed;
} }
try try
{ {
returnObject = method.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, methodParameters); returnObject = invoker.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, paramSpan);
//returnObject = method.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, methodParameters);
// Marshal reference parameters back to original unmanaged references
for (int i = 0; i < numParams; i++)
{
IntPtr nativePtr = nativePtrs[i];
Type type = parameterTypes[i];
if (nativePtr != IntPtr.Zero && type.IsByRef)
{
object managed = paramSpan[i];
type = type.GetElementType();
if (managed == null)
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), IntPtr.Zero);
else if (type.IsArray)
MarshalToNative(managed, nativePtr, type);
else
{
var ptr = nativePtr.ToPointer();
if (type == typeof(Type))
managed = GetTypeHolder(Unsafe.As<Type>(managed));
var handle = ManagedHandle.Alloc(managed/*, GCHandleType.Weak*/);
var handleptr = ManagedHandle.ToIntPtr(handle);
Unsafe.Write<IntPtr>(ptr, handleptr);
//Unsafe.Write<IntPtr>(nativePtr.ToPointer(), ManagedHandle.ToIntPtr(ManagedHandle.Alloc(managed, GCHandleType.Weak)));
}
}
}
} }
catch (Exception exception) catch (Exception exception)
{ {
@@ -1900,32 +2063,19 @@ namespace FlaxEngine.Interop
Unsafe.Write<IntPtr>(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(exception, GCHandleType.Weak)); Unsafe.Write<IntPtr>(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(exception, GCHandleType.Weak));
return IntPtr.Zero; return IntPtr.Zero;
} }
finally
// Marshal reference parameters back to original unmanaged references
for (int i = 0; i < numParams; i++)
{ {
IntPtr nativePtr = nativePtrs[i]; //ObjectArrayPool.Return(methodParameters);
Type type = parameterTypes[i]; for (int i = 0; i < numParams; i++)
object managed = methodParameters[i]; paramSpan[i] = null;
if (nativePtr != IntPtr.Zero && type.IsByRef)
{
type = type.GetElementType();
if (managed == null)
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), IntPtr.Zero);
else if (type.IsArray)
MarshalToNative(managed, nativePtr, type);
else
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), ManagedHandle.ToIntPtr(ManagedHandle.Alloc(managed, GCHandleType.Weak)));
}
} }
// Return value
return Invoker.MarshalReturnValueThunkGeneric(method.ReturnType, returnObject); return Invoker.MarshalReturnValueThunkGeneric(method.ReturnType, returnObject);
} }
} }
} }
#endif #endif
} }
internal class NativeInteropException : Exception internal class NativeInteropException : Exception
{ {

View File

@@ -74,7 +74,7 @@
MObject* exception = nullptr; \ MObject* exception = nullptr; \
auto resultObj = _method_##name->Invoke(GetManagedInstance(), params, &exception); \ auto resultObj = _method_##name->Invoke(GetManagedInstance(), params, &exception); \
if (resultObj) \ if (resultObj) \
result = (DragDropEffect)MUtils::Unbox<int32>(resultObj); \ result = (DragDropEffect)MUtils::Unbox<int32>(resultObj, true); \
END_INVOKE_EVENT(name) END_INVOKE_EVENT(name)
#else #else
#define INVOKE_EVENT(name, paramsCount, param0, param1, param2) #define INVOKE_EVENT(name, paramsCount, param0, param1, param2)

View File

@@ -1332,7 +1332,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
} }
// Unbox result // Unbox result
result = MUtils::UnboxVariant(resultObject); result = MUtils::UnboxVariant(resultObject, true);
#if 0 #if 0
// Helper method invocations logging // Helper method invocations logging
@@ -1366,7 +1366,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
paramValue.SetString(MUtils::ToString((MString*)param)); paramValue.SetString(MUtils::ToString((MString*)param));
break; break;
case VariantType::Object: case VariantType::Object:
paramValue = MUtils::UnboxVariant((MObject*)param); paramValue = MUtils::UnboxVariant((MObject*)param, true);
break; break;
case VariantType::Structure: case VariantType::Structure:
{ {
@@ -1383,7 +1383,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
{ {
MType* paramType = mMethod->GetParameterType(paramIdx); MType* paramType = mMethod->GetParameterType(paramIdx);
if (MCore::Type::IsReference(paramType) && MCore::Type::GetType(paramType) == MTypes::Object) if (MCore::Type::IsReference(paramType) && MCore::Type::GetType(paramType) == MTypes::Object)
paramValue = MUtils::UnboxVariant((MObject*)outParams[paramIdx]); paramValue = MUtils::UnboxVariant((MObject*)outParams[paramIdx], true);
break; break;
} }
} }
@@ -1527,7 +1527,7 @@ bool ManagedBinaryModule::GetFieldValue(void* field, const Variant& instance, Va
const auto mField = (MField*)field; const auto mField = (MField*)field;
resultObject = mField->GetValueBoxed(instanceObject); resultObject = mField->GetValueBoxed(instanceObject);
} }
result = MUtils::UnboxVariant(resultObject); result = MUtils::UnboxVariant(resultObject, true);
return false; return false;
#else #else
return true; return true;

View File

@@ -304,7 +304,7 @@ MTypeObject* MUtils::BoxVariantType(const VariantType& value)
return INTERNAL_TYPE_GET_OBJECT(mType); return INTERNAL_TYPE_GET_OBJECT(mType);
} }
Variant MUtils::UnboxVariant(MObject* value) Variant MUtils::UnboxVariant(MObject* value, bool releaseHandle)
{ {
if (value == nullptr) if (value == nullptr)
return Variant::Null; return Variant::Null;
@@ -345,7 +345,11 @@ Variant MUtils::UnboxVariant(MObject* value)
case MTypes::R8: case MTypes::R8:
return *static_cast<double*>(unboxed); return *static_cast<double*>(unboxed);
case MTypes::String: case MTypes::String:
{
if (releaseHandle)
MUtils::FreeManaged<String>(value);
return Variant(MUtils::ToString((MString*)value)); return Variant(MUtils::ToString((MString*)value));
}
case MTypes::Ptr: case MTypes::Ptr:
return *static_cast<void**>(unboxed); return *static_cast<void**>(unboxed);
case MTypes::ValueType: case MTypes::ValueType:
@@ -400,6 +404,8 @@ Variant MUtils::UnboxVariant(MObject* value)
{ {
Variant v; Variant v;
v.SetBlob(ptr, MCore::Array::GetLength((MArray*)value)); v.SetBlob(ptr, MCore::Array::GetLength((MArray*)value));
if (releaseHandle)
MUtils::FreeManaged<Array<byte>>(value);
return v; return v;
} }
const StringAnsiView fullname = arrayClass->GetFullName(); const StringAnsiView fullname = arrayClass->GetFullName();
@@ -508,7 +514,10 @@ Variant MUtils::UnboxVariant(MObject* value)
{ {
// Array of Objects // Array of Objects
for (int32 i = 0; i < array.Count(); i++) for (int32 i = 0; i < array.Count(); i++)
array[i] = UnboxVariant(((MObject**)ptr)[i]); array[i] = UnboxVariant(((MObject**)ptr)[i], releaseHandle);
if (releaseHandle)
MUtils::FreeManaged<Array<byte>>(value);
} }
return v; return v;
} }
@@ -527,10 +536,13 @@ Variant MUtils::UnboxVariant(MObject* value)
{ {
MObject* keyManaged = managedKeysPtr[i]; MObject* keyManaged = managedKeysPtr[i];
MObject* valueManaged = managed.GetValue(keyManaged); MObject* valueManaged = managed.GetValue(keyManaged);
native.Add(UnboxVariant(keyManaged), UnboxVariant(valueManaged)); native.Add(UnboxVariant(keyManaged, releaseHandle), UnboxVariant(valueManaged, releaseHandle));
} }
Variant v(MoveTemp(native)); Variant v(MoveTemp(native));
v.Type.SetTypeName(klass->GetFullName()); v.Type.SetTypeName(klass->GetFullName());
if (releaseHandle)
MCore::GCHandle::Free(*(MGCHandle*)&value);
//MUtils::FreeManaged<Dictionary<byte, byte>>(value);
return v; return v;
} }
break; break;
@@ -548,6 +560,8 @@ Variant MUtils::UnboxVariant(MObject* value)
v.Type = MoveTemp(VariantType(VariantType::Enum, fullname)); v.Type = MoveTemp(VariantType(VariantType::Enum, fullname));
// TODO: what about 64-bit enum? use enum size with memcpy // TODO: what about 64-bit enum? use enum size with memcpy
v.AsUint64 = *static_cast<uint32*>(MCore::Object::Unbox(value)); v.AsUint64 = *static_cast<uint32*>(MCore::Object::Unbox(value));
if (releaseHandle)
MUtils::FreeManaged<byte>(value);
return v; return v;
} }
if (klass->IsValueType()) if (klass->IsValueType())
@@ -565,10 +579,12 @@ Variant MUtils::UnboxVariant(MObject* value)
type.Struct.Unbox(v.AsBlob.Data, value); type.Struct.Unbox(v.AsBlob.Data, value);
return v; return v;
} }
return Variant(value);
} }
return Variant(value); auto variant = Variant(value);
if (releaseHandle)
MUtils::FreeManaged<byte>(value);
return variant;
} }
MObject* MUtils::BoxVariant(const Variant& value) MObject* MUtils::BoxVariant(const Variant& value)

View File

@@ -39,7 +39,7 @@ namespace MUtils
extern FLAXENGINE_API MTypeObject* BoxScriptingTypeHandle(const ScriptingTypeHandle& value); extern FLAXENGINE_API MTypeObject* BoxScriptingTypeHandle(const ScriptingTypeHandle& value);
extern FLAXENGINE_API VariantType UnboxVariantType(MType* type); extern FLAXENGINE_API VariantType UnboxVariantType(MType* type);
extern FLAXENGINE_API MTypeObject* BoxVariantType(const VariantType& value); extern FLAXENGINE_API MTypeObject* BoxVariantType(const VariantType& value);
extern FLAXENGINE_API Variant UnboxVariant(MObject* value); extern FLAXENGINE_API Variant UnboxVariant(MObject* value, bool releaseHandle = false);
extern FLAXENGINE_API MObject* BoxVariant(const Variant& value); extern FLAXENGINE_API MObject* BoxVariant(const Variant& value);
} }
@@ -51,8 +51,41 @@ struct MConverter
void Unbox(T& result, MObject* data); void Unbox(T& result, MObject* data);
void ToManagedArray(MArray* result, const Span<T>& data); void ToManagedArray(MArray* result, const Span<T>& data);
void ToNativeArray(Span<T>& result, const MArray* data); void ToNativeArray(Span<T>& result, const MArray* data);
void FreeManaged(MObject* data);
}; };
#if USE_NETCORE
// Special case for boolean values, the handles are fixed and should not be removed
template<>
struct MConverter<bool>
{
MObject* Box(const bool& data, const MClass* klass)
{
return MCore::Object::Box((void*)&data, klass);
}
void Unbox(bool& result, MObject* data)
{
if (data)
Platform::MemoryCopy(&result, MCore::Object::Unbox(data), sizeof(bool));
}
void ToManagedArray(MArray* result, const Span<bool>& data)
{
Platform::MemoryCopy(MCore::Array::GetAddress(result), data.Get(), data.Length() * sizeof(bool));
}
void ToNativeArray(Span<bool>& result, const MArray* data)
{
Platform::MemoryCopy(result.Get(), MCore::Array::GetAddress(data), result.Length() * sizeof(bool));
}
void FreeManaged(MObject* data)
{
}
};
#endif
// Converter for POD types (that can use raw memory copy). // Converter for POD types (that can use raw memory copy).
template<typename T> template<typename T>
struct MConverter<T, typename TEnableIf<TAnd<TIsPODType<T>, TNot<TIsBaseOf<class ScriptingObject, typename TRemovePointer<T>::Type>>>::Value>::Type> struct MConverter<T, typename TEnableIf<TAnd<TIsPODType<T>, TNot<TIsBaseOf<class ScriptingObject, typename TRemovePointer<T>::Type>>>::Value>::Type>
@@ -77,6 +110,11 @@ struct MConverter<T, typename TEnableIf<TAnd<TIsPODType<T>, TNot<TIsBaseOf<class
{ {
Platform::MemoryCopy(result.Get(), MCore::Array::GetAddress(data), result.Length() * sizeof(T)); Platform::MemoryCopy(result.Get(), MCore::Array::GetAddress(data), result.Length() * sizeof(T));
} }
void FreeManaged(MObject* data)
{
MCore::GCHandle::Free(*(MGCHandle*)&data);
}
}; };
// Converter for String. // Converter for String.
@@ -85,22 +123,22 @@ struct MConverter<String>
{ {
MObject* Box(const String& data, const MClass* klass) MObject* Box(const String& data, const MClass* klass)
{ {
#if USE_NETCORE /*#if USE_NETCORE
MString* str = MUtils::ToString(data); MString* str = MUtils::ToString(data);
return MCore::Object::Box(str, klass); return MCore::Object::Box(str, klass);
#else #else*/
return (MObject*)MUtils::ToString(data); return (MObject*)MUtils::ToString(data);
#endif //#endif
} }
void Unbox(String& result, MObject* data) void Unbox(String& result, MObject* data)
{ {
#if USE_NETCORE /*#if USE_NETCORE
MString* str = (MString*)MCore::Object::Unbox(data); MString* str = (MString*)MCore::Object::Unbox(data);
result = MUtils::ToString(str); result = MUtils::ToString(str);
#else #else*/
result = MUtils::ToString((MString*)data); result = MUtils::ToString((MString*)data);
#endif //#endif
} }
void ToManagedArray(MArray* result, const Span<String>& data) void ToManagedArray(MArray* result, const Span<String>& data)
@@ -120,6 +158,13 @@ struct MConverter<String>
for (int32 i = 0; i < result.Length(); i++) for (int32 i = 0; i < result.Length(); i++)
MUtils::ToString(dataPtr[i], result.Get()[i]); MUtils::ToString(dataPtr[i], result.Get()[i]);
} }
void FreeManaged(MObject* data)
{
//MString* str = (MString*)MCore::Object::Unbox(data);
//MCore::GCHandle::Free(*(MGCHandle*)&str);
MCore::GCHandle::Free(*(MGCHandle*)&data);
}
}; };
// Converter for StringAnsi. // Converter for StringAnsi.
@@ -153,6 +198,11 @@ struct MConverter<StringAnsi>
for (int32 i = 0; i < result.Length(); i++) for (int32 i = 0; i < result.Length(); i++)
MUtils::ToString(dataPtr[i], result.Get()[i]); MUtils::ToString(dataPtr[i], result.Get()[i]);
} }
void FreeManaged(MObject* data)
{
MCore::GCHandle::Free(*(MGCHandle*)&data);
}
}; };
// Converter for StringView. // Converter for StringView.
@@ -186,6 +236,11 @@ struct MConverter<StringView>
for (int32 i = 0; i < result.Length(); i++) for (int32 i = 0; i < result.Length(); i++)
MUtils::ToString(dataPtr[i], result.Get()[i]); MUtils::ToString(dataPtr[i], result.Get()[i]);
} }
void FreeManaged(MObject* data)
{
MCore::GCHandle::Free(*(MGCHandle*)&data);
}
}; };
// Converter for Variant. // Converter for Variant.
@@ -219,6 +274,10 @@ struct MConverter<Variant>
for (int32 i = 0; i < result.Length(); i++) for (int32 i = 0; i < result.Length(); i++)
result.Get()[i] = MUtils::UnboxVariant(dataPtr[i]); result.Get()[i] = MUtils::UnboxVariant(dataPtr[i]);
} }
void FreeManaged(MObject* data)
{
}
}; };
// Converter for Scripting Objects (collection of pointers). // Converter for Scripting Objects (collection of pointers).
@@ -252,6 +311,10 @@ struct MConverter<T*, typename TEnableIf<TIsBaseOf<class ScriptingObject, T>::Va
for (int32 i = 0; i < result.Length(); i++) for (int32 i = 0; i < result.Length(); i++)
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]); result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
} }
void FreeManaged(MObject* data)
{
}
}; };
// Converter for Scripting Objects (collection of values). // Converter for Scripting Objects (collection of values).
@@ -273,6 +336,10 @@ struct MConverter<T, typename TEnableIf<TIsBaseOf<class ScriptingObject, T>::Val
MCore::GC::WriteArrayRef(result, Span<MObject*>(objects, data.Length())); MCore::GC::WriteArrayRef(result, Span<MObject*>(objects, data.Length()));
Allocator::Free(objects); Allocator::Free(objects);
} }
void FreeManaged(MObject* data)
{
}
}; };
// Converter for ScriptingObject References. // Converter for ScriptingObject References.
@@ -309,6 +376,10 @@ struct MConverter<ScriptingObjectReference<T>>
for (int32 i = 0; i < result.Length(); i++) for (int32 i = 0; i < result.Length(); i++)
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]); result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
} }
void FreeManaged(MObject* data)
{
}
}; };
// Converter for Asset References. // Converter for Asset References.
@@ -345,6 +416,10 @@ struct MConverter<AssetReference<T>>
for (int32 i = 0; i < result.Length(); i++) for (int32 i = 0; i < result.Length(); i++)
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]); result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
} }
void FreeManaged(MObject* data)
{
}
}; };
// TODO: use MarshalAs=Guid on SoftAssetReference to pass guid over bindings and not load asset in glue code // TODO: use MarshalAs=Guid on SoftAssetReference to pass guid over bindings and not load asset in glue code
@@ -370,6 +445,10 @@ struct MConverter<SoftAssetReference<T>>
for (int32 i = 0; i < result.Length(); i++) for (int32 i = 0; i < result.Length(); i++)
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]); result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
} }
void FreeManaged(MObject* data)
{
}
}; };
// Converter for Array. // Converter for Array.
@@ -395,6 +474,10 @@ struct MConverter<Array<T>>
Span<T> resultSpan(result.Get(), length); Span<T> resultSpan(result.Get(), length);
converter.ToNativeArray(resultSpan, array); converter.ToNativeArray(resultSpan, array);
} }
void FreeManaged(MObject* data)
{
}
}; };
namespace MUtils namespace MUtils
@@ -435,6 +518,7 @@ namespace MUtils
/// <summary> /// <summary>
/// Unboxes MObject to the native value of the given type. /// Unboxes MObject to the native value of the given type.
/// </summary> /// </summary>
///
template<class T> template<class T>
T Unbox(MObject* object) T Unbox(MObject* object)
{ {
@@ -444,6 +528,32 @@ namespace MUtils
return result; return result;
} }
/// <summary>
/// Unboxes MObject to the native value of the given type.
/// </summary>
/// <param name="object">The object.</param>
/// <param name="releaseHandle">True if boxed managed handle should be released.</param>
template<class T>
T Unbox(MObject* object, bool releaseHandle)
{
MConverter<T> converter;
T result;
converter.Unbox(result, object);
if (releaseHandle)
converter.FreeManaged(object);
return result;
}
/// <summary>
/// Releases the managed resources of a boxed object.
/// </summary>
template<class T>
void FreeManaged(MObject* object)
{
MConverter<T> converter;
converter.FreeManaged(object);
}
/// <summary> /// <summary>
/// Links managed array data to the unmanaged BytesContainer. /// Links managed array data to the unmanaged BytesContainer.
/// </summary> /// </summary>

View File

@@ -883,7 +883,6 @@ bool MAssembly::LoadCorlib()
bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePath) bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePath)
{ {
// TODO: Use new hostfxr delegate load_assembly_bytes? (.NET 8+)
// Open .Net assembly // Open .Net assembly
static void* LoadAssemblyImagePtr = GetStaticMethodPointer(TEXT("LoadAssemblyImage")); static void* LoadAssemblyImagePtr = GetStaticMethodPointer(TEXT("LoadAssemblyImage"));
_handle = CallStaticMethod<void*, const Char*>(LoadAssemblyImagePtr, assemblyPath.Get()); _handle = CallStaticMethod<void*, const Char*>(LoadAssemblyImagePtr, assemblyPath.Get());

View File

@@ -38,7 +38,7 @@ TEST_CASE("Scripting")
CHECK(method); CHECK(method);
MObject* result = method->Invoke(nullptr, nullptr, nullptr); MObject* result = method->Invoke(nullptr, nullptr, nullptr);
CHECK(result); CHECK(result);
int32 resultValue = MUtils::Unbox<int32>(result); int32 resultValue = MUtils::Unbox<int32>(result, true);
CHECK(resultValue == 0); CHECK(resultValue == 0);
} }

View File

@@ -745,7 +745,7 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value)
StringAsANSI<40> fieldNameAnsi(*fieldName, fieldName.Length()); StringAsANSI<40> fieldNameAnsi(*fieldName, fieldName.Length());
auto field = mclass->GetField(fieldNameAnsi.Get()); auto field = mclass->GetField(fieldNameAnsi.Get());
if (field) if (field)
value = MUtils::UnboxVariant(field->GetValueBoxed(instance)); value = MUtils::UnboxVariant(field->GetValueBoxed(instance), true);
break; break;
} }
} }

View File

@@ -711,7 +711,12 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut) if (parameterInfo.IsOut)
contents.Append("out "); contents.Append("out ");
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller)) else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
contents.Append("ref "); {
if (parameterInfo.IsConst || parameterInfo.Type.IsConst)
contents.Append("in ");
else
contents.Append("ref ");
}
// Out parameters that need additional converting will be converted at the native side (eg. object reference) // Out parameters that need additional converting will be converted at the native side (eg. object reference)
if (parameterInfo.IsOut && !string.IsNullOrEmpty(GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller))) if (parameterInfo.IsOut && !string.IsNullOrEmpty(GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller)))
@@ -754,7 +759,12 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut) if (parameterInfo.IsOut)
contents.Append("out "); contents.Append("out ");
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller)) else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
contents.Append("ref "); {
if (parameterInfo.IsConst || parameterInfo.Type.IsConst)
contents.Append("in ");
else
contents.Append("/*xcvg1*/ ref ");
}
contents.Append(nativeType); contents.Append(nativeType);
contents.Append(' '); contents.Append(' ');
contents.Append(parameterInfo.Name); contents.Append(parameterInfo.Name);
@@ -802,7 +812,17 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut) if (parameterInfo.IsOut)
contents.Append("out "); contents.Append("out ");
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller)) else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
contents.Append("ref "); {
if (isSetter || parameterInfo.Type.IsConst)
contents.Append("in ");
else
{
//CanUpdate(BehaviorUpdateContext
if (parameterInfo.Name == "context" && functionInfo.Name == "CanUpdate")
separator = separator;
contents.Append("/*xcvg2*/ ref ");
}
}
var convertFunc = GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller); var convertFunc = GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller);
var paramName = isSetter ? "value" : parameterInfo.Name; var paramName = isSetter ? "value" : parameterInfo.Name;
@@ -836,7 +856,12 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut) if (parameterInfo.IsOut)
contents.Append("out "); contents.Append("out ");
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller)) else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
contents.Append("ref "); {
if (parameterInfo.IsConst ||parameterInfo.Type.IsConst)
contents.Append("in ");
else
contents.Append("ref ");
}
// Pass value // Pass value
contents.Append(parameterInfo.DefaultValue); contents.Append(parameterInfo.DefaultValue);
@@ -1369,7 +1394,12 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut) if (parameterInfo.IsOut)
contents.Append("out "); contents.Append("out ");
else if (parameterInfo.IsRef) else if (parameterInfo.IsRef)
contents.Append("ref "); {
if (parameterInfo.IsConst || parameterInfo.Type.IsConst)
contents.Append("in ");
else
contents.Append("/*faffaf1*/ ref ");
}
else if (parameterInfo.IsThis) else if (parameterInfo.IsThis)
contents.Append("this "); contents.Append("this ");
else if (parameterInfo.IsParams) else if (parameterInfo.IsParams)
@@ -1431,7 +1461,7 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut) if (parameterInfo.IsOut)
contents.Append("out "); contents.Append("out ");
else if (parameterInfo.IsRef) else if (parameterInfo.IsRef)
contents.Append("ref "); contents.Append("/*faffaf2*/ ref ");
else if (parameterInfo.IsThis) else if (parameterInfo.IsThis)
contents.Append("this "); contents.Append("this ");
else if (parameterInfo.IsParams) else if (parameterInfo.IsParams)
@@ -1522,8 +1552,8 @@ namespace Flax.Build.Bindings
public static class ManagedToNative public static class ManagedToNative
{ {
public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged)); public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged));
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero; public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => managed != null ? ManagedHandle.ToIntPtr(managed/*, GCHandleType.Weak*/) : IntPtr.Zero;
public static void Free(IntPtr unmanaged) {} public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.Free(unmanaged);
} }
#if FLAX_EDITOR #if FLAX_EDITOR
[HideInEditor] [HideInEditor]
@@ -1708,7 +1738,7 @@ namespace Flax.Build.Bindings
{ {
var managedType = GenerateCSharpNativeToManaged(buildData, marshalType.GenericArgs[0], structureInfo); var managedType = GenerateCSharpNativeToManaged(buildData, marshalType.GenericArgs[0], structureInfo);
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{managedType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;"); toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{managedType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;"); toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/) : IntPtr.Zero; // plaa 1");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}"); freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
// Permanent ScriptingObject handle is passed from native side, do not release it // Permanent ScriptingObject handle is passed from native side, do not release it
@@ -1717,7 +1747,7 @@ namespace Flax.Build.Bindings
else if (marshalType.Type == "ScriptingObject") else if (marshalType.Type == "ScriptingObject")
{ {
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<FlaxEngine.Object>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;"); toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<FlaxEngine.Object>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;"); toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/) : IntPtr.Zero; // plaa 2");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}"); freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
// Permanent ScriptingObject handle is passed from native side, do not release it // Permanent ScriptingObject handle is passed from native side, do not release it
@@ -1726,7 +1756,7 @@ namespace Flax.Build.Bindings
else if (marshalType.IsPtr && originalType != "IntPtr" && !originalType.EndsWith("*")) else if (marshalType.IsPtr && originalType != "IntPtr" && !originalType.EndsWith("*"))
{ {
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;"); toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;"); toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/) : IntPtr.Zero; // plaa 3");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}"); freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
// Permanent ScriptingObject handle is passed from native side, do not release it // Permanent ScriptingObject handle is passed from native side, do not release it
@@ -1735,7 +1765,7 @@ namespace Flax.Build.Bindings
else if (marshalType.Type == "Dictionary") else if (marshalType.Type == "Dictionary")
{ {
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;"); toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak);"); toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/);");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}"); freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}"); freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
} }
@@ -1757,7 +1787,7 @@ namespace Flax.Build.Bindings
{ {
// Array elements passed as GCHandles // Array elements passed as GCHandles
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>(Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target)) : null;"); toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>(Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target)) : null;");
toNativeContent.AppendLine($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(NativeInterop.ManagedArrayToGCHandleWrappedArray(managed.{fieldInfo.Name}), GCHandleType.Weak) : IntPtr.Zero;"); toNativeContent.AppendLine($"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<IntPtr> ptrs = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<IntPtr>(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}"); freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<IntPtr> ptrs = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<IntPtr>(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
// Permanent ScriptingObject handle is passed from native side, do not release it // Permanent ScriptingObject handle is passed from native side, do not release it
@@ -1775,7 +1805,7 @@ namespace Flax.Build.Bindings
else if (marshalType.Type == "Version") else if (marshalType.Type == "Version")
{ {
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;"); toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak);"); toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/);");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}"); freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}"); freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
} }
@@ -1824,66 +1854,77 @@ namespace Flax.Build.Bindings
toManagedContent.Append("return managed;"); toManagedContent.Append("return managed;");
} }
string hideInEditorAttribute = (buildData.Target != null & buildData.Target.IsEditor) ? "[HideInEditor]" : "";
string marshalManagedType = structureInfo.Name;
string marshalNativeType = $"{structureInfo.Name}Internal";
contents.AppendLine(string.Join(Environment.NewLine + indent, (indent + $$""" contents.AppendLine(string.Join(Environment.NewLine + indent, (indent + $$"""
/// <summary> /// <summary>
/// Marshaller for type <see cref="{{structureInfo.Name}}"/>. /// Marshaller for type <see cref="{{marshalManagedType}}"/>.
/// </summary> /// </summary>
{{InsertHideInEditorSection()}} {{hideInEditorAttribute}}
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerFullName}}.ManagedToNative))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerFullName}}.ManagedToNative))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerFullName}}.ManagedToNative))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerFullName}}.ManagedToNative))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementIn, typeof({{marshallerFullName}}.ManagedToNative))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ElementIn, typeof({{marshallerFullName}}.ManagedToNative))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedOut, typeof({{marshallerFullName}}.NativeToManaged))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ManagedToUnmanagedOut, typeof({{marshallerFullName}}.NativeToManaged))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedIn, typeof({{marshallerFullName}}.NativeToManaged))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.UnmanagedToManagedIn, typeof({{marshallerFullName}}.NativeToManaged))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementOut, typeof({{marshallerFullName}}.NativeToManaged))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ElementOut, typeof({{marshallerFullName}}.NativeToManaged))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedRef, typeof({{marshallerFullName}}.Bidirectional))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ManagedToUnmanagedRef, typeof({{marshallerFullName}}.Bidirectional))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedRef, typeof({{marshallerFullName}}.Bidirectional))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.UnmanagedToManagedRef, typeof({{marshallerFullName}}.Bidirectional))]
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementRef, typeof({{marshallerFullName}}))] [CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ElementRef, typeof({{marshallerFullName}}))]
{{GenerateCSharpAccessLevel(structureInfo.Access)}}static unsafe class {{marshallerName}} {{GenerateCSharpAccessLevel(structureInfo.Access)}}static unsafe class {{marshallerName}}
{ {
#pragma warning disable 1591 #pragma warning disable 1591
#pragma warning disable 618 #pragma warning disable 618
{{structContents.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}} {{structContents.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
{{InsertHideInEditorSection()}} {{hideInEditorAttribute}}
public static class NativeToManaged public static class NativeToManaged
{ {
public static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged); public static {{marshalManagedType}} ConvertToManaged({{marshalNativeType}} unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
public static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => {{marshallerFullName}}.ToNative(managed); public static {{marshalNativeType}} ConvertToUnmanaged({{marshalManagedType}} managed) => {{marshallerFullName}}.ToNative(managed);
public static void Free({{structureInfo.Name}}Internal unmanaged) public static void Free({{marshalNativeType}} unmanaged)
{ {
{{freeContents2.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}} {{freeContents2.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
} }
} }
{{InsertHideInEditorSection()}} {{hideInEditorAttribute}}
public static class ManagedToNative public static class ManagedToNative
{ {
public static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged); public static {{marshalManagedType}} ConvertToManaged({{marshalNativeType}} unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
public static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => {{marshallerFullName}}.ToNative(managed); public static {{marshalNativeType}} ConvertToUnmanaged({{marshalManagedType}} managed) => {{marshallerFullName}}.ToNative(managed);
public static void Free({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.Free(unmanaged); public static void Free({{marshalNativeType}} unmanaged) => {{marshallerFullName}}.Free(unmanaged);
} }
{{InsertHideInEditorSection()}} {{hideInEditorAttribute}}
public struct Bidirectional public ref struct Bidirectional
{ {
{{structureInfo.Name}} managed; {{marshalManagedType}} managed;
{{structureInfo.Name}}Internal unmanaged; {{marshalNativeType}} unmanaged;
public void FromManaged({{structureInfo.Name}} managed) => this.managed = managed; public void FromManaged({{marshalManagedType}} managed) => this.managed = managed;
public {{structureInfo.Name}}Internal ToUnmanaged() { unmanaged = {{marshallerFullName}}.ToNative(managed); return unmanaged; } public {{marshalNativeType}} ToUnmanaged() { unmanaged = {{marshallerFullName}}.ToNative(managed); return unmanaged; }
public void FromUnmanaged({{structureInfo.Name}}Internal unmanaged) => this.unmanaged = unmanaged; public void FromUnmanaged({{marshalNativeType}} unmanaged)
public {{structureInfo.Name}} ToManaged() { managed = {{marshallerFullName}}.ToManaged(unmanaged); return managed; } {
public void Free() => NativeToManaged.Free(unmanaged); if (!unmanaged.Equals(this.unmanaged))
{{marshallerName}}.Free(this.unmanaged); // Release temporary handles before replacing them with permanent handles
this.unmanaged = unmanaged;
}
public {{marshalManagedType}} ToManaged() { managed = {{marshallerFullName}}.ToManaged(unmanaged); return managed; }
public void Free()
{
NativeToManaged.Free(unmanaged);
}
} }
internal static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => ToManaged(unmanaged); internal static {{marshalManagedType}} ConvertToManaged({{marshalNativeType}} unmanaged) => ToManaged(unmanaged);
internal static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => ToNative(managed); internal static {{marshalNativeType}} ConvertToUnmanaged({{marshalManagedType}} managed) => ToNative(managed);
internal static void Free({{structureInfo.Name}}Internal unmanaged) internal static void Free({{marshalNativeType}} unmanaged)
{ {
{{freeContents.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}} {{freeContents.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
} }
internal static {{structureInfo.Name}} ToManaged({{structureInfo.Name}}Internal unmanaged) internal static {{marshalManagedType}} ToManaged({{marshalNativeType}} unmanaged)
{ {
{{toManagedContent.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}} {{toManagedContent.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
} }
internal static {{structureInfo.Name}}Internal ToNative({{structureInfo.Name}} managed) internal static {{marshalNativeType}} ToNative({{marshalManagedType}} managed)
{ {
{{toNativeContent.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}} {{toNativeContent.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
} }
@@ -1892,13 +1933,6 @@ namespace Flax.Build.Bindings
} }
""").Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries))); """).Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)));
string InsertHideInEditorSection()
{
return (buildData.Target != null & buildData.Target.IsEditor) ? $$"""
[HideInEditor]
""" : "";
}
PutStringBuilder(toManagedContent); PutStringBuilder(toManagedContent);
PutStringBuilder(toNativeContent); PutStringBuilder(toNativeContent);
PutStringBuilder(freeContents); PutStringBuilder(freeContents);
@@ -2231,7 +2265,7 @@ namespace Flax.Build.Bindings
if (parameterInfo.IsOut) if (parameterInfo.IsOut)
contents.Append("out "); contents.Append("out ");
else if (parameterInfo.IsRef) else if (parameterInfo.IsRef)
contents.Append("ref "); contents.Append("/*faffaf3*/ ref ");
else if (parameterInfo.IsThis) else if (parameterInfo.IsThis)
contents.Append("this "); contents.Append("this ");
else if (parameterInfo.IsParams) else if (parameterInfo.IsParams)

View File

@@ -119,7 +119,8 @@ namespace Flax.Build.Bindings
// Pass as pointer to local variable converted for managed runtime // Pass as pointer to local variable converted for managed runtime
if (paramType.IsPtr) if (paramType.IsPtr)
result = string.Format(nativeToManaged, '*' + paramName); result = string.Format(nativeToManaged, '*' + paramName);
contents.Append($" auto __param_{paramName} = {result};").AppendLine(); contents.Append($" auto __param_orig_{paramName} = {result};").AppendLine();
contents.Append($" auto __param_{paramName} = __param_orig_{paramName};").AppendLine();
result = $"&__param_{paramName}"; result = $"&__param_{paramName}";
useLocalVar = true; useLocalVar = true;
} }
@@ -998,6 +999,11 @@ namespace Flax.Build.Bindings
} }
#endif #endif
if (functionInfo.UniqueName == "SetView" && callerName == "SceneRenderTaskInternal")
callerName = callerName;
if (functionInfo.UniqueName == "SetDriveControl")
callerName = callerName;
// Setup function binding glue to ensure that wrapper method signature matches for C++ and C# // Setup function binding glue to ensure that wrapper method signature matches for C++ and C#
functionInfo.Glue = new FunctionInfo.GlueInfo functionInfo.Glue = new FunctionInfo.GlueInfo
{ {
@@ -1155,6 +1161,7 @@ namespace Flax.Build.Bindings
}, },
IsOut = parameterInfo.IsOut, IsOut = parameterInfo.IsOut,
IsRef = isRefOut || parameterInfo.Type.IsRef, IsRef = isRefOut || parameterInfo.Type.IsRef,
IsConst = true,
}); });
} }
#endif #endif
@@ -1557,7 +1564,8 @@ namespace Flax.Build.Bindings
if (paramIsRef) if (paramIsRef)
{ {
// Pass as pointer to value when using ref/out parameter // Pass as pointer to value when using ref/out parameter
contents.Append($" auto __param_{parameterInfo.Name} = {paramValue};").AppendLine(); contents.Append($" auto __param_orig_{parameterInfo.Name} = {paramValue};").AppendLine();
contents.Append($" auto __param_{parameterInfo.Name} = __param_orig_{parameterInfo.Name};").AppendLine();
paramValue = $"&__param_{parameterInfo.Name}"; paramValue = $"&__param_{parameterInfo.Name}";
useLocalVar = true; useLocalVar = true;
} }
@@ -1637,6 +1645,78 @@ namespace Flax.Build.Bindings
else if (!passAsParamPtr) else if (!passAsParamPtr)
paramValue = '*' + paramValue; paramValue = '*' + paramValue;
contents.Append($" {parameterInfo.Name} = {paramValue};").AppendLine(); contents.Append($" {parameterInfo.Name} = {paramValue};").AppendLine();
// Release temporary GCHandles
/*if (passAsParamPtr)
{
//contents.Append($" if (params[{i}] != (void*)&__param_{parameterInfo.Name})").AppendLine();
contents.Append($" {{").AppendLine();
contents.Append($" auto __param{i}_handle = *(MGCHandle*)&params[{i}];").AppendLine();
contents.Append($" MCore::GCHandle::Free(__param{i}_handle);").AppendLine();
contents.Append($" }}").AppendLine();
}
else
{
//contents.Append($" if (params[{i}] != (void*)&__param_{parameterInfo.Name})").AppendLine();
contents.Append($" {{").AppendLine();
contents.Append($" auto __param{i}_handle = *(MGCHandle*)&params[{i}];").AppendLine();
contents.Append($" MCore::GCHandle::Free(__param{i}_handle);").AppendLine();
contents.Append($" }}").AppendLine();
}*/
}
}
}
// Release GCHandles of boxed values
for (var i = 0; i < functionInfo.Parameters.Count; i++)
{
var parameterInfo = functionInfo.Parameters[i];
var paramValue = GenerateCppWrapperNativeToBox(buildData, parameterInfo.Type, classInfo, out var apiType, parameterInfo.Name);
var paramIsRef = parameterInfo.IsRef || parameterInfo.IsOut;
if (paramValue.Contains("MUtils::Box<")) // FIXME
{
if (paramIsRef)
{
//contents.Append($" MUtils::FreeManaged<{parameterInfo.Type.ToString(false)}>((MObject*)__param_{parameterInfo.Name});").AppendLine();
/*if (useThunk)
{
// Release the original handle
contents.Append($" if (__param_orig_{parameterInfo.Name} != __param_{parameterInfo.Name}) //asdf1a").AppendLine();
contents.Append($" MUtils::FreeManaged<{parameterInfo.Type.ToString(false)}>((MObject*)__param_{parameterInfo.Name});").AppendLine();
}*/
contents.Append($" MUtils::FreeManaged<{parameterInfo.Type.Type}>((MObject*)__param_{parameterInfo.Name});").AppendLine();
if (useThunk)
{
// Release the original handle
contents.Append($" if (__param_orig_{parameterInfo.Name} != __param_{ parameterInfo.Name})").AppendLine();
contents.Append($" MUtils::FreeManaged<{parameterInfo.Type.Type}>((MObject*)__param_orig_{parameterInfo.Name});").AppendLine();
}
}
else if (apiType != null && !apiType.IsInBuild)
{
// int: ispod, isvaluetype
// vector: ispod, isvaluetype, isstruct
// guid: ispod, isvaluetype, isstruct, isinbuild
contents.Append($" MUtils::FreeManaged<{parameterInfo.Type.Type}>((MObject*)params[{i}]); //asdf1b").AppendLine();
//contents.Append($" auto __param{i}_handle = *(MGCHandle*)&params[{i}]; // asdf1b").AppendLine();
//contents.Append($" ASSERT((((unsigned long long)__param{i}_handle & 0xC000000000000000) >> 62) == 0);").AppendLine();
//contents.Append($" MCore::GCHandle::Free(__param{i}_handle);").AppendLine();
}
else if (apiType != null && !apiType.IsValueType)
{
if (parameterInfo.Type.Type.ToLower().Contains("string"))
apiType = apiType;
contents.Append($" MUtils::FreeManaged<{parameterInfo.Type.Type}>((MObject*)params[{i}]); //asdf1d").AppendLine();
}
else //if (apiType != null)
{
if (parameterInfo.Type.Type.ToLower().Contains("string"))
apiType = apiType;
//contents.Append($" //asdf1c {parameterInfo.Type.Type}").AppendLine();
contents.Append($" auto __param{i}_handle = *(MGCHandle*)&params[{i}]; // asdf1c").AppendLine();
//contents.Append($" ASSERT((((unsigned long long)__param{i}_handle & 0xC000000000000000) >> 62) == 0);").AppendLine();
contents.Append($" MCore::GCHandle::Free(__param{i}_handle);").AppendLine();
} }
} }
} }
@@ -2092,6 +2172,22 @@ namespace Flax.Build.Bindings
else if (!passAsParamPtr) else if (!passAsParamPtr)
paramValue = '*' + paramValue; paramValue = '*' + paramValue;
contents.Append($" arg{i} = {paramValue};").AppendLine(); contents.Append($" arg{i} = {paramValue};").AppendLine();
// Release temporary GCHandles
if (CppParamsThatNeedConversion[i])
{
// Release the original handle
contents.Append($" if (&__param_orig_arg{i} != &__param_arg{i})").AppendLine();
contents.Append($" FreeManaged(({managedType})__param_arg{i});").AppendLine();
}
/*contents.Append($" FreeManaged(({managedType})__param_arg{i}); //hmm1b").AppendLine();
if (CppParamsThatNeedConversion[i])
{
// Release the original handle
contents.Append($" if (&__param_orig_arg{i} != &__param_arg{i})").AppendLine();
contents.Append($" FreeManaged(({managedType})__param_orig_arg{i});").AppendLine();
}*/
paramType.IsRef = true; paramType.IsRef = true;
} }
} }
@@ -2389,6 +2485,7 @@ namespace Flax.Build.Bindings
{ {
Type = fieldInfo.Type, Type = fieldInfo.Type,
Name = "value", Name = "value",
IsConst = true,
}, },
}, },
ReturnType = new TypeInfo ReturnType = new TypeInfo
@@ -2998,6 +3095,7 @@ namespace Flax.Build.Bindings
header.AppendLine("namespace {"); header.AppendLine("namespace {");
header.AppendFormat("{0} ToManaged(const {1}& value);", wrapperName, fullName).AppendLine(); header.AppendFormat("{0} ToManaged(const {1}& value);", wrapperName, fullName).AppendLine();
header.AppendFormat("{1} ToNative(const {0}& value);", wrapperName, fullName).AppendLine(); header.AppendFormat("{1} ToNative(const {0}& value);", wrapperName, fullName).AppendLine();
header.AppendFormat("void FreeManaged(const {0}& value);", wrapperName).AppendLine();
header.AppendLine("}"); header.AppendLine("}");
// Generate MConverter for a structure // Generate MConverter for a structure
@@ -3013,7 +3111,10 @@ namespace Flax.Build.Bindings
header.AppendFormat(" DLLEXPORT USED void Unbox({0}& result, MObject* data)", fullName).AppendLine(); header.AppendFormat(" DLLEXPORT USED void Unbox({0}& result, MObject* data)", fullName).AppendLine();
header.Append(" {").AppendLine(); header.Append(" {").AppendLine();
header.AppendFormat(" result = ToNative(*reinterpret_cast<{0}*>(MCore::Object::Unbox(data)));", wrapperName).AppendLine(); //header.AppendFormat(" result = ToNative(*reinterpret_cast<{0}*>(MCore::Object::Unbox(data)));", wrapperName).AppendLine();
header.AppendFormat(" auto managed = *reinterpret_cast<{0}*>(MCore::Object::Unbox(data));", wrapperName).AppendLine();
header.AppendFormat(" result = ToNative(managed);", wrapperName).AppendLine();
header.AppendFormat(" ::FreeManaged(managed);", wrapperName).AppendLine();
header.Append(" }").AppendLine(); header.Append(" }").AppendLine();
header.AppendFormat(" DLLEXPORT USED void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine(); header.AppendFormat(" DLLEXPORT USED void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine();
@@ -3033,6 +3134,13 @@ namespace Flax.Build.Bindings
header.Append(" for (int32 i = 0; i < result.Length(); i++)").AppendLine(); header.Append(" for (int32 i = 0; i < result.Length(); i++)").AppendLine();
header.Append(" result[i] = ToNative(dataPtr[i]);").AppendLine(); header.Append(" result[i] = ToNative(dataPtr[i]);").AppendLine();
header.Append(" }").AppendLine(); header.Append(" }").AppendLine();
header.AppendFormat(" void FreeManaged(MObject* data)", fullName).AppendLine();
header.Append(" {").AppendLine();
//header.AppendFormat(" auto managed = MCore::Object::Unbox(data);", wrapperName).AppendLine();
//header.AppendFormat(" ::FreeManaged(*reinterpret_cast<{0}*>(managed));", wrapperName).AppendLine();
header.AppendFormat(" MCore::GCHandle::Free(*(MGCHandle*)&data);", wrapperName).AppendLine();
header.Append(" }").AppendLine();
header.Append('}').Append(';').AppendLine(); header.Append('}').Append(';').AppendLine();
// Generate converting function native -> managed // Generate converting function native -> managed
@@ -3107,8 +3215,47 @@ namespace Flax.Build.Bindings
header.AppendFormat(" result.{0} = value.{0};", fieldInfo.Name).AppendLine(); header.AppendFormat(" result.{0} = value.{0};", fieldInfo.Name).AppendLine();
else else
header.AppendFormat(" result.{0} = {1};", fieldInfo.Name, string.Format(wrapper, string.Format("value.{0}", fieldInfo.Name))).AppendLine(); header.AppendFormat(" result.{0} = {1};", fieldInfo.Name, string.Format(wrapper, string.Format("value.{0}", fieldInfo.Name))).AppendLine();
/*if (wrapper.Contains("::ToNative(")) // FIXME
{
header.Append($" auto __param{fieldInfo.Name}_handle = *(MGCHandle*)&value.{fieldInfo.Name};").AppendLine();
header.Append($" ASSERT((((unsigned long long)__param{fieldInfo.Name}_handle & 0xC000000000000000) >> 62) == 0); // asdf3").AppendLine();
header.Append($" MCore::GCHandle::Free(__param{fieldInfo.Name}_handle);").AppendLine();
}*/
} }
header.Append(" return result;").AppendLine(); header.Append(" return result;").AppendLine();
header.Append('}').AppendLine();
// Generate release function for temporary managed handles
header.AppendLine();
header.AppendFormat("void FreeManaged(const {0}& value)", wrapperName).AppendLine();
header.Append('{').AppendLine();
for (var i = 0; i < fields.Count; i++)
{
var fieldInfo = fields[i];
if (fieldInfo.IsStatic || fieldInfo.IsConstexpr)
continue;
CppNonPodTypesConvertingGeneration = true;
var wrapper = GenerateCppWrapperManagedToNative(buildData, fieldInfo.Type, apiType, out var managedType, out var fieldApiType, null, out _);
CppNonPodTypesConvertingGeneration = false;
if (wrapper.Contains("::ToNative(")) // FIXME fieldApiType.IsScriptingObject
{
header.Append($" auto __param{fieldInfo.Name}_handle = *(MGCHandle*)&value.{fieldInfo.Name};").AppendLine();
//header.Append($" ASSERT((((unsigned long long)__param{fieldInfo.Name}_handle & 0xC000000000000000) >> 62) == 0); // asdf3").AppendLine();
header.Append($" if ((((unsigned long long)__param{fieldInfo.Name}_handle & 0xC000000000000000) >> 62) != 0) // asdf3").AppendLine();
header.Append($" __param{fieldInfo.Name}_handle = __param{fieldInfo.Name}_handle;").AppendLine();
header.Append($" MCore::GCHandle::Free(__param{fieldInfo.Name}_handle);").AppendLine();
}
}
if (classInfo != null)
{
header.Append($" auto __value_handle = *(MGCHandle*)&value;").AppendLine();
header.Append($" ASSERT((((unsigned long long)__value_handle & 0xC000000000000000) >> 62) == 0); // asdf4").AppendLine();
header.Append($" MCore::GCHandle::Free(__value_handle);").AppendLine();
}
header.Append('}').AppendLine(); header.Append('}').AppendLine();
header.AppendLine("}"); header.AppendLine("}");
} }
@@ -3187,6 +3334,11 @@ namespace Flax.Build.Bindings
header.AppendFormat(" Unbox(result[i], dataPtr[i]);", fullName).AppendLine(); header.AppendFormat(" Unbox(result[i], dataPtr[i]);", fullName).AppendLine();
header.Append(" }").AppendLine(); header.Append(" }").AppendLine();
header.AppendFormat(" void FreeManaged(MObject* data) // honk1", fullName).AppendLine();
header.Append(" {").AppendLine();
//header.Append(" ::FreeManaged(reinterpret_cast<OnlineUserManaged*>(MCore::Object::Unbox(data)));").AppendLine();
header.Append(" }").AppendLine();
header.Append('}').Append(';').AppendLine(); header.Append('}').Append(';').AppendLine();
} }
} }