_wip
This commit is contained in:
@@ -15,7 +15,7 @@ if errorlevel 1 goto BuildToolFailed
|
||||
|
||||
:: Build bindings for all editor configurations
|
||||
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
|
||||
echo Done!
|
||||
|
||||
@@ -334,7 +334,7 @@ Window* ManagedEditor::GetMainWindow()
|
||||
ASSERT(HasManagedInstance());
|
||||
const auto method = GetClass()->GetMethod("GetMainWindowPtr");
|
||||
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()
|
||||
@@ -346,7 +346,7 @@ bool ManagedEditor::CanReloadScripts()
|
||||
Internal_CanReloadScripts = GetClass()->GetMethod("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()
|
||||
@@ -365,7 +365,7 @@ bool ManagedEditor::CanAutoBuildCSG()
|
||||
Internal_CanAutoBuildCSG = GetClass()->GetMethod("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()
|
||||
@@ -384,7 +384,7 @@ bool ManagedEditor::CanAutoBuildNavMesh()
|
||||
Internal_CanAutoBuildNavMesh = GetClass()->GetMethod("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
|
||||
@@ -397,7 +397,7 @@ bool ManagedEditor::HasGameViewportFocus() const
|
||||
Internal_HasGameViewportFocus = GetClass()->GetMethod("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;
|
||||
}
|
||||
@@ -495,7 +495,7 @@ bool ManagedEditor::OnAppExit()
|
||||
Internal_OnAppExit = GetClass()->GetMethod("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()
|
||||
|
||||
@@ -12,6 +12,7 @@ using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Tabs;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Assertions;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
|
||||
@@ -972,6 +973,9 @@ namespace FlaxEditor.Windows
|
||||
_cloneProjectButton.Clicked -= OnCloneProjectButtonClicked;
|
||||
PluginManager.PluginsChanged -= OnPluginsChanged;
|
||||
|
||||
Assert.IsTrue(!_entries.Any());
|
||||
_entries.Clear();
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ bool AccessVariant(Variant& instance, const StringAnsiView& member, Variant& val
|
||||
if (set)
|
||||
mField->SetValue(instanceObject, MUtils::VariantToManagedArgPtr(value, mField->GetType(), failed));
|
||||
else
|
||||
value = MUtils::UnboxVariant(mField->GetValueBoxed(instanceObject));
|
||||
value = MUtils::UnboxVariant(mField->GetValueBoxed(instanceObject), true);
|
||||
return !failed;
|
||||
}
|
||||
else if (const auto mProperty = mClass->GetProperty(member.Get()))
|
||||
@@ -79,7 +79,7 @@ bool AccessVariant(Variant& instance, const StringAnsiView& member, Variant& val
|
||||
if (set)
|
||||
mProperty->SetValue(instanceObject, MUtils::VariantToManagedArgPtr(value, mProperty->GetType(), failed), nullptr);
|
||||
else
|
||||
value = MUtils::UnboxVariant(mProperty->GetValue(instanceObject, nullptr));
|
||||
value = MUtils::UnboxVariant(mProperty->GetValue(instanceObject, nullptr), true);
|
||||
return !failed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value&
|
||||
}
|
||||
|
||||
// Extract result
|
||||
value = MUtils::UnboxVariant(result);
|
||||
value = MUtils::UnboxVariant(result, true);
|
||||
context.ValueCache.Add(boxBase, value);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace FlaxEngine.Interop
|
||||
|
||||
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)
|
||||
@@ -157,7 +157,7 @@ namespace FlaxEngine.Interop
|
||||
if (returnObject == null)
|
||||
return IntPtr.Zero;
|
||||
if (returnType == typeof(string))
|
||||
return ManagedString.ToNativeWeak(Unsafe.As<string>(returnObject));
|
||||
return ManagedString.ToNative/*Weak*/(Unsafe.As<string>(returnObject));
|
||||
if (returnType == typeof(ManagedHandle))
|
||||
return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnObject);
|
||||
if (returnType == typeof(bool))
|
||||
@@ -168,7 +168,7 @@ namespace FlaxEngine.Interop
|
||||
return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As<Array>(returnObject)), GCHandleType.Weak);
|
||||
if (returnType.IsArray)
|
||||
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)
|
||||
@@ -181,7 +181,7 @@ namespace FlaxEngine.Interop
|
||||
if (returnObject == null)
|
||||
return IntPtr.Zero;
|
||||
if (returnType == typeof(string))
|
||||
return ManagedString.ToNativeWeak(Unsafe.As<string>(returnObject));
|
||||
return ManagedString.ToNative/*Weak*/(Unsafe.As<string>(returnObject));
|
||||
if (returnType == typeof(IntPtr))
|
||||
return (IntPtr)(object)returnObject;
|
||||
if (returnType == typeof(ManagedHandle))
|
||||
@@ -210,7 +210,7 @@ namespace FlaxEngine.Interop
|
||||
return (IntPtr)new UIntPtr((ulong)(System.UInt32)(object)returnObject);
|
||||
if (returnType == typeof(System.UInt64))
|
||||
return (IntPtr)new UIntPtr((ulong)(System.UInt64)(object)returnObject);
|
||||
return ManagedHandle.ToIntPtr(returnObject, GCHandleType.Weak);
|
||||
return ManagedHandle.ToIntPtr(returnObject/*, GCHandleType.Weak*/);
|
||||
}
|
||||
|
||||
#if !USE_AOT
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#define USE_CONCURRENT_DICT
|
||||
#define USE_GCHANDLE
|
||||
|
||||
#if USE_NETCORE
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -8,6 +11,7 @@ using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using FlaxEngine.Assertions;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
#pragma warning disable 1591
|
||||
|
||||
@@ -356,18 +360,77 @@ namespace FlaxEngine.Interop
|
||||
#endif
|
||||
public struct ManagedHandle
|
||||
{
|
||||
#if USE_GCHANDLE
|
||||
private GCHandle handle;
|
||||
|
||||
private static HashSet<IntPtr> _weakHandles = new HashSet<nint>();
|
||||
private static Dictionary<IntPtr, string> _handles = new();
|
||||
private static Dictionary<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.Add((IntPtr)handle, value?.GetType().FullName ?? "");
|
||||
else
|
||||
{
|
||||
if (_handles2.Count > 12)
|
||||
type = type;
|
||||
if (value?.GetType() == typeof(string))
|
||||
type = type;
|
||||
_handles2.Add((IntPtr)handle, value?.GetType().FullName ?? "");
|
||||
}
|
||||
}
|
||||
#else
|
||||
private IntPtr handle;
|
||||
|
||||
private ManagedHandle(IntPtr handle) => this.handle = handle;
|
||||
|
||||
private ManagedHandle(object value, GCHandleType type) => handle = ManagedHandlePool.AllocateHandle(value, type);
|
||||
|
||||
#endif
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ManagedHandle Alloc(object value) => new ManagedHandle(value, GCHandleType.Normal);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
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))
|
||||
{
|
||||
handle = handle;
|
||||
}
|
||||
else if (_handles2.Remove((IntPtr)handle))
|
||||
{
|
||||
handle = handle;
|
||||
}
|
||||
|
||||
handle.Free();
|
||||
}
|
||||
|
||||
|
||||
public object Target => handle.Target;
|
||||
|
||||
public bool IsAllocated => handle.IsAllocated;
|
||||
#else
|
||||
public void Free()
|
||||
{
|
||||
if (handle == IntPtr.Zero)
|
||||
@@ -383,6 +446,7 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
|
||||
public bool IsAllocated => handle != IntPtr.Zero;
|
||||
#endif
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator ManagedHandle(IntPtr value) => FromIntPtr(value);
|
||||
@@ -393,6 +457,16 @@ namespace FlaxEngine.Interop
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
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)]
|
||||
public static IntPtr ToIntPtr(object value) => ManagedHandlePool.AllocateHandle(value, GCHandleType.Normal);
|
||||
|
||||
@@ -401,6 +475,7 @@ namespace FlaxEngine.Interop
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IntPtr ToIntPtr(ManagedHandle value) => value.handle;
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
#if USE_GCHANDLE
|
||||
internal static class ManagedHandlePool
|
||||
{
|
||||
internal static void TryCollectWeakHandles(bool force = false)
|
||||
{
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
internal static class ManagedHandlePool
|
||||
{
|
||||
private const int WeakPoolCollectionSizeThreshold = 10000000;
|
||||
@@ -425,17 +509,31 @@ namespace FlaxEngine.Interop
|
||||
// Dictionaries for storing the valid handles.
|
||||
// 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.
|
||||
#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, 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: .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.
|
||||
// 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 Dictionary<IntPtr, object> weakPoolOther = new();
|
||||
private static object weakPoolLock = new object();
|
||||
private static int weakPoolSize = 1;
|
||||
private static int weakPoolOtherSize = 1;
|
||||
#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 int nextWeakPoolGCCollection;
|
||||
private static long lastWeakPoolCollectionTime;
|
||||
@@ -466,6 +564,19 @@ namespace FlaxEngine.Interop
|
||||
|
||||
// Swap the pools and release the oldest pool for GC
|
||||
(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();
|
||||
}
|
||||
|
||||
@@ -485,14 +596,43 @@ namespace FlaxEngine.Interop
|
||||
internal static IntPtr AllocateHandle(object value, GCHandleType type)
|
||||
{
|
||||
IntPtr handle = NewHandle(type);
|
||||
#if USE_CONCURRENT_DICT
|
||||
switch (type)
|
||||
{
|
||||
case GCHandleType.Normal:
|
||||
lock (persistentPool)
|
||||
persistentPool.Add(handle, value);
|
||||
//lock (persistentPoolLock)
|
||||
persistentPool.TryAdd(handle, value);
|
||||
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
|
||||
// value = value;
|
||||
break;
|
||||
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));
|
||||
break;
|
||||
case GCHandleType.Weak:
|
||||
@@ -501,25 +641,36 @@ namespace FlaxEngine.Interop
|
||||
{
|
||||
TryCollectWeakHandles();
|
||||
weakPool.Add(handle, value);
|
||||
//weakPool[handle] = value;
|
||||
|
||||
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
|
||||
// value = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return handle;
|
||||
}
|
||||
|
||||
internal static object GetObject(IntPtr handle)
|
||||
{
|
||||
switch (GetHandleType(handle))
|
||||
GCHandleType type = GetHandleType(handle);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case GCHandleType.Normal:
|
||||
lock (persistentPool)
|
||||
#if !USE_CONCURRENT_DICT
|
||||
lock (persistentPoolLock)
|
||||
#endif
|
||||
{
|
||||
if (persistentPool.TryGetValue(handle, out object value))
|
||||
return value;
|
||||
}
|
||||
break;
|
||||
case GCHandleType.Pinned:
|
||||
lock (pinnedPool)
|
||||
#if !USE_CONCURRENT_DICT
|
||||
lock (pinnedPoolLock)
|
||||
#endif
|
||||
{
|
||||
if (pinnedPool.TryGetValue(handle, out GCHandle gcHandle))
|
||||
return gcHandle.Target;
|
||||
@@ -537,15 +688,98 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
break;
|
||||
}
|
||||
throw new NativeInteropException("Invalid ManagedHandle");
|
||||
throw new NativeInteropException($"Invalid ManagedHandle of type '{type}'");
|
||||
}
|
||||
|
||||
internal static void SetObject(IntPtr handle, object value)
|
||||
{
|
||||
switch (GetHandleType(handle))
|
||||
GCHandleType type = GetHandleType(handle);
|
||||
#if USE_CONCURRENT_DICT
|
||||
switch (type)
|
||||
{
|
||||
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);
|
||||
if (!Unsafe.IsNullRef(ref obj))
|
||||
@@ -556,7 +790,7 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
break;
|
||||
case GCHandleType.Pinned:
|
||||
lock (pinnedPool)
|
||||
lock (pinnedPoolLock)
|
||||
{
|
||||
ref GCHandle gcHandle = ref CollectionsMarshal.GetValueRefOrNullRef(pinnedPool, handle);
|
||||
if (!Unsafe.IsNullRef(ref gcHandle))
|
||||
@@ -590,22 +824,32 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
break;
|
||||
}
|
||||
throw new NativeInteropException("Invalid ManagedHandle");
|
||||
#endif
|
||||
throw new NativeInteropException($"Invalid ManagedHandle of type '{type}'");
|
||||
}
|
||||
|
||||
internal static void FreeHandle(IntPtr handle)
|
||||
{
|
||||
switch (GetHandleType(handle))
|
||||
GCHandleType type = GetHandleType(handle);
|
||||
switch (type)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GCHandleType.Pinned:
|
||||
lock (pinnedPool)
|
||||
#if !USE_CONCURRENT_DICT
|
||||
lock (pinnedPoolLock)
|
||||
#endif
|
||||
{
|
||||
if (pinnedPool.Remove(handle, out GCHandle gcHandle))
|
||||
{
|
||||
@@ -617,12 +861,19 @@ namespace FlaxEngine.Interop
|
||||
case GCHandleType.Weak:
|
||||
case GCHandleType.WeakTrackResurrection:
|
||||
lock (weakPoolLock)
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,8 +59,9 @@ namespace FlaxEngine.Interop
|
||||
#endif
|
||||
public struct ManagedToNativeState
|
||||
{
|
||||
ManagedArray managedArray;
|
||||
IntPtr handle;
|
||||
ManagedArray _pooledManagedArray;
|
||||
ManagedArray _managedArray;
|
||||
ManagedHandle handle;
|
||||
|
||||
public void FromManaged(object managed)
|
||||
{
|
||||
@@ -73,29 +74,30 @@ namespace FlaxEngine.Interop
|
||||
if (NativeInterop.ArrayFactory.GetMarshalledType(elementType) == elementType)
|
||||
{
|
||||
// Use pooled managed array wrapper to be passed around as handle to it
|
||||
(ManagedHandle tmp, managedArray) = ManagedArray.WrapPooledArray(arr);
|
||||
handle = ManagedHandle.ToIntPtr(tmp);
|
||||
(handle, _pooledManagedArray) = ManagedArray.WrapPooledArray(arr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert array contents to be properly accessed by the native code (as GCHandles array)
|
||||
managedArray = NativeInterop.ManagedArrayToGCHandleWrappedArray(arr);
|
||||
handle = ManagedHandle.ToIntPtr(ManagedHandle.Alloc(managedArray));
|
||||
managedArray = null; // It's not pooled
|
||||
_managedArray = NativeInterop.ManagedArrayToGCHandleWrappedArray(arr);
|
||||
handle = ManagedHandle.Alloc(_pooledManagedArray);
|
||||
}
|
||||
}
|
||||
else
|
||||
handle = ManagedHandle.ToIntPtr(managed, GCHandleType.Weak);
|
||||
handle = ManagedHandle.Alloc(managed/*, GCHandleType.Weak*/);
|
||||
}
|
||||
|
||||
public IntPtr ToUnmanaged()
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
public IntPtr ToUnmanaged() => ManagedHandle.ToIntPtr(handle);
|
||||
|
||||
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)
|
||||
{
|
||||
ManagedHandle.FromIntPtr(unmanaged).Free();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,7 +412,7 @@ namespace FlaxEngine.Interop
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
@@ -688,11 +691,11 @@ namespace FlaxEngine.Interop
|
||||
public static class ManagedToNative
|
||||
{
|
||||
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); // No need to free weak handles
|
||||
ManagedString.Free(unmanaged); // No need to free weak handles
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Loader;
|
||||
using System.Threading;
|
||||
using FlaxEngine.Assertions;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
#pragma warning disable 1591
|
||||
@@ -602,13 +604,13 @@ namespace FlaxEngine.Interop
|
||||
[UnmanagedCallersOnly]
|
||||
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]
|
||||
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]
|
||||
@@ -661,7 +663,7 @@ namespace FlaxEngine.Interop
|
||||
{
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
object value = MarshalToManaged(valuePtr, type);
|
||||
return ManagedHandle.Alloc(value, GCHandleType.Weak);
|
||||
return ManagedHandle.Alloc(value/*, GCHandleType.Weak*/);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -714,7 +716,7 @@ namespace FlaxEngine.Interop
|
||||
internal static IntPtr InvokeMethod(ManagedHandle instanceHandle, ManagedHandle methodHandle, IntPtr paramPtr, IntPtr exceptionPtr)
|
||||
{
|
||||
MethodHolder methodHolder = Unsafe.As<MethodHolder>(methodHandle.Target);
|
||||
#if !USE_AOT
|
||||
#if false//#if !USE_AOT
|
||||
if (methodHolder.TryGetDelegate(out var methodDelegate, out var methodDelegateContext))
|
||||
{
|
||||
// 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
|
||||
object returnObject;
|
||||
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++)
|
||||
{
|
||||
@@ -748,6 +750,17 @@ namespace FlaxEngine.Interop
|
||||
try
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -762,19 +775,11 @@ namespace FlaxEngine.Interop
|
||||
throw realException;
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
// Marshal reference parameters back to original unmanaged references
|
||||
for (int i = 0; i < numParams; i++)
|
||||
finally
|
||||
{
|
||||
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());
|
||||
}
|
||||
//ObjectArrayPool.Return(methodParameters);
|
||||
}
|
||||
|
||||
// Return value
|
||||
return Invoker.MarshalReturnValueGeneric(methodHolder.returnType, returnObject);
|
||||
}
|
||||
}
|
||||
@@ -908,7 +913,7 @@ namespace FlaxEngine.Interop
|
||||
if (File.Exists(pdbPath))
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
else
|
||||
@@ -998,6 +1003,8 @@ namespace FlaxEngine.Interop
|
||||
Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Scripting AssemblyLoadContext was not unloaded.");
|
||||
weakRef.Free();
|
||||
|
||||
Assert.IsFalse(AssemblyLoadContext.All.Any(x => x.Name == "Flax"));
|
||||
|
||||
static bool IsHandleAlive(GCHandle weakRef)
|
||||
{
|
||||
// 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
|
||||
cachedDelegatesCollectible.Clear();
|
||||
cachedDelegatesCollectible = new();
|
||||
foreach (var pair in managedTypesCollectible)
|
||||
pair.Value.handle.Free();
|
||||
managedTypesCollectible.Clear();
|
||||
@@ -1115,6 +1123,9 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
}
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
|
||||
// Unload the ALC
|
||||
scriptingAssemblyLoadContext.Unload();
|
||||
scriptingAssemblyLoadContext.Resolving -= OnScriptingAssemblyLoadContextResolving;
|
||||
|
||||
@@ -18,6 +18,7 @@ using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Buffers;
|
||||
|
||||
namespace FlaxEngine.Interop
|
||||
{
|
||||
@@ -1244,7 +1245,7 @@ namespace FlaxEngine.Interop
|
||||
|
||||
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)
|
||||
@@ -1291,14 +1292,14 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
else
|
||||
managedArray = ManagedArrayToGCHandleWrappedArray(arr);
|
||||
managedPtr = ManagedHandle.ToIntPtr(managedArray, GCHandleType.Weak);
|
||||
managedPtr = ManagedHandle.ToIntPtr(managedArray/*, GCHandleType.Weak*/);
|
||||
}
|
||||
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), managedPtr);
|
||||
}
|
||||
|
||||
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 uint pinnedBoxedValuesPointer = 0;
|
||||
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 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 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);
|
||||
if (toNativeMethod != null)
|
||||
{
|
||||
tuple.deleg = unboxerToNativeMethod.MakeGenericMethod(type, toNativeMethod.ReturnType).CreateDelegate<UnboxerDelegate>();
|
||||
tuple.toNativeDeleg = toNativeMethod.CreateDelegate(typeof(ToNativeDelegate<,>).MakeGenericType(type, toNativeMethod.ReturnType));
|
||||
Type internalType = 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
|
||||
{
|
||||
@@ -1555,7 +1561,7 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
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)
|
||||
@@ -1569,12 +1575,15 @@ namespace FlaxEngine.Interop
|
||||
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.
|
||||
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];
|
||||
Action freeNativeDelegate = pinnedNativeFreeDelegates[index];
|
||||
if (freeNativeDelegate != null)
|
||||
freeNativeDelegate();
|
||||
if (alloc.size < size)
|
||||
{
|
||||
if (alloc.ptr != IntPtr.Zero)
|
||||
@@ -1586,7 +1595,7 @@ namespace FlaxEngine.Interop
|
||||
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
|
||||
return IntPtr.Zero;
|
||||
@@ -1594,11 +1603,16 @@ namespace FlaxEngine.Interop
|
||||
return new IntPtr(Unsafe.AsPointer(ref Unsafe.Unbox<T>(value)));
|
||||
}
|
||||
|
||||
private static IntPtr UnboxPointerWithConverter<T, TInternal>(object value, object converter) where T : struct
|
||||
where TInternal : struct
|
||||
private static IntPtr UnboxPointerWithConverter<T, TInternal>(
|
||||
object value, object nativeConverterDelegate, object nativeFreeDelegate) where T : struct
|
||||
where TInternal : struct
|
||||
{
|
||||
ToNativeDelegate<T, TInternal> toNative = Unsafe.As<ToNativeDelegate<T, TInternal>>(converter);
|
||||
return PinValue<TInternal>(toNative(Unsafe.Unbox<T>(value)));
|
||||
ToNativeDelegate<T, TInternal> toNative = Unsafe.As<ToNativeDelegate<T, TInternal>>(nativeConverterDelegate);
|
||||
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()
|
||||
{
|
||||
return;
|
||||
MakeNewCustomDelegateFunc =
|
||||
typeof(Expression).Assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers")
|
||||
.GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate<Func<Type[], Type>>();
|
||||
MakeNewCustomDelegateFunc(new[] { typeof(void) });
|
||||
|
||||
#if FLAX_EDITOR
|
||||
// Load System.Linq.Expressions assembly to collectible ALC.
|
||||
// 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.
|
||||
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 =
|
||||
assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers")
|
||||
.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
|
||||
MakeNewCustomDelegateFunc(new[] { typeof(void) });
|
||||
Debug.Logger.LogHandler.LogWrite(LogType.Warning, "InitMethods ok .");
|
||||
|
||||
{
|
||||
// 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();
|
||||
MakeNewCustomDelegateFuncCollectible(new[] { typeof(void) });
|
||||
var ret = MakeNewCustomDelegateFuncCollectible(new[] { typeof(void) });
|
||||
Assert.IsTrue(ret.IsCollectible);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1785,6 +1811,102 @@ namespace FlaxEngine.Interop
|
||||
#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
|
||||
/// <summary>
|
||||
/// Wrapper class for invoking function pointers from unmanaged code.
|
||||
@@ -1792,13 +1914,18 @@ namespace FlaxEngine.Interop
|
||||
internal class ThunkContext
|
||||
{
|
||||
internal MethodInfo method;
|
||||
internal MethodInvoker invoker;
|
||||
internal Type[] parameterTypes;
|
||||
internal Invoker.InvokeThunkDelegate methodDelegate;
|
||||
internal object methodDelegateContext;
|
||||
|
||||
internal static object[] objectPool = new object[128];
|
||||
internal static int objectPoolIndex = 0;
|
||||
|
||||
internal ThunkContext(MethodInfo method)
|
||||
{
|
||||
this.method = method;
|
||||
invoker = MethodInvoker.Create(method);
|
||||
parameterTypes = method.GetParameterTypes();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
#if false
|
||||
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);
|
||||
if (invokerType != null)
|
||||
@@ -1838,11 +1966,13 @@ namespace FlaxEngine.Interop
|
||||
|
||||
if (methodDelegate != 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)
|
||||
{
|
||||
IntPtr* nativePtrs = stackalloc IntPtr[] { param1, param2, param3, param4, param5, param6, param7 };
|
||||
#if false
|
||||
if (methodDelegate != null)
|
||||
{
|
||||
IntPtr returnValue;
|
||||
@@ -1861,36 +1991,69 @@ namespace FlaxEngine.Interop
|
||||
return returnValue;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// The parameters are wrapped (boxed) in GCHandles
|
||||
object returnObject;
|
||||
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++)
|
||||
{
|
||||
IntPtr nativePtr = nativePtrs[i];
|
||||
object managed = null;
|
||||
if (nativePtr != IntPtr.Zero)
|
||||
{
|
||||
object managed = null;
|
||||
Type type = parameterTypes[i];
|
||||
Type elementType = type.GetElementType();
|
||||
if (type.IsByRef)
|
||||
{
|
||||
// References use indirection to support value returning
|
||||
nativePtr = Unsafe.Read<IntPtr>(nativePtr.ToPointer());
|
||||
type = elementType;
|
||||
type = type.GetElementType();
|
||||
}
|
||||
if (type.IsArray)
|
||||
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
|
||||
managed = ManagedHandle.FromIntPtr(nativePtr).Target;
|
||||
paramSpan[i] = managed;
|
||||
}
|
||||
methodParameters[i] = managed;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -1900,32 +2063,19 @@ namespace FlaxEngine.Interop
|
||||
Unsafe.Write<IntPtr>(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(exception, GCHandleType.Weak));
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
// Marshal reference parameters back to original unmanaged references
|
||||
for (int i = 0; i < numParams; i++)
|
||||
finally
|
||||
{
|
||||
IntPtr nativePtr = nativePtrs[i];
|
||||
Type type = parameterTypes[i];
|
||||
object managed = methodParameters[i];
|
||||
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)));
|
||||
}
|
||||
//ObjectArrayPool.Return(methodParameters);
|
||||
for (int i = 0; i < numParams; i++)
|
||||
paramSpan[i] = null;
|
||||
}
|
||||
|
||||
// Return value
|
||||
return Invoker.MarshalReturnValueThunkGeneric(method.ReturnType, returnObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
internal class NativeInteropException : Exception
|
||||
{
|
||||
|
||||
@@ -450,7 +450,7 @@ public:
|
||||
/// <summary>
|
||||
/// The high-level renderer context. Used to collect the draw calls for the scene rendering. Can be used to perform a custom rendering.
|
||||
/// </summary>
|
||||
API_STRUCT(NoDefault) struct RenderContext
|
||||
API_STRUCT(NoDefault) struct FLAXENGINE_API RenderContext
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContext);
|
||||
|
||||
@@ -491,7 +491,7 @@ API_STRUCT(NoDefault) struct RenderContext
|
||||
/// <summary>
|
||||
/// The high-level renderer context batch that encapsulates multiple rendering requests within a single task (eg. optimize main view scene rendering and shadow projections at once).
|
||||
/// </summary>
|
||||
API_STRUCT(NoDefault) struct RenderContextBatch
|
||||
API_STRUCT(NoDefault) struct FLAXENGINE_API RenderContextBatch
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContextBatch);
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
MObject* exception = nullptr; \
|
||||
auto resultObj = _method_##name->Invoke(GetManagedInstance(), params, &exception); \
|
||||
if (resultObj) \
|
||||
result = (DragDropEffect)MUtils::Unbox<int32>(resultObj); \
|
||||
result = (DragDropEffect)MUtils::Unbox<int32>(resultObj, true); \
|
||||
END_INVOKE_EVENT(name)
|
||||
#else
|
||||
#define INVOKE_EVENT(name, paramsCount, param0, param1, param2)
|
||||
|
||||
@@ -1332,7 +1332,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
|
||||
}
|
||||
|
||||
// Unbox result
|
||||
result = MUtils::UnboxVariant(resultObject);
|
||||
result = MUtils::UnboxVariant(resultObject, true);
|
||||
|
||||
#if 0
|
||||
// Helper method invocations logging
|
||||
@@ -1366,7 +1366,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
|
||||
paramValue.SetString(MUtils::ToString((MString*)param));
|
||||
break;
|
||||
case VariantType::Object:
|
||||
paramValue = MUtils::UnboxVariant((MObject*)param);
|
||||
paramValue = MUtils::UnboxVariant((MObject*)param, true);
|
||||
break;
|
||||
case VariantType::Structure:
|
||||
{
|
||||
@@ -1383,7 +1383,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
|
||||
{
|
||||
MType* paramType = mMethod->GetParameterType(paramIdx);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1527,7 +1527,7 @@ bool ManagedBinaryModule::GetFieldValue(void* field, const Variant& instance, Va
|
||||
const auto mField = (MField*)field;
|
||||
resultObject = mField->GetValueBoxed(instanceObject);
|
||||
}
|
||||
result = MUtils::UnboxVariant(resultObject);
|
||||
result = MUtils::UnboxVariant(resultObject, true);
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
|
||||
@@ -304,7 +304,7 @@ MTypeObject* MUtils::BoxVariantType(const VariantType& value)
|
||||
return INTERNAL_TYPE_GET_OBJECT(mType);
|
||||
}
|
||||
|
||||
Variant MUtils::UnboxVariant(MObject* value)
|
||||
Variant MUtils::UnboxVariant(MObject* value, bool releaseHandle)
|
||||
{
|
||||
if (value == nullptr)
|
||||
return Variant::Null;
|
||||
@@ -345,7 +345,11 @@ Variant MUtils::UnboxVariant(MObject* value)
|
||||
case MTypes::R8:
|
||||
return *static_cast<double*>(unboxed);
|
||||
case MTypes::String:
|
||||
{
|
||||
if (releaseHandle)
|
||||
MUtils::FreeManaged<String>(value);
|
||||
return Variant(MUtils::ToString((MString*)value));
|
||||
}
|
||||
case MTypes::Ptr:
|
||||
return *static_cast<void**>(unboxed);
|
||||
case MTypes::ValueType:
|
||||
@@ -400,6 +404,8 @@ Variant MUtils::UnboxVariant(MObject* value)
|
||||
{
|
||||
Variant v;
|
||||
v.SetBlob(ptr, MCore::Array::GetLength((MArray*)value));
|
||||
if (releaseHandle)
|
||||
MUtils::FreeManaged<Array<byte>>(value);
|
||||
return v;
|
||||
}
|
||||
const StringAnsiView fullname = arrayClass->GetFullName();
|
||||
@@ -508,7 +514,10 @@ Variant MUtils::UnboxVariant(MObject* value)
|
||||
{
|
||||
// Array of Objects
|
||||
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;
|
||||
}
|
||||
@@ -527,10 +536,13 @@ Variant MUtils::UnboxVariant(MObject* value)
|
||||
{
|
||||
MObject* keyManaged = managedKeysPtr[i];
|
||||
MObject* valueManaged = managed.GetValue(keyManaged);
|
||||
native.Add(UnboxVariant(keyManaged), UnboxVariant(valueManaged));
|
||||
native.Add(UnboxVariant(keyManaged, releaseHandle), UnboxVariant(valueManaged, releaseHandle));
|
||||
}
|
||||
Variant v(MoveTemp(native));
|
||||
v.Type.SetTypeName(klass->GetFullName());
|
||||
if (releaseHandle)
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&value);
|
||||
//MUtils::FreeManaged<Dictionary<byte, byte>>(value);
|
||||
return v;
|
||||
}
|
||||
break;
|
||||
@@ -548,6 +560,8 @@ Variant MUtils::UnboxVariant(MObject* value)
|
||||
v.Type = MoveTemp(VariantType(VariantType::Enum, fullname));
|
||||
// TODO: what about 64-bit enum? use enum size with memcpy
|
||||
v.AsUint64 = *static_cast<uint32*>(MCore::Object::Unbox(value));
|
||||
if (releaseHandle)
|
||||
MUtils::FreeManaged<byte>(value);
|
||||
return v;
|
||||
}
|
||||
if (klass->IsValueType())
|
||||
@@ -565,10 +579,12 @@ Variant MUtils::UnboxVariant(MObject* value)
|
||||
type.Struct.Unbox(v.AsBlob.Data, value);
|
||||
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)
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace MUtils
|
||||
extern FLAXENGINE_API MTypeObject* BoxScriptingTypeHandle(const ScriptingTypeHandle& value);
|
||||
extern FLAXENGINE_API VariantType UnboxVariantType(MType* type);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -51,8 +51,41 @@ struct MConverter
|
||||
void Unbox(T& result, MObject* data);
|
||||
void ToManagedArray(MArray* result, const Span<T>& 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).
|
||||
template<typename T>
|
||||
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));
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&data);
|
||||
}
|
||||
};
|
||||
|
||||
// Converter for String.
|
||||
@@ -85,22 +123,22 @@ struct MConverter<String>
|
||||
{
|
||||
MObject* Box(const String& data, const MClass* klass)
|
||||
{
|
||||
#if USE_NETCORE
|
||||
/*#if USE_NETCORE
|
||||
MString* str = MUtils::ToString(data);
|
||||
return MCore::Object::Box(str, klass);
|
||||
#else
|
||||
#else*/
|
||||
return (MObject*)MUtils::ToString(data);
|
||||
#endif
|
||||
//#endif
|
||||
}
|
||||
|
||||
void Unbox(String& result, MObject* data)
|
||||
{
|
||||
#if USE_NETCORE
|
||||
/*#if USE_NETCORE
|
||||
MString* str = (MString*)MCore::Object::Unbox(data);
|
||||
result = MUtils::ToString(str);
|
||||
#else
|
||||
#else*/
|
||||
result = MUtils::ToString((MString*)data);
|
||||
#endif
|
||||
//#endif
|
||||
}
|
||||
|
||||
void ToManagedArray(MArray* result, const Span<String>& data)
|
||||
@@ -120,6 +158,13 @@ struct MConverter<String>
|
||||
for (int32 i = 0; i < result.Length(); 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.
|
||||
@@ -153,6 +198,11 @@ struct MConverter<StringAnsi>
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
MUtils::ToString(dataPtr[i], result.Get()[i]);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&data);
|
||||
}
|
||||
};
|
||||
|
||||
// Converter for StringView.
|
||||
@@ -186,6 +236,11 @@ struct MConverter<StringView>
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
MUtils::ToString(dataPtr[i], result.Get()[i]);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&data);
|
||||
}
|
||||
};
|
||||
|
||||
// Converter for Variant.
|
||||
@@ -219,6 +274,10 @@ struct MConverter<Variant>
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
result.Get()[i] = MUtils::UnboxVariant(dataPtr[i]);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// 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++)
|
||||
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// 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()));
|
||||
Allocator::Free(objects);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Converter for ScriptingObject References.
|
||||
@@ -309,6 +376,10 @@ struct MConverter<ScriptingObjectReference<T>>
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Converter for Asset References.
|
||||
@@ -345,6 +416,10 @@ struct MConverter<AssetReference<T>>
|
||||
for (int32 i = 0; i < result.Length(); 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
|
||||
@@ -370,6 +445,10 @@ struct MConverter<SoftAssetReference<T>>
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Converter for Array.
|
||||
@@ -395,6 +474,10 @@ struct MConverter<Array<T>>
|
||||
Span<T> resultSpan(result.Get(), length);
|
||||
converter.ToNativeArray(resultSpan, array);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
namespace MUtils
|
||||
@@ -435,6 +518,7 @@ namespace MUtils
|
||||
/// <summary>
|
||||
/// Unboxes MObject to the native value of the given type.
|
||||
/// </summary>
|
||||
///
|
||||
template<class T>
|
||||
T Unbox(MObject* object)
|
||||
{
|
||||
@@ -444,6 +528,32 @@ namespace MUtils
|
||||
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>
|
||||
/// Links managed array data to the unmanaged BytesContainer.
|
||||
/// </summary>
|
||||
|
||||
@@ -883,7 +883,6 @@ bool MAssembly::LoadCorlib()
|
||||
|
||||
bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePath)
|
||||
{
|
||||
// TODO: Use new hostfxr delegate load_assembly_bytes? (.NET 8+)
|
||||
// Open .Net assembly
|
||||
static void* LoadAssemblyImagePtr = GetStaticMethodPointer(TEXT("LoadAssemblyImage"));
|
||||
_handle = CallStaticMethod<void*, const Char*>(LoadAssemblyImagePtr, assemblyPath.Get());
|
||||
|
||||
@@ -38,7 +38,7 @@ TEST_CASE("Scripting")
|
||||
CHECK(method);
|
||||
MObject* result = method->Invoke(nullptr, nullptr, nullptr);
|
||||
CHECK(result);
|
||||
int32 resultValue = MUtils::Unbox<int32>(result);
|
||||
int32 resultValue = MUtils::Unbox<int32>(result, true);
|
||||
CHECK(resultValue == 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -745,7 +745,7 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value)
|
||||
StringAsANSI<40> fieldNameAnsi(*fieldName, fieldName.Length());
|
||||
auto field = mclass->GetField(fieldNameAnsi.Get());
|
||||
if (field)
|
||||
value = MUtils::UnboxVariant(field->GetValueBoxed(instance));
|
||||
value = MUtils::UnboxVariant(field->GetValueBoxed(instance), true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -711,7 +711,12 @@ namespace Flax.Build.Bindings
|
||||
if (parameterInfo.IsOut)
|
||||
contents.Append("out ");
|
||||
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)
|
||||
if (parameterInfo.IsOut && !string.IsNullOrEmpty(GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller)))
|
||||
@@ -754,7 +759,12 @@ namespace Flax.Build.Bindings
|
||||
if (parameterInfo.IsOut)
|
||||
contents.Append("out ");
|
||||
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(' ');
|
||||
contents.Append(parameterInfo.Name);
|
||||
@@ -802,7 +812,17 @@ namespace Flax.Build.Bindings
|
||||
if (parameterInfo.IsOut)
|
||||
contents.Append("out ");
|
||||
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 paramName = isSetter ? "value" : parameterInfo.Name;
|
||||
@@ -836,7 +856,12 @@ namespace Flax.Build.Bindings
|
||||
if (parameterInfo.IsOut)
|
||||
contents.Append("out ");
|
||||
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
|
||||
contents.Append(parameterInfo.DefaultValue);
|
||||
@@ -1369,7 +1394,12 @@ namespace Flax.Build.Bindings
|
||||
if (parameterInfo.IsOut)
|
||||
contents.Append("out ");
|
||||
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)
|
||||
contents.Append("this ");
|
||||
else if (parameterInfo.IsParams)
|
||||
@@ -1431,7 +1461,7 @@ namespace Flax.Build.Bindings
|
||||
if (parameterInfo.IsOut)
|
||||
contents.Append("out ");
|
||||
else if (parameterInfo.IsRef)
|
||||
contents.Append("ref ");
|
||||
contents.Append("/*faffaf2*/ ref ");
|
||||
else if (parameterInfo.IsThis)
|
||||
contents.Append("this ");
|
||||
else if (parameterInfo.IsParams)
|
||||
@@ -1522,8 +1552,8 @@ namespace Flax.Build.Bindings
|
||||
public static class ManagedToNative
|
||||
{
|
||||
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 void Free(IntPtr unmanaged) {}
|
||||
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => managed != null ? ManagedHandle.ToIntPtr(managed/*, GCHandleType.Weak*/) : IntPtr.Zero;
|
||||
public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.Free(unmanaged);
|
||||
}
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
@@ -1708,7 +1738,7 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
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;");
|
||||
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(); }}");
|
||||
|
||||
// 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")
|
||||
{
|
||||
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(); }}");
|
||||
|
||||
// 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("*"))
|
||||
{
|
||||
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(); }}");
|
||||
|
||||
// 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")
|
||||
{
|
||||
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(); }}");
|
||||
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
|
||||
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(); }}");
|
||||
|
||||
// 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")
|
||||
{
|
||||
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(); }}");
|
||||
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;");
|
||||
}
|
||||
|
||||
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 + $$"""
|
||||
/// <summary>
|
||||
/// Marshaller for type <see cref="{{structureInfo.Name}}"/>.
|
||||
/// Marshaller for type <see cref="{{marshalManagedType}}"/>.
|
||||
/// </summary>
|
||||
{{InsertHideInEditorSection()}}
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementIn, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedOut, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedIn, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementOut, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedRef, typeof({{marshallerFullName}}.Bidirectional))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedRef, typeof({{marshallerFullName}}.Bidirectional))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementRef, typeof({{marshallerFullName}}))]
|
||||
{{hideInEditorAttribute}}
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ElementIn, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ManagedToUnmanagedOut, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.UnmanagedToManagedIn, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ElementOut, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ManagedToUnmanagedRef, typeof({{marshallerFullName}}.Bidirectional))]
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.UnmanagedToManagedRef, typeof({{marshallerFullName}}.Bidirectional))]
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ElementRef, typeof({{marshallerFullName}}))]
|
||||
{{GenerateCSharpAccessLevel(structureInfo.Access)}}static unsafe class {{marshallerName}}
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
#pragma warning disable 618
|
||||
{{structContents.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
|
||||
|
||||
{{InsertHideInEditorSection()}}
|
||||
{{hideInEditorAttribute}}
|
||||
public static class NativeToManaged
|
||||
{
|
||||
public static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
|
||||
public static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => {{marshallerFullName}}.ToNative(managed);
|
||||
public static void Free({{structureInfo.Name}}Internal unmanaged)
|
||||
public static {{marshalManagedType}} ConvertToManaged({{marshalNativeType}} unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
|
||||
public static {{marshalNativeType}} ConvertToUnmanaged({{marshalManagedType}} managed) => {{marshallerFullName}}.ToNative(managed);
|
||||
public static void Free({{marshalNativeType}} unmanaged)
|
||||
{
|
||||
{{freeContents2.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
|
||||
}
|
||||
}
|
||||
{{InsertHideInEditorSection()}}
|
||||
{{hideInEditorAttribute}}
|
||||
public static class ManagedToNative
|
||||
{
|
||||
public static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
|
||||
public static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => {{marshallerFullName}}.ToNative(managed);
|
||||
public static void Free({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.Free(unmanaged);
|
||||
public static {{marshalManagedType}} ConvertToManaged({{marshalNativeType}} unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
|
||||
public static {{marshalNativeType}} ConvertToUnmanaged({{marshalManagedType}} managed) => {{marshallerFullName}}.ToNative(managed);
|
||||
public static void Free({{marshalNativeType}} unmanaged) => {{marshallerFullName}}.Free(unmanaged);
|
||||
}
|
||||
{{InsertHideInEditorSection()}}
|
||||
public struct Bidirectional
|
||||
{{hideInEditorAttribute}}
|
||||
public ref struct Bidirectional
|
||||
{
|
||||
{{structureInfo.Name}} managed;
|
||||
{{structureInfo.Name}}Internal unmanaged;
|
||||
public void FromManaged({{structureInfo.Name}} managed) => this.managed = managed;
|
||||
public {{structureInfo.Name}}Internal ToUnmanaged() { unmanaged = {{marshallerFullName}}.ToNative(managed); return unmanaged; }
|
||||
public void FromUnmanaged({{structureInfo.Name}}Internal unmanaged) => this.unmanaged = unmanaged;
|
||||
public {{structureInfo.Name}} ToManaged() { managed = {{marshallerFullName}}.ToManaged(unmanaged); return managed; }
|
||||
public void Free() => NativeToManaged.Free(unmanaged);
|
||||
{{marshalManagedType}} managed;
|
||||
{{marshalNativeType}} unmanaged;
|
||||
public void FromManaged({{marshalManagedType}} managed) => this.managed = managed;
|
||||
public {{marshalNativeType}} ToUnmanaged() { unmanaged = {{marshallerFullName}}.ToNative(managed); return unmanaged; }
|
||||
public void FromUnmanaged({{marshalNativeType}} 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 {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => ToNative(managed);
|
||||
internal static void Free({{structureInfo.Name}}Internal unmanaged)
|
||||
internal static {{marshalManagedType}} ConvertToManaged({{marshalNativeType}} unmanaged) => ToManaged(unmanaged);
|
||||
internal static {{marshalNativeType}} ConvertToUnmanaged({{marshalManagedType}} managed) => ToNative(managed);
|
||||
internal static void Free({{marshalNativeType}} unmanaged)
|
||||
{
|
||||
{{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()}}
|
||||
}
|
||||
internal static {{structureInfo.Name}}Internal ToNative({{structureInfo.Name}} managed)
|
||||
internal static {{marshalNativeType}} ToNative({{marshalManagedType}} managed)
|
||||
{
|
||||
{{toNativeContent.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
|
||||
}
|
||||
@@ -1892,13 +1933,6 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
""").Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)));
|
||||
|
||||
string InsertHideInEditorSection()
|
||||
{
|
||||
return (buildData.Target != null & buildData.Target.IsEditor) ? $$"""
|
||||
[HideInEditor]
|
||||
""" : "";
|
||||
}
|
||||
|
||||
PutStringBuilder(toManagedContent);
|
||||
PutStringBuilder(toNativeContent);
|
||||
PutStringBuilder(freeContents);
|
||||
@@ -2231,7 +2265,7 @@ namespace Flax.Build.Bindings
|
||||
if (parameterInfo.IsOut)
|
||||
contents.Append("out ");
|
||||
else if (parameterInfo.IsRef)
|
||||
contents.Append("ref ");
|
||||
contents.Append("/*faffaf3*/ ref ");
|
||||
else if (parameterInfo.IsThis)
|
||||
contents.Append("this ");
|
||||
else if (parameterInfo.IsParams)
|
||||
|
||||
@@ -119,7 +119,8 @@ namespace Flax.Build.Bindings
|
||||
// Pass as pointer to local variable converted for managed runtime
|
||||
if (paramType.IsPtr)
|
||||
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}";
|
||||
useLocalVar = true;
|
||||
}
|
||||
@@ -998,6 +999,11 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
#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#
|
||||
functionInfo.Glue = new FunctionInfo.GlueInfo
|
||||
{
|
||||
@@ -1155,6 +1161,7 @@ namespace Flax.Build.Bindings
|
||||
},
|
||||
IsOut = parameterInfo.IsOut,
|
||||
IsRef = isRefOut || parameterInfo.Type.IsRef,
|
||||
IsConst = true,
|
||||
});
|
||||
}
|
||||
#endif
|
||||
@@ -1550,7 +1557,8 @@ namespace Flax.Build.Bindings
|
||||
if (paramIsRef)
|
||||
{
|
||||
// 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}";
|
||||
useLocalVar = true;
|
||||
}
|
||||
@@ -1630,6 +1638,78 @@ namespace Flax.Build.Bindings
|
||||
else if (!passAsParamPtr)
|
||||
paramValue = '*' + paramValue;
|
||||
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*)¶ms[{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*)¶ms[{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*)¶ms[{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*)¶ms[{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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2085,6 +2165,22 @@ namespace Flax.Build.Bindings
|
||||
else if (!passAsParamPtr)
|
||||
paramValue = '*' + paramValue;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -2382,6 +2478,7 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
Type = fieldInfo.Type,
|
||||
Name = "value",
|
||||
IsConst = true,
|
||||
},
|
||||
},
|
||||
ReturnType = new TypeInfo
|
||||
@@ -2991,21 +3088,28 @@ namespace Flax.Build.Bindings
|
||||
header.AppendLine("namespace {");
|
||||
header.AppendFormat("{0} ToManaged(const {1}& 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("}");
|
||||
|
||||
// Generate MConverter for a structure
|
||||
header.Append("template<>").AppendLine();
|
||||
header.AppendFormat("struct MConverter<{0}>", fullName).AppendLine();
|
||||
header.AppendFormat("struct DLLEXPORT MConverter<{0}>", fullName).AppendLine();
|
||||
header.Append('{').AppendLine();
|
||||
|
||||
header.AppendFormat(" MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine();
|
||||
header.Append(" {").AppendLine();
|
||||
header.Append(" auto managed = ToManaged(data);").AppendLine();
|
||||
header.Append(" return MCore::Object::Box((void*)&managed, klass);").AppendLine();
|
||||
header.Append(" }").AppendLine();
|
||||
|
||||
header.AppendFormat(" void Unbox({0}& result, MObject* data)", fullName).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.AppendFormat(" void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine();
|
||||
header.Append(" {").AppendLine();
|
||||
header.AppendFormat(" MClass* klass = {0}::TypeInitializer.GetClass();", fullName).AppendLine();
|
||||
@@ -3016,12 +3120,21 @@ namespace Flax.Build.Bindings
|
||||
header.Append(" MCore::GC::WriteValue(&resultPtr[i], &managed, 1, klass);").AppendLine();
|
||||
header.Append(" }").AppendLine();
|
||||
header.Append(" }").AppendLine();
|
||||
|
||||
header.AppendFormat(" void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine();
|
||||
header.Append(" {").AppendLine();
|
||||
header.AppendFormat(" {0}* dataPtr = ({0}*)MCore::Array::GetAddress(data);", wrapperName).AppendLine();
|
||||
header.Append(" for (int32 i = 0; i < result.Length(); i++)").AppendLine();
|
||||
header.Append(" result[i] = ToNative(dataPtr[i]);").AppendLine();
|
||||
header.Append(" }").AppendLine();
|
||||
|
||||
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();
|
||||
|
||||
// Generate converting function native -> managed
|
||||
@@ -3096,8 +3209,47 @@ namespace Flax.Build.Bindings
|
||||
header.AppendFormat(" result.{0} = value.{0};", fieldInfo.Name).AppendLine();
|
||||
else
|
||||
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('}').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.AppendLine("}");
|
||||
}
|
||||
@@ -3176,6 +3328,11 @@ namespace Flax.Build.Bindings
|
||||
header.AppendFormat(" Unbox(result[i], dataPtr[i]);", fullName).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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user