Merge remote-tracking branch 'origin/master' into 1.7
This commit is contained in:
@@ -8,6 +8,7 @@ using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FlaxEngine.Interop
|
||||
{
|
||||
@@ -20,26 +21,135 @@ namespace FlaxEngine.Interop
|
||||
{
|
||||
// TODO: Use .NET8 Unsafe.BitCast<TRet, ValueType>(returnValue) for more efficient casting of value types over boxing cast
|
||||
|
||||
internal static IntPtr MarshalReturnValue<TRet>(ref TRet returnValue)
|
||||
internal static class InvokerMarshallers<T>
|
||||
{
|
||||
internal delegate IntPtr Delegate(ref T value);
|
||||
internal static Delegate deleg;
|
||||
internal static Delegate delegThunk;
|
||||
|
||||
static InvokerMarshallers()
|
||||
{
|
||||
Type type = typeof(T);
|
||||
if (type == typeof(string))
|
||||
deleg = typeof(Invoker).GetMethod(nameof(MarshalReturnValueString), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
|
||||
else if (type == typeof(ManagedHandle))
|
||||
deleg = typeof(Invoker).GetMethod(nameof(MarshalReturnValueManagedHandle), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
|
||||
else if (type == typeof(Type))
|
||||
deleg = typeof(Invoker).GetMethod(nameof(MarshalReturnValueType), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
|
||||
else if (type.IsArray)
|
||||
deleg = typeof(Invoker).GetMethod(nameof(MarshalReturnValueArray), BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(type).CreateDelegate<Delegate>();
|
||||
else if (type == typeof(bool))
|
||||
deleg = typeof(Invoker).GetMethod(nameof(MarshalReturnValueBool), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
|
||||
else
|
||||
deleg = typeof(Invoker).GetMethod(nameof(MarshalReturnValueWrapped), BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(type).CreateDelegate<Delegate>();
|
||||
|
||||
if (type == typeof(string))
|
||||
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueString), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
|
||||
else if (type == typeof(ManagedHandle))
|
||||
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueManagedHandle), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
|
||||
else if (type == typeof(Type))
|
||||
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueType), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
|
||||
else if (type.IsArray)
|
||||
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueArray), BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(type).CreateDelegate<Delegate>();
|
||||
else if (type == typeof(System.Boolean))
|
||||
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueMonoBoolean), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
|
||||
else if (type == typeof(IntPtr))
|
||||
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueIntPtr), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
|
||||
else if (type == typeof(System.Int16))
|
||||
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueInt16), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
|
||||
else if (type == typeof(System.Int32))
|
||||
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueInt32), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
|
||||
else if (type == typeof(System.Int64))
|
||||
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueInt64), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
|
||||
else if (type == typeof(System.UInt16))
|
||||
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueUInt16), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
|
||||
else if (type == typeof(System.UInt32))
|
||||
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueUInt32), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
|
||||
else if (type == typeof(System.UInt64))
|
||||
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueUInt64), BindingFlags.Static | BindingFlags.NonPublic).CreateDelegate<Delegate>();
|
||||
else
|
||||
delegThunk = typeof(Invoker).GetMethod(nameof(MarshalReturnValueWrapped), BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(type).CreateDelegate<Delegate>();
|
||||
}
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueString(ref string returnValue)
|
||||
{
|
||||
return returnValue != null ? ManagedString.ToNativeWeak(returnValue) : IntPtr.Zero;
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueManagedHandle(ref ManagedHandle returnValue)
|
||||
{
|
||||
return returnValue != null ? ManagedHandle.ToIntPtr(returnValue) : IntPtr.Zero;
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueType(ref Type returnValue)
|
||||
{
|
||||
return returnValue != null ? ManagedHandle.ToIntPtr(GetTypeGCHandle(returnValue)) : IntPtr.Zero;
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueArray<TRet>(ref TRet returnValue)
|
||||
{
|
||||
if (returnValue == null)
|
||||
return IntPtr.Zero;
|
||||
if (typeof(TRet) == typeof(string))
|
||||
return ManagedString.ToNativeWeak(Unsafe.As<string>(returnValue));
|
||||
if (typeof(TRet) == typeof(ManagedHandle))
|
||||
return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnValue);
|
||||
if (typeof(TRet) == typeof(bool))
|
||||
return (bool)(object)returnValue ? boolTruePtr : boolFalsePtr;
|
||||
if (typeof(TRet) == typeof(Type))
|
||||
return ManagedHandle.ToIntPtr(GetTypeGCHandle(Unsafe.As<Type>(returnValue)));
|
||||
if (typeof(TRet).IsArray)
|
||||
{
|
||||
var elementType = typeof(TRet).GetElementType();
|
||||
if (ArrayFactory.GetMarshalledType(elementType) == elementType)
|
||||
return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As<Array>(returnValue)), GCHandleType.Weak);
|
||||
return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As<Array>(returnValue)), GCHandleType.Weak);
|
||||
}
|
||||
return ManagedHandle.ToIntPtr(returnValue, GCHandleType.Weak);
|
||||
var elementType = typeof(TRet).GetElementType();
|
||||
if (ArrayFactory.GetMarshalledType(elementType) == elementType)
|
||||
return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As<Array>(returnValue)), GCHandleType.Weak);
|
||||
return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As<Array>(returnValue)), GCHandleType.Weak);
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueBool(ref bool returnValue)
|
||||
{
|
||||
return returnValue ? boolTruePtr : boolFalsePtr;
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueIntPtr(ref IntPtr returnValue)
|
||||
{
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueMonoBoolean(ref bool returnValue)
|
||||
{
|
||||
return returnValue ? 1 : 0;
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueInt16(ref Int16 returnValue)
|
||||
{
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueInt32(ref Int32 returnValue)
|
||||
{
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueInt64(ref Int64 returnValue)
|
||||
{
|
||||
return new IntPtr(returnValue);
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueUInt16(ref UInt16 returnValue)
|
||||
{
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueUInt32(ref UInt32 returnValue)
|
||||
{
|
||||
return new IntPtr(returnValue);
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueUInt64(ref UInt64 returnValue)
|
||||
{
|
||||
return new IntPtr((long)returnValue);
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueWrapped<TRet>(ref TRet returnValue)
|
||||
{
|
||||
return returnValue != null ? ManagedHandle.ToIntPtr(returnValue, GCHandleType.Weak) : IntPtr.Zero;
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValue<TRet>(ref TRet returnValue)
|
||||
{
|
||||
return InvokerMarshallers<TRet>.deleg(ref returnValue);
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueGeneric(Type returnType, object returnObject)
|
||||
@@ -63,39 +173,7 @@ namespace FlaxEngine.Interop
|
||||
|
||||
internal static IntPtr MarshalReturnValueThunk<TRet>(ref TRet returnValue)
|
||||
{
|
||||
if (returnValue == null)
|
||||
return IntPtr.Zero;
|
||||
if (typeof(TRet) == typeof(string))
|
||||
return ManagedString.ToNativeWeak(Unsafe.As<string>(returnValue));
|
||||
if (typeof(TRet) == typeof(IntPtr))
|
||||
return (IntPtr)(object)returnValue;
|
||||
if (typeof(TRet) == typeof(ManagedHandle))
|
||||
return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnValue);
|
||||
if (typeof(TRet) == typeof(Type))
|
||||
return ManagedHandle.ToIntPtr(GetTypeGCHandle(Unsafe.As<Type>(returnValue)));
|
||||
if (typeof(TRet).IsArray)
|
||||
{
|
||||
var elementType = typeof(TRet).GetElementType();
|
||||
if (ArrayFactory.GetMarshalledType(elementType) == elementType)
|
||||
return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As<Array>(returnValue)), GCHandleType.Weak);
|
||||
return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As<Array>(returnValue)), GCHandleType.Weak);
|
||||
}
|
||||
// Match Mono bindings and pass value as pointer to prevent boxing it
|
||||
if (typeof(TRet) == typeof(System.Boolean))
|
||||
return new IntPtr(((System.Boolean)(object)returnValue) ? 1 : 0);
|
||||
if (typeof(TRet) == typeof(System.Int16))
|
||||
return new IntPtr((int)(System.Int16)(object)returnValue);
|
||||
if (typeof(TRet) == typeof(System.Int32))
|
||||
return new IntPtr((int)(System.Int32)(object)returnValue);
|
||||
if (typeof(TRet) == typeof(System.Int64))
|
||||
return new IntPtr((long)(System.Int64)(object)returnValue);
|
||||
if (typeof(TRet) == typeof(System.UInt16))
|
||||
return (IntPtr)new UIntPtr((ulong)(System.UInt16)(object)returnValue);
|
||||
if (typeof(TRet) == typeof(System.UInt32))
|
||||
return (IntPtr)new UIntPtr((ulong)(System.UInt32)(object)returnValue);
|
||||
if (typeof(TRet) == typeof(System.UInt64))
|
||||
return (IntPtr)new UIntPtr((ulong)(System.UInt64)(object)returnValue);
|
||||
return ManagedHandle.ToIntPtr(returnValue, GCHandleType.Weak);
|
||||
return InvokerMarshallers<TRet>.delegThunk(ref returnValue);
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueThunkGeneric(Type returnType, object returnObject)
|
||||
@@ -205,7 +283,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -215,7 +293,7 @@ namespace FlaxEngine.Interop
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -242,7 +320,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -262,7 +340,7 @@ namespace FlaxEngine.Interop
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -291,7 +369,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -317,7 +395,7 @@ namespace FlaxEngine.Interop
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -347,7 +425,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -379,7 +457,7 @@ namespace FlaxEngine.Interop
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -410,7 +488,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -448,7 +526,7 @@ namespace FlaxEngine.Interop
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -480,7 +558,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -490,7 +568,7 @@ namespace FlaxEngine.Interop
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -517,7 +595,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -537,7 +615,7 @@ namespace FlaxEngine.Interop
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -566,7 +644,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -592,7 +670,7 @@ namespace FlaxEngine.Interop
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -622,7 +700,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -654,7 +732,7 @@ namespace FlaxEngine.Interop
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -685,7 +763,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -723,7 +801,7 @@ namespace FlaxEngine.Interop
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -755,7 +833,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -765,7 +843,7 @@ namespace FlaxEngine.Interop
|
||||
return MarshalReturnValue(ref ret);
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -792,7 +870,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -812,7 +890,7 @@ namespace FlaxEngine.Interop
|
||||
return MarshalReturnValue(ref ret);
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -841,7 +919,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -867,7 +945,7 @@ namespace FlaxEngine.Interop
|
||||
return MarshalReturnValue(ref ret);
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -897,7 +975,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -929,7 +1007,7 @@ namespace FlaxEngine.Interop
|
||||
return MarshalReturnValue(ref ret);
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -960,7 +1038,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -998,7 +1076,7 @@ namespace FlaxEngine.Interop
|
||||
return MarshalReturnValue(ref ret);
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -1030,7 +1108,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -1040,7 +1118,7 @@ namespace FlaxEngine.Interop
|
||||
return MarshalReturnValue(ref ret);
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -1067,7 +1145,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -1087,7 +1165,7 @@ namespace FlaxEngine.Interop
|
||||
return MarshalReturnValue(ref ret);
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -1116,7 +1194,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -1142,7 +1220,7 @@ namespace FlaxEngine.Interop
|
||||
return MarshalReturnValue(ref ret);
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -1172,7 +1250,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -1204,7 +1282,7 @@ namespace FlaxEngine.Interop
|
||||
return MarshalReturnValue(ref ret);
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
@@ -1235,7 +1313,7 @@ namespace FlaxEngine.Interop
|
||||
return Unsafe.As<ThunkInvokerDelegate>(CreateDelegateFromMethod(method, false));
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static IntPtr MarshalAndInvoke(object delegateContext, ManagedHandle instancePtr, IntPtr paramPtr)
|
||||
{
|
||||
(Type[] types, InvokerDelegate deleg) = (Tuple<Type[], InvokerDelegate>)(delegateContext);
|
||||
@@ -1273,7 +1351,7 @@ namespace FlaxEngine.Interop
|
||||
return MarshalReturnValue(ref ret);
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
//[DebuggerStepThrough]
|
||||
internal static unsafe IntPtr InvokeThunk(object delegateContext, ManagedHandle instancePtr, IntPtr* paramPtrs)
|
||||
{
|
||||
ThunkInvokerDelegate deleg = Unsafe.As<ThunkInvokerDelegate>(delegateContext);
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace FlaxEngine.Interop
|
||||
private int _elementSize;
|
||||
private int _length;
|
||||
|
||||
[ThreadStatic] private static Dictionary<ManagedArray, ManagedHandle> pooledArrayHandles;
|
||||
|
||||
public static ManagedArray WrapNewArray(Array arr) => new ManagedArray(arr, arr.GetType());
|
||||
|
||||
public static ManagedArray WrapNewArray(Array arr, Type arrayType) => new ManagedArray(arr, arrayType);
|
||||
@@ -37,22 +39,38 @@ namespace FlaxEngine.Interop
|
||||
/// Returns an instance of ManagedArray from shared pool.
|
||||
/// </summary>
|
||||
/// <remarks>The resources must be released by calling FreePooled() instead of Free()-method.</remarks>
|
||||
public static ManagedArray WrapPooledArray(Array arr)
|
||||
public static (ManagedHandle managedHandle, ManagedArray managedArray) WrapPooledArray(Array arr)
|
||||
{
|
||||
ManagedArray managedArray = ManagedArrayPool.Get();
|
||||
managedArray.WrapArray(arr, arr.GetType());
|
||||
return managedArray;
|
||||
|
||||
if (pooledArrayHandles == null)
|
||||
pooledArrayHandles = new();
|
||||
if (!pooledArrayHandles.TryGetValue(managedArray, out ManagedHandle handle))
|
||||
{
|
||||
handle = ManagedHandle.Alloc(managedArray);
|
||||
pooledArrayHandles.Add(managedArray, handle);
|
||||
}
|
||||
return (handle, managedArray);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an instance of ManagedArray from shared pool.
|
||||
/// </summary>
|
||||
/// <remarks>The resources must be released by calling FreePooled() instead of Free()-method.</remarks>
|
||||
public static ManagedArray WrapPooledArray(Array arr, Type arrayType)
|
||||
public static ManagedHandle WrapPooledArray(Array arr, Type arrayType)
|
||||
{
|
||||
ManagedArray managedArray = ManagedArrayPool.Get(arr.Length * NativeInterop.GetTypeSize(arr.GetType().GetElementType()));
|
||||
managedArray.WrapArray(arr, arrayType);
|
||||
return managedArray;
|
||||
|
||||
if (pooledArrayHandles == null)
|
||||
pooledArrayHandles = new();
|
||||
if (!pooledArrayHandles.TryGetValue(managedArray, out ManagedHandle handle))
|
||||
{
|
||||
handle = ManagedHandle.Alloc(managedArray);
|
||||
pooledArrayHandles.Add(managedArray, handle);
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
internal static ManagedArray AllocateNewArray(int length, Type arrayType, Type elementType)
|
||||
@@ -64,12 +82,20 @@ namespace FlaxEngine.Interop
|
||||
/// <summary>
|
||||
/// Returns an instance of ManagedArray from shared pool.
|
||||
/// </summary>
|
||||
/// <remarks>The resources must be released by calling FreePooled() instead of Free()-method.</remarks>
|
||||
public static ManagedArray AllocatePooledArray<T>(int length) where T : unmanaged
|
||||
/// <remarks>The resources must be released by calling FreePooled() instead of Free()-method. Do not release the returned ManagedHandle.</remarks>
|
||||
public static (ManagedHandle managedHandle, ManagedArray managedArray) AllocatePooledArray<T>(int length) where T : unmanaged
|
||||
{
|
||||
ManagedArray managedArray = ManagedArrayPool.Get(length * Unsafe.SizeOf<T>());
|
||||
managedArray.Allocate<T>(length);
|
||||
return managedArray;
|
||||
|
||||
if (pooledArrayHandles == null)
|
||||
pooledArrayHandles = new();
|
||||
if (!pooledArrayHandles.TryGetValue(managedArray, out ManagedHandle handle))
|
||||
{
|
||||
handle = ManagedHandle.Alloc(managedArray);
|
||||
pooledArrayHandles.Add(managedArray, handle);
|
||||
}
|
||||
return (handle, managedArray);
|
||||
}
|
||||
|
||||
public ManagedArray(Array arr, Type elementType) => WrapArray(arr, elementType);
|
||||
@@ -91,18 +117,19 @@ namespace FlaxEngine.Interop
|
||||
|
||||
internal void Allocate<T>(int length) where T : unmanaged
|
||||
{
|
||||
_length = length;
|
||||
_arrayType = typeof(T[]);
|
||||
_elementType = typeof(T);
|
||||
_elementSize = Unsafe.SizeOf<T>();
|
||||
|
||||
// Try to reuse existing allocated buffer
|
||||
if (length * Unsafe.SizeOf<T>() > _unmanagedAllocationSize)
|
||||
if (length * _elementSize > _unmanagedAllocationSize)
|
||||
{
|
||||
if (_unmanagedAllocationSize > 0)
|
||||
NativeInterop.NativeFree(_unmanagedData.ToPointer());
|
||||
_unmanagedData = (IntPtr)NativeInterop.NativeAlloc(length, Unsafe.SizeOf<T>());
|
||||
_unmanagedAllocationSize = Unsafe.SizeOf<T>() * length;
|
||||
_unmanagedData = (IntPtr)NativeInterop.NativeAlloc(length, _elementSize);
|
||||
_unmanagedAllocationSize = _elementSize * length;
|
||||
}
|
||||
_length = length;
|
||||
_arrayType = typeof(T).MakeArrayType();
|
||||
_elementType = typeof(T);
|
||||
_elementSize = Unsafe.SizeOf<T>();
|
||||
}
|
||||
|
||||
private ManagedArray()
|
||||
@@ -112,12 +139,12 @@ namespace FlaxEngine.Interop
|
||||
private ManagedArray(IntPtr ptr, int length, Type arrayType, Type elementType)
|
||||
{
|
||||
Assert.IsTrue(arrayType.IsArray);
|
||||
_elementType = elementType;
|
||||
_elementSize = NativeInterop.GetTypeSize(elementType);
|
||||
_unmanagedData = ptr;
|
||||
_unmanagedAllocationSize = Marshal.SizeOf(elementType) * length;
|
||||
_unmanagedAllocationSize = _elementSize * length;
|
||||
_length = length;
|
||||
_arrayType = arrayType;
|
||||
_elementType = elementType;
|
||||
_elementSize = NativeInterop.GetTypeSize(_elementType);
|
||||
}
|
||||
|
||||
~ManagedArray()
|
||||
@@ -330,15 +357,9 @@ namespace FlaxEngine.Interop
|
||||
{
|
||||
private IntPtr handle;
|
||||
|
||||
private ManagedHandle(IntPtr handle)
|
||||
{
|
||||
this.handle = handle;
|
||||
}
|
||||
private ManagedHandle(IntPtr handle) => this.handle = handle;
|
||||
|
||||
private ManagedHandle(object value, GCHandleType type)
|
||||
{
|
||||
handle = ManagedHandlePool.AllocateHandle(value, type);
|
||||
}
|
||||
private ManagedHandle(object value, GCHandleType type) => handle = ManagedHandlePool.AllocateHandle(value, type);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ManagedHandle Alloc(object value) => new ManagedHandle(value, GCHandleType.Normal);
|
||||
@@ -383,7 +404,7 @@ namespace FlaxEngine.Interop
|
||||
|
||||
public override int GetHashCode() => handle.GetHashCode();
|
||||
|
||||
public override bool Equals(object o) => o is ManagedHandle other && Equals(other);
|
||||
public override bool Equals(object obj) => obj is ManagedHandle other && handle == other.handle;
|
||||
|
||||
public bool Equals(ManagedHandle other) => handle == other.handle;
|
||||
|
||||
@@ -391,42 +412,44 @@ namespace FlaxEngine.Interop
|
||||
|
||||
public static bool operator !=(ManagedHandle a, ManagedHandle b) => a.handle != b.handle;
|
||||
|
||||
private static class ManagedHandlePool
|
||||
internal static class ManagedHandlePool
|
||||
{
|
||||
private const int WeakPoolCollectionSizeThreshold = 10000000;
|
||||
private const int WeakPoolCollectionTimeThreshold = 500;
|
||||
|
||||
private static ulong normalHandleAccumulator = 0;
|
||||
private static ulong pinnedHandleAccumulator = 0;
|
||||
private static ulong weakHandleAccumulator = 0;
|
||||
// Rolling numbers for handles, two bits reserved for the type
|
||||
private static ulong normalHandleAccumulator = ((ulong)GCHandleType.Normal << 62) & 0xC000000000000000;
|
||||
private static ulong pinnedHandleAccumulator = ((ulong)GCHandleType.Pinned << 62) & 0xC000000000000000;
|
||||
private static ulong weakHandleAccumulator = ((ulong)GCHandleType.Weak << 62) & 0xC000000000000000;
|
||||
|
||||
private static object poolLock = new object();
|
||||
private static Dictionary<IntPtr, object> persistentPool = new Dictionary<nint, object>();
|
||||
private static Dictionary<IntPtr, GCHandle> pinnedPool = new Dictionary<nint, GCHandle>();
|
||||
// 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.
|
||||
private static Dictionary<IntPtr, object> persistentPool = new();
|
||||
private static Dictionary<IntPtr, GCHandle> pinnedPool = new();
|
||||
|
||||
// Manage double-buffered pool for weak handles in order to avoid collecting in-flight handles
|
||||
[ThreadStatic] private static Dictionary<IntPtr, object> weakPool;
|
||||
[ThreadStatic] private static Dictionary<IntPtr, object> weakPoolOther;
|
||||
[ThreadStatic] private static ulong nextWeakPoolCollection;
|
||||
[ThreadStatic] private static int nextWeakPoolGCCollection;
|
||||
[ThreadStatic] private static long lastWeakPoolCollectionTime;
|
||||
// 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 ulong nextWeakPoolCollection;
|
||||
private static int nextWeakPoolGCCollection;
|
||||
private static long lastWeakPoolCollectionTime;
|
||||
|
||||
/// <summary>
|
||||
/// Tries to free all references to old weak handles so GC can collect them.
|
||||
/// </summary>
|
||||
private static void TryCollectWeakHandles()
|
||||
internal static void TryCollectWeakHandles()
|
||||
{
|
||||
if (weakHandleAccumulator < nextWeakPoolCollection)
|
||||
return;
|
||||
|
||||
nextWeakPoolCollection = weakHandleAccumulator + 1000;
|
||||
if (weakPool == null)
|
||||
{
|
||||
weakPool = new Dictionary<nint, object>();
|
||||
weakPoolOther = new Dictionary<nint, object>();
|
||||
nextWeakPoolGCCollection = GC.CollectionCount(0) + 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to swap pools after garbage collection or whenever the pool gets too large
|
||||
var gc0CollectionCount = GC.CollectionCount(0);
|
||||
if (gc0CollectionCount < nextWeakPoolGCCollection && weakPool.Count < WeakPoolCollectionSizeThreshold)
|
||||
@@ -443,130 +466,157 @@ namespace FlaxEngine.Interop
|
||||
weakPool.Clear();
|
||||
}
|
||||
|
||||
private static IntPtr NewHandle(GCHandleType type)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static IntPtr NewHandle(GCHandleType type) => type switch
|
||||
{
|
||||
IntPtr handle;
|
||||
if (type == GCHandleType.Normal)
|
||||
handle = (IntPtr)Interlocked.Increment(ref normalHandleAccumulator);
|
||||
else if (type == GCHandleType.Pinned)
|
||||
handle = (IntPtr)Interlocked.Increment(ref pinnedHandleAccumulator);
|
||||
else //if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection)
|
||||
handle = (IntPtr)Interlocked.Increment(ref weakHandleAccumulator);
|
||||
|
||||
// Two bits reserved for the type
|
||||
handle |= (IntPtr)(((ulong)type << 62) & 0xC000000000000000);
|
||||
return handle;
|
||||
}
|
||||
GCHandleType.Normal => (IntPtr)Interlocked.Increment(ref normalHandleAccumulator),
|
||||
GCHandleType.Pinned => (IntPtr)Interlocked.Increment(ref pinnedHandleAccumulator),
|
||||
GCHandleType.Weak => (IntPtr)Interlocked.Increment(ref weakHandleAccumulator),
|
||||
GCHandleType.WeakTrackResurrection => (IntPtr)Interlocked.Increment(ref weakHandleAccumulator),
|
||||
_ => throw new NotImplementedException(type.ToString())
|
||||
};
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static GCHandleType GetHandleType(IntPtr handle)
|
||||
{
|
||||
return (GCHandleType)(((ulong)handle & 0xC000000000000000) >> 62);
|
||||
}
|
||||
private static GCHandleType GetHandleType(IntPtr handle) => (GCHandleType)(((ulong)handle & 0xC000000000000000) >> 62);
|
||||
|
||||
internal static IntPtr AllocateHandle(object value, GCHandleType type)
|
||||
{
|
||||
TryCollectWeakHandles();
|
||||
IntPtr handle = NewHandle(type);
|
||||
if (type == GCHandleType.Normal)
|
||||
switch (type)
|
||||
{
|
||||
lock (poolLock)
|
||||
case GCHandleType.Normal:
|
||||
lock (persistentPool)
|
||||
persistentPool.Add(handle, value);
|
||||
}
|
||||
else if (type == GCHandleType.Pinned)
|
||||
{
|
||||
lock (poolLock)
|
||||
break;
|
||||
case GCHandleType.Pinned:
|
||||
lock (pinnedPool)
|
||||
pinnedPool.Add(handle, GCHandle.Alloc(value, GCHandleType.Pinned));
|
||||
break;
|
||||
case GCHandleType.Weak:
|
||||
case GCHandleType.WeakTrackResurrection:
|
||||
lock (weakPoolLock)
|
||||
{
|
||||
TryCollectWeakHandles();
|
||||
weakPool.Add(handle, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection)
|
||||
weakPool.Add(handle, value);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
internal static object GetObject(IntPtr handle)
|
||||
{
|
||||
TryCollectWeakHandles();
|
||||
object value;
|
||||
GCHandleType type = GetHandleType(handle);
|
||||
if (type == GCHandleType.Normal)
|
||||
switch (GetHandleType(handle))
|
||||
{
|
||||
lock (poolLock)
|
||||
case GCHandleType.Normal:
|
||||
lock (persistentPool)
|
||||
{
|
||||
if (persistentPool.TryGetValue(handle, out value))
|
||||
if (persistentPool.TryGetValue(handle, out object value))
|
||||
return value;
|
||||
}
|
||||
}
|
||||
else if (type == GCHandleType.Pinned)
|
||||
{
|
||||
lock (poolLock)
|
||||
break;
|
||||
case GCHandleType.Pinned:
|
||||
lock (pinnedPool)
|
||||
{
|
||||
if (pinnedPool.TryGetValue(handle, out GCHandle gchandle))
|
||||
return gchandle.Target;
|
||||
if (pinnedPool.TryGetValue(handle, out GCHandle gcHandle))
|
||||
return gcHandle.Target;
|
||||
}
|
||||
break;
|
||||
case GCHandleType.Weak:
|
||||
case GCHandleType.WeakTrackResurrection:
|
||||
lock (weakPoolLock)
|
||||
{
|
||||
TryCollectWeakHandles();
|
||||
if (weakPool.TryGetValue(handle, out object value))
|
||||
return value;
|
||||
else if (weakPoolOther.TryGetValue(handle, out value))
|
||||
return value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (weakPool.TryGetValue(handle, out value))
|
||||
return value;
|
||||
else if (weakPoolOther.TryGetValue(handle, out value))
|
||||
return value;
|
||||
|
||||
throw new NativeInteropException("Invalid ManagedHandle");
|
||||
}
|
||||
|
||||
internal static void SetObject(IntPtr handle, object value)
|
||||
{
|
||||
TryCollectWeakHandles();
|
||||
GCHandleType type = GetHandleType(handle);
|
||||
if (type == GCHandleType.Normal)
|
||||
switch (GetHandleType(handle))
|
||||
{
|
||||
lock (poolLock)
|
||||
case GCHandleType.Normal:
|
||||
lock (persistentPool)
|
||||
{
|
||||
if (persistentPool.ContainsKey(handle))
|
||||
persistentPool[handle] = value;
|
||||
ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(persistentPool, handle);
|
||||
if (!Unsafe.IsNullRef(ref obj))
|
||||
{
|
||||
obj = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == GCHandleType.Pinned)
|
||||
{
|
||||
lock (poolLock)
|
||||
break;
|
||||
case GCHandleType.Pinned:
|
||||
lock (pinnedPool)
|
||||
{
|
||||
if (pinnedPool.TryGetValue(handle, out GCHandle gchandle))
|
||||
gchandle.Target = value;
|
||||
ref GCHandle gcHandle = ref CollectionsMarshal.GetValueRefOrNullRef(pinnedPool, handle);
|
||||
if (!Unsafe.IsNullRef(ref gcHandle))
|
||||
{
|
||||
gcHandle.Target = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GCHandleType.Weak:
|
||||
case GCHandleType.WeakTrackResurrection:
|
||||
lock (weakPoolLock)
|
||||
{
|
||||
TryCollectWeakHandles();
|
||||
{
|
||||
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 if (weakPool.ContainsKey(handle))
|
||||
weakPool[handle] = value;
|
||||
else if (weakPoolOther.ContainsKey(handle))
|
||||
weakPoolOther[handle] = value;
|
||||
|
||||
throw new NativeInteropException("Invalid ManagedHandle");
|
||||
}
|
||||
|
||||
internal static void FreeHandle(IntPtr handle)
|
||||
{
|
||||
TryCollectWeakHandles();
|
||||
GCHandleType type = GetHandleType(handle);
|
||||
if (type == GCHandleType.Normal)
|
||||
switch (GetHandleType(handle))
|
||||
{
|
||||
lock (poolLock)
|
||||
case GCHandleType.Normal:
|
||||
lock (persistentPool)
|
||||
{
|
||||
if (persistentPool.Remove(handle))
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (type == GCHandleType.Pinned)
|
||||
{
|
||||
lock (poolLock)
|
||||
break;
|
||||
case GCHandleType.Pinned:
|
||||
lock (pinnedPool)
|
||||
{
|
||||
if (pinnedPool.Remove(handle, out GCHandle gchandle))
|
||||
if (pinnedPool.Remove(handle, out GCHandle gcHandle))
|
||||
{
|
||||
gchandle.Free();
|
||||
gcHandle.Free();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
case GCHandleType.Weak:
|
||||
case GCHandleType.WeakTrackResurrection:
|
||||
lock (weakPoolLock)
|
||||
TryCollectWeakHandles();
|
||||
return;
|
||||
|
||||
}
|
||||
throw new NativeInteropException("Invalid ManagedHandle");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,14 +200,13 @@ namespace FlaxEngine.Interop
|
||||
public void FromManaged(Array managed)
|
||||
{
|
||||
if (managed != null)
|
||||
managedArray = ManagedArray.WrapPooledArray(managed);
|
||||
(handle, managedArray) = ManagedArray.WrapPooledArray(managed);
|
||||
}
|
||||
|
||||
public IntPtr ToUnmanaged()
|
||||
{
|
||||
if (managedArray == null)
|
||||
return IntPtr.Zero;
|
||||
handle = ManagedHandle.Alloc(managedArray, GCHandleType.Weak);
|
||||
return ManagedHandle.ToIntPtr(handle);
|
||||
}
|
||||
|
||||
@@ -216,7 +215,6 @@ namespace FlaxEngine.Interop
|
||||
if (managedArray == null)
|
||||
return;
|
||||
managedArray.FreePooled();
|
||||
//handle.Free(); // No need to free weak handles
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +333,6 @@ namespace FlaxEngine.Interop
|
||||
#endif
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedIn, typeof(ArrayMarshaller<,>.ManagedToNative))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedOut, typeof(ArrayMarshaller<,>.ManagedToNative))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementIn, typeof(ArrayMarshaller<,>.ManagedToNative))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedOut, typeof(ArrayMarshaller<,>.NativeToManaged))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedIn, typeof(ArrayMarshaller<,>.NativeToManaged))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementOut, typeof(ArrayMarshaller<,>.NativeToManaged))]
|
||||
@@ -388,38 +385,28 @@ namespace FlaxEngine.Interop
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
#endif
|
||||
public static class ManagedToNative
|
||||
public ref struct ManagedToNative
|
||||
{
|
||||
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[] managed, out int numElements)
|
||||
T[] sourceArray;
|
||||
ManagedArray managedArray;
|
||||
ManagedHandle managedHandle;
|
||||
|
||||
public void FromManaged(T[] managed)
|
||||
{
|
||||
if (managed is null)
|
||||
{
|
||||
numElements = 0;
|
||||
return null;
|
||||
}
|
||||
numElements = managed.Length;
|
||||
ManagedArray managedArray = ManagedArray.AllocatePooledArray<TUnmanagedElement>(managed.Length);
|
||||
return (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedArray, GCHandleType.Normal);
|
||||
}
|
||||
|
||||
public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed) => managed;
|
||||
|
||||
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
|
||||
{
|
||||
if (unmanaged == null)
|
||||
return Span<TUnmanagedElement>.Empty;
|
||||
ManagedArray managedArray = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target);
|
||||
return managedArray.ToSpan<TUnmanagedElement>();
|
||||
}
|
||||
|
||||
public static void Free(TUnmanagedElement* unmanaged)
|
||||
{
|
||||
if (unmanaged == null)
|
||||
return;
|
||||
ManagedHandle handle = ManagedHandle.FromIntPtr(new IntPtr(unmanaged));
|
||||
(Unsafe.As<ManagedArray>(handle.Target)).FreePooled();
|
||||
//handle.Free(); // No need to free weak handles
|
||||
|
||||
sourceArray = managed;
|
||||
(managedHandle, managedArray) = ManagedArray.AllocatePooledArray<TUnmanagedElement>(managed.Length);
|
||||
}
|
||||
|
||||
public ReadOnlySpan<T> GetManagedValuesSource() => sourceArray;
|
||||
|
||||
public Span<TUnmanagedElement> GetUnmanagedValuesDestination() => managedArray.ToSpan<TUnmanagedElement>();
|
||||
|
||||
public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedHandle);
|
||||
|
||||
public void Free() => managedArray.FreePooled();
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
@@ -427,26 +414,25 @@ namespace FlaxEngine.Interop
|
||||
#endif
|
||||
public struct Bidirectional
|
||||
{
|
||||
T[] managedArray;
|
||||
ManagedArray unmanagedArray;
|
||||
T[] sourceArray;
|
||||
ManagedArray managedArray;
|
||||
ManagedHandle handle;
|
||||
|
||||
public void FromManaged(T[] managed)
|
||||
{
|
||||
if (managed == null)
|
||||
return;
|
||||
managedArray = managed;
|
||||
unmanagedArray = ManagedArray.AllocatePooledArray<TUnmanagedElement>(managed.Length);
|
||||
handle = ManagedHandle.Alloc(unmanagedArray);
|
||||
sourceArray = managed;
|
||||
(handle, managedArray) = ManagedArray.AllocatePooledArray<TUnmanagedElement>(managed.Length);
|
||||
}
|
||||
|
||||
public ReadOnlySpan<T> GetManagedValuesSource() => managedArray;
|
||||
public ReadOnlySpan<T> GetManagedValuesSource() => sourceArray;
|
||||
|
||||
public Span<TUnmanagedElement> GetUnmanagedValuesDestination()
|
||||
{
|
||||
if (unmanagedArray == null)
|
||||
if (managedArray == null)
|
||||
return Span<TUnmanagedElement>.Empty;
|
||||
return unmanagedArray.ToSpan<TUnmanagedElement>();
|
||||
return managedArray.ToSpan<TUnmanagedElement>();
|
||||
}
|
||||
|
||||
public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)ManagedHandle.ToIntPtr(handle);
|
||||
@@ -454,26 +440,22 @@ namespace FlaxEngine.Interop
|
||||
public void FromUnmanaged(TUnmanagedElement* unmanaged)
|
||||
{
|
||||
ManagedArray arr = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(new IntPtr(unmanaged)).Target);
|
||||
if (managedArray == null || managedArray.Length != arr.Length)
|
||||
managedArray = new T[arr.Length];
|
||||
if (sourceArray == null || sourceArray.Length != arr.Length)
|
||||
sourceArray = new T[arr.Length];
|
||||
}
|
||||
|
||||
public ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int numElements)
|
||||
{
|
||||
if (unmanagedArray == null)
|
||||
if (managedArray == null)
|
||||
return ReadOnlySpan<TUnmanagedElement>.Empty;
|
||||
return unmanagedArray.ToSpan<TUnmanagedElement>();
|
||||
return managedArray.ToSpan<TUnmanagedElement>();
|
||||
}
|
||||
|
||||
public Span<T> GetManagedValuesDestination(int numElements) => managedArray;
|
||||
public Span<T> GetManagedValuesDestination(int numElements) => sourceArray;
|
||||
|
||||
public T[] ToManaged() => managedArray;
|
||||
public T[] ToManaged() => sourceArray;
|
||||
|
||||
public void Free()
|
||||
{
|
||||
unmanagedArray.FreePooled();
|
||||
handle.Free();
|
||||
}
|
||||
public void Free() => managedArray.FreePooled();
|
||||
}
|
||||
|
||||
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[] managed, out int numElements)
|
||||
@@ -484,9 +466,8 @@ namespace FlaxEngine.Interop
|
||||
return null;
|
||||
}
|
||||
numElements = managed.Length;
|
||||
ManagedArray managedArray = ManagedArray.AllocatePooledArray<TUnmanagedElement>(managed.Length);
|
||||
IntPtr handle = ManagedHandle.ToIntPtr(managedArray);
|
||||
return (TUnmanagedElement*)handle;
|
||||
(ManagedHandle managedArrayHandle, _) = ManagedArray.AllocatePooledArray<TUnmanagedElement>(managed.Length);
|
||||
return (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedArrayHandle);
|
||||
}
|
||||
|
||||
public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed) => managed;
|
||||
@@ -517,7 +498,6 @@ namespace FlaxEngine.Interop
|
||||
return;
|
||||
ManagedHandle handle = ManagedHandle.FromIntPtr(new IntPtr(unmanaged));
|
||||
Unsafe.As<ManagedArray>(handle.Target).FreePooled();
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -526,7 +526,7 @@ namespace FlaxEngine.Interop
|
||||
{
|
||||
Type elementType = Unsafe.As<Type>(typeHandle.Target);
|
||||
Type marshalledType = ArrayFactory.GetMarshalledType(elementType);
|
||||
Type arrayType = elementType.MakeArrayType();
|
||||
Type arrayType = ArrayFactory.GetArrayType(elementType);
|
||||
if (marshalledType.IsValueType)
|
||||
{
|
||||
ManagedArray managedArray = ManagedArray.AllocateNewArray((int)size, arrayType, marshalledType);
|
||||
@@ -544,7 +544,7 @@ namespace FlaxEngine.Interop
|
||||
internal static ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle)
|
||||
{
|
||||
Type elementType = Unsafe.As<Type>(elementTypeHandle.Target);
|
||||
Type classType = elementType.MakeArrayType();
|
||||
Type classType = ArrayFactory.GetArrayType(elementType);
|
||||
return GetTypeGCHandle(classType);
|
||||
}
|
||||
|
||||
@@ -650,7 +650,6 @@ namespace FlaxEngine.Interop
|
||||
if (!type.IsValueType)
|
||||
return ManagedHandle.ToIntPtr(handle);
|
||||
|
||||
// HACK: Get the address of a non-pinned value
|
||||
return ValueTypeUnboxer.GetPointer(value, type);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ using FlaxEngine.Assertions;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace FlaxEngine.Interop
|
||||
{
|
||||
@@ -967,6 +968,7 @@ namespace FlaxEngine.Interop
|
||||
private delegate Array CreateArrayDelegate(long size);
|
||||
|
||||
private static ConcurrentDictionary<Type, Type> marshalledTypes = new ConcurrentDictionary<Type, Type>(1, 3);
|
||||
private static ConcurrentDictionary<Type, Type> arrayTypes = new ConcurrentDictionary<Type, Type>(1, 3);
|
||||
private static ConcurrentDictionary<Type, CreateArrayDelegate> createArrayDelegates = new ConcurrentDictionary<Type, CreateArrayDelegate>(1, 3);
|
||||
|
||||
internal static Type GetMarshalledType(Type elementType)
|
||||
@@ -986,6 +988,15 @@ namespace FlaxEngine.Interop
|
||||
return marshalledTypes.GetOrAdd(elementType, Factory);
|
||||
}
|
||||
|
||||
internal static Type GetArrayType(Type elementType)
|
||||
{
|
||||
static Type Factory(Type type) => type.MakeArrayType();
|
||||
|
||||
if (arrayTypes.TryGetValue(elementType, out var arrayType))
|
||||
return arrayType;
|
||||
return arrayTypes.GetOrAdd(elementType, Factory);
|
||||
}
|
||||
|
||||
internal static Array CreateArray(Type type, long size)
|
||||
{
|
||||
static CreateArrayDelegate Factory(Type type)
|
||||
@@ -1009,44 +1020,78 @@ namespace FlaxEngine.Interop
|
||||
|
||||
internal static class ValueTypeUnboxer
|
||||
{
|
||||
private delegate IntPtr UnboxerDelegate(object value);
|
||||
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 uint pinnedAllocationsPointer = 0;
|
||||
|
||||
private delegate TInternal ToNativeDelegate<T, TInternal>(T value);
|
||||
private delegate IntPtr UnboxerDelegate(object value, object converter);
|
||||
|
||||
private static ConcurrentDictionary<Type, UnboxerDelegate> unboxers = new ConcurrentDictionary<Type, UnboxerDelegate>(1, 3);
|
||||
private static ConcurrentDictionary<Type, (UnboxerDelegate deleg, object toNativeDeleg)> 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);
|
||||
|
||||
internal static IntPtr GetPointer(object value, Type type)
|
||||
{
|
||||
if (!unboxers.TryGetValue(type, out var deleg))
|
||||
if (!unboxers.TryGetValue(type, out var tuple))
|
||||
{
|
||||
// Non-POD structures use internal layout (eg. SpriteHandleManaged in C++ with SpriteHandleMarshaller.SpriteHandleInternal in C#) so convert C# data into C++ data
|
||||
var attr = type.GetCustomAttribute<System.Runtime.InteropServices.Marshalling.NativeMarshallingAttribute>();
|
||||
var toNativeMethod = attr?.NativeType.GetMethod("ToNative", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
if (toNativeMethod != null)
|
||||
{
|
||||
deleg = unboxerToNativeMethod.MakeGenericMethod(toNativeMethod.ReturnType).CreateDelegate<UnboxerDelegate>();
|
||||
tuple.deleg = unboxerToNativeMethod.MakeGenericMethod(type, toNativeMethod.ReturnType).CreateDelegate<UnboxerDelegate>();
|
||||
tuple.toNativeDeleg = toNativeMethod.CreateDelegate(typeof(ToNativeDelegate<,>).MakeGenericType(type, toNativeMethod.ReturnType));
|
||||
}
|
||||
else
|
||||
{
|
||||
deleg = unboxerMethod.MakeGenericMethod(type).CreateDelegate<UnboxerDelegate>();
|
||||
tuple.deleg = unboxerMethod.MakeGenericMethod(type).CreateDelegate<UnboxerDelegate>();
|
||||
}
|
||||
deleg = unboxers.GetOrAdd(type, deleg);
|
||||
tuple = unboxers.GetOrAdd(type, tuple);
|
||||
}
|
||||
return deleg(value);
|
||||
return tuple.deleg(value, tuple.toNativeDeleg);
|
||||
}
|
||||
|
||||
private static IntPtr UnboxPointer<T>(object value) where T : struct
|
||||
private static void PinValue(object value)
|
||||
{
|
||||
// Prevent garbage collector from relocating the boxed value by pinning it temporarily.
|
||||
// The pointer should remain valid quite long time but will be eventually unpinned.
|
||||
uint index = Interlocked.Increment(ref pinnedBoxedValuesPointer) % (uint)pinnedBoxedValues.Length;
|
||||
ref GCHandle handle = ref pinnedBoxedValues[index];
|
||||
if (handle.IsAllocated)
|
||||
handle.Free();
|
||||
handle = GCHandle.Alloc(value, GCHandleType.Pinned);
|
||||
}
|
||||
|
||||
private static IntPtr PinValue<T>(T value) where T : struct
|
||||
{
|
||||
// Store the converted value in unmanaged memory so it will not be relocated by the garbage collector.
|
||||
int size = Unsafe.SizeOf<T>();
|
||||
uint index = Interlocked.Increment(ref pinnedAllocationsPointer) % (uint)pinnedAllocations.Length;
|
||||
ref (IntPtr ptr, int size) alloc = ref pinnedAllocations[index];
|
||||
if (alloc.size < size)
|
||||
{
|
||||
if (alloc.ptr != IntPtr.Zero)
|
||||
NativeFree(alloc.ptr.ToPointer());
|
||||
alloc.ptr = new IntPtr(NativeAlloc(size));
|
||||
alloc.size = size;
|
||||
}
|
||||
|
||||
Unsafe.Write<T>(alloc.ptr.ToPointer(), value);
|
||||
return alloc.ptr;
|
||||
}
|
||||
|
||||
private static IntPtr UnboxPointer<T>(object value, object converter) where T : struct
|
||||
{
|
||||
PinValue(value);
|
||||
return new IntPtr(Unsafe.AsPointer(ref Unsafe.Unbox<T>(value)));
|
||||
}
|
||||
|
||||
private static IntPtr UnboxPointerWithConverter<T>(object value) where T : struct
|
||||
private static IntPtr UnboxPointerWithConverter<T, TInternal>(object value, object converter) where T : struct where TInternal : struct
|
||||
{
|
||||
var type = value.GetType();
|
||||
var attr = type.GetCustomAttribute<System.Runtime.InteropServices.Marshalling.NativeMarshallingAttribute>();
|
||||
var toNative = attr.NativeType.GetMethod("ToNative", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
value = toNative.Invoke(null, new[] { value });
|
||||
return new IntPtr(Unsafe.AsPointer(ref Unsafe.Unbox<T>(value)));
|
||||
ToNativeDelegate<T, TInternal> toNative = Unsafe.As<ToNativeDelegate<T, TInternal>>(converter);
|
||||
return PinValue<TInternal>(toNative(Unsafe.Unbox<T>(value)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1127,13 +1127,6 @@ bool NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHa
|
||||
rpc.Info = *info;
|
||||
rpc.ArgsData.Copy(Span<byte>(argsStream->GetBuffer(), argsStream->GetPosition()));
|
||||
rpc.Targets.Copy(targetIds);
|
||||
#if USE_EDITOR || !BUILD_RELEASE
|
||||
auto it = Objects.Find(obj->GetID());
|
||||
if (it == Objects.End())
|
||||
{
|
||||
LOG(Error, "Cannot invoke RPC method '{0}.{1}' on object '{2}' that is not registered in networking (use 'NetworkReplicator.AddObject').", type.ToString(), String(name), obj->GetID());
|
||||
}
|
||||
#endif
|
||||
ObjectsLock.Unlock();
|
||||
|
||||
// Check if skip local execution (eg. server rpc called from client or client rpc with specific targets)
|
||||
@@ -1554,7 +1547,13 @@ void NetworkInternal::NetworkReplicatorUpdate()
|
||||
continue;
|
||||
auto it = Objects.Find(obj->GetID());
|
||||
if (it == Objects.End())
|
||||
{
|
||||
#if USE_EDITOR || !BUILD_RELEASE
|
||||
if (!DespawnedObjects.Contains(obj->GetID()))
|
||||
LOG(Error, "Cannot invoke RPC method '{0}.{1}' on object '{2}' that is not registered in networking (use 'NetworkReplicator.AddObject').", e.Name.First.ToString(), String(e.Name.Second), obj->GetID());
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
auto& item = it->Item;
|
||||
|
||||
// Send RPC message
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
#include "Engine/Particles/Types.h"
|
||||
#include "Engine/Particles/ParticlesSimulation.h"
|
||||
#include "Engine/Particles/ParticlesData.h"
|
||||
#include "Engine/Core/Types/CommonValue.h"
|
||||
#include "Engine/Core/Math/Vector4.h"
|
||||
#include "Engine/Core/Types/BaseTypes.h"
|
||||
|
||||
class ParticleEffect;
|
||||
|
||||
|
||||
@@ -179,6 +179,18 @@ namespace FlaxEngine
|
||||
Internal_Destroy(GetUnmanagedPtr(obj), timeLeft);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys the specified object and clears the reference variable.
|
||||
/// The object obj will be destroyed immediately.
|
||||
/// If obj is a Script it will be removed from the Actor and deleted.
|
||||
/// If obj is an Actor it will be removed from the Scene and deleted as well as all its Scripts and all children of the Actor.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to destroy.</param>
|
||||
public static void DestroyNow(Object obj)
|
||||
{
|
||||
Internal_DestroyNow(GetUnmanagedPtr(obj));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys the specified object and clears the reference variable.
|
||||
/// The object obj will be destroyed now or after the time specified in seconds from now.
|
||||
@@ -316,6 +328,9 @@ namespace FlaxEngine
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_Destroy", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
|
||||
internal static partial void Internal_Destroy(IntPtr obj, float timeLeft);
|
||||
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_DestroyNow", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
|
||||
internal static partial void Internal_DestroyNow(IntPtr obj);
|
||||
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_GetTypeName", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
|
||||
internal static partial string Internal_GetTypeName(IntPtr obj);
|
||||
|
||||
|
||||
@@ -878,7 +878,7 @@ MMethod* MClass::GetMethod(const char* name, int32 numParams) const
|
||||
GetMethods();
|
||||
for (int32 i = 0; i < _methods.Count(); i++)
|
||||
{
|
||||
if (_methods[i]->GetName() == name && _methods[i]->GetParametersCount() == numParams)
|
||||
if (_methods[i]->GetParametersCount() == numParams && _methods[i]->GetName() == name)
|
||||
return _methods[i];
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
@@ -742,6 +742,12 @@ DEFINE_INTERNAL_CALL(void) ObjectInternal_Destroy(ScriptingObject* obj, float ti
|
||||
obj->DeleteObject(timeLeft, useGameTime);
|
||||
}
|
||||
|
||||
DEFINE_INTERNAL_CALL(void) ObjectInternal_DestroyNow(ScriptingObject* obj)
|
||||
{
|
||||
if (obj)
|
||||
obj->DeleteObjectNow();
|
||||
}
|
||||
|
||||
DEFINE_INTERNAL_CALL(MString*) ObjectInternal_GetTypeName(ScriptingObject* obj)
|
||||
{
|
||||
INTERNAL_CALL_CHECK_RETURN(obj, nullptr);
|
||||
@@ -838,6 +844,7 @@ public:
|
||||
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceCreated", &ObjectInternal_ManagedInstanceCreated);
|
||||
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceDeleted", &ObjectInternal_ManagedInstanceDeleted);
|
||||
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Destroy", &ObjectInternal_Destroy);
|
||||
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_DestroyNow", &ObjectInternal_DestroyNow);
|
||||
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_GetTypeName", &ObjectInternal_GetTypeName);
|
||||
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_FindObject", &ObjectInternal_FindObject);
|
||||
ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_TryFindObject", &ObjectInternal_TryFindObject);
|
||||
|
||||
Reference in New Issue
Block a user