_net8 wip

This commit is contained in:
2024-03-17 15:18:09 +02:00
parent 81f94e08a9
commit 6d9c6fecf7
2 changed files with 290 additions and 32 deletions

View File

@@ -766,42 +766,233 @@ namespace FlaxEngine.Interop
#endif
{
// Slow path, method parameters needs to be stored in heap
// TODO: Benchmark again against the fast path
object returnObject;
object instance = instanceHandle.IsAllocated ? instanceHandle.Target : null;
int numParams = methodHolder.parameterTypes.Length;
object[] methodParameters = new object[numParams];
for (int i = 0; i < numParams; i++)
{
IntPtr nativePtr = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * i)).ToPointer());
methodParameters[i] = MarshalToManaged(nativePtr, methodHolder.parameterTypes[i]);
}
//if (numParams > maxnumpars)
// maxnumpars = numParams;
try
#if false
if (numParams > 4)
#else
if (true)
#endif
{
returnObject = methodHolder.method.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, methodParameters);
}
catch (Exception exception)
{
// The internal exception thrown in MethodInfo.Invoke is caught here
Exception realException = exception;
if (exception.InnerException != null && exception.TargetSite.ReflectedType.Name == "MethodInvoker")
realException = exception.InnerException;
if (exceptionPtr != IntPtr.Zero)
Unsafe.Write<IntPtr>(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(realException, GCHandleType.Weak));
else
throw realException;
return IntPtr.Zero;
}
// Marshal reference parameters back to original unmanaged references
for (int i = 0; i < numParams; i++)
{
Type parameterType = methodHolder.parameterTypes[i];
if (parameterType.IsByRef)
object[] methodParameters = new object[numParams];
for (int i = 0; i < numParams; i++)
{
IntPtr nativePtr = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * i)).ToPointer());
MarshalToNative(methodParameters[i], nativePtr, parameterType.GetElementType());
methodParameters[i] = MarshalToManaged(nativePtr, methodHolder.parameterTypes[i]);
}
try
{
returnObject = methodHolder.method.Invoke(instance, methodParameters);
}
catch (Exception exception)
{
// The internal exception thrown in MethodInfo.Invoke is caught here
Exception realException = exception;
if (exception.InnerException != null && exception.TargetSite.ReflectedType.Name == "MethodInvoker")
realException = exception.InnerException;
if (exceptionPtr != IntPtr.Zero)
Unsafe.Write<IntPtr>(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(realException, GCHandleType.Weak));
else
throw realException;
return IntPtr.Zero;
}
// 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());
}
}
}
else
{
if (numParams == 0)
{
try
{
returnObject = methodHolder.methodInvoker.Invoke(instance);
}
catch (Exception exception)
{
// The internal exception thrown in MethodInfo.Invoke is caught here
Exception realException = exception;
if (exception.InnerException != null && exception.TargetSite.ReflectedType.Name == "MethodInvoker")
realException = exception.InnerException;
if (exceptionPtr != IntPtr.Zero)
Unsafe.Write<IntPtr>(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(realException, GCHandleType.Weak));
else
throw realException;
return IntPtr.Zero;
}
}
else if (numParams == 1)
{
IntPtr nativePtr1 = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * 0)).ToPointer());
Span<object?> paramSpan = [
MarshalToManaged(nativePtr1, methodHolder.parameterTypes[0])
];
try
{
returnObject = methodHolder.methodInvoker.Invoke(instance, paramSpan);
}
catch (Exception exception)
{
// The internal exception thrown in MethodInfo.Invoke is caught here
Exception realException = exception;
if (exception.InnerException != null && exception.TargetSite.ReflectedType.Name == "MethodInvoker")
realException = exception.InnerException;
if (exceptionPtr != IntPtr.Zero)
Unsafe.Write<IntPtr>(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(realException, GCHandleType.Weak));
else
throw realException;
return IntPtr.Zero;
}
// 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(paramSpan[i], nativePtr, parameterType.GetElementType());
}
}
}
else if (numParams == 2)
{
IntPtr nativePtr1 = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * 0)).ToPointer());
IntPtr nativePtr2 = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * 1)).ToPointer());
Span<object?> paramSpan = [
MarshalToManaged(nativePtr1, methodHolder.parameterTypes[0]),
MarshalToManaged(nativePtr2, methodHolder.parameterTypes[1])
];
try
{
returnObject = methodHolder.methodInvoker.Invoke(instance, paramSpan);
}
catch (Exception exception)
{
// The internal exception thrown in MethodInfo.Invoke is caught here
Exception realException = exception;
if (exception.InnerException != null && exception.TargetSite.ReflectedType.Name == "MethodInvoker")
realException = exception.InnerException;
if (exceptionPtr != IntPtr.Zero)
Unsafe.Write<IntPtr>(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(realException, GCHandleType.Weak));
else
throw realException;
return IntPtr.Zero;
}
// 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(paramSpan[i], nativePtr, parameterType.GetElementType());
}
}
}
else if (numParams == 3)
{
IntPtr nativePtr1 = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * 0)).ToPointer());
IntPtr nativePtr2 = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * 1)).ToPointer());
IntPtr nativePtr3 = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * 2)).ToPointer());
Span<object?> paramSpan = [
MarshalToManaged(nativePtr1, methodHolder.parameterTypes[0]),
MarshalToManaged(nativePtr2, methodHolder.parameterTypes[1]),
MarshalToManaged(nativePtr3, methodHolder.parameterTypes[2])
];
try
{
returnObject = methodHolder.methodInvoker.Invoke(instance, paramSpan);
}
catch (Exception exception)
{
// The internal exception thrown in MethodInfo.Invoke is caught here
Exception realException = exception;
if (exception.InnerException != null && exception.TargetSite.ReflectedType.Name == "MethodInvoker")
realException = exception.InnerException;
if (exceptionPtr != IntPtr.Zero)
Unsafe.Write<IntPtr>(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(realException, GCHandleType.Weak));
else
throw realException;
return IntPtr.Zero;
}
// 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(paramSpan[i], nativePtr, parameterType.GetElementType());
}
}
}
else //if (numParams == 4)
{
IntPtr nativePtr1 = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * 0)).ToPointer());
IntPtr nativePtr2 = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * 1)).ToPointer());
IntPtr nativePtr3 = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * 2)).ToPointer());
IntPtr nativePtr4 = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * 3)).ToPointer());
Span<object?> paramSpan = [
MarshalToManaged(nativePtr1, methodHolder.parameterTypes[0]),
MarshalToManaged(nativePtr2, methodHolder.parameterTypes[1]),
MarshalToManaged(nativePtr3, methodHolder.parameterTypes[2]),
MarshalToManaged(nativePtr4, methodHolder.parameterTypes[3])
];
try
{
returnObject = methodHolder.methodInvoker.Invoke(instance, paramSpan);
}
catch (Exception exception)
{
// The internal exception thrown in MethodInfo.Invoke is caught here
Exception realException = exception;
if (exception.InnerException != null && exception.TargetSite.ReflectedType.Name == "MethodInvoker")
realException = exception.InnerException;
if (exceptionPtr != IntPtr.Zero)
Unsafe.Write<IntPtr>(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(realException, GCHandleType.Weak));
else
throw realException;
return IntPtr.Zero;
}
// 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(paramSpan[i], nativePtr, parameterType.GetElementType());
}
}
}
}

View File

@@ -116,16 +116,39 @@ namespace FlaxEngine.Interop
}
#if !USE_AOT
#if !NET8_0_OR_GREATER
// Cache offsets to frequently accessed fields of FlaxEngine.Object
private static int unmanagedPtrFieldOffset = IntPtr.Size + (Unsafe.Read<int>((typeof(FlaxEngine.Object).GetField("__unmanagedPtr", BindingFlags.Instance | BindingFlags.NonPublic).FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF);
private static int internalIdFieldOffset = IntPtr.Size + (Unsafe.Read<int>((typeof(FlaxEngine.Object).GetField("__internalId", BindingFlags.Instance | BindingFlags.NonPublic).FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF);
#endif
[UnmanagedCallersOnly]
internal static void ScriptingObjectSetInternalValues(ManagedHandle objectHandle, IntPtr unmanagedPtr, IntPtr idPtr)
{
#if NET8_0_OR_GREATER
//Object obj2 = objectHandle.Target as Object;
object obj = objectHandle.Target;
if (obj is not Object)
if (obj is not Object obj2)
return;
{
ref IntPtr fieldRef = ref SetObjectUnmanagedPtr(obj2);
fieldRef = unmanagedPtr;
}
if (idPtr != IntPtr.Zero)
{
ref Guid nativeId = ref Unsafe.AsRef<Guid>(idPtr.ToPointer());
ref Guid fieldRef = ref SetObjectInternalId(obj2);
fieldRef = nativeId;
}
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "__unmanagedPtr")]
extern static ref IntPtr SetObjectUnmanagedPtr(Object obj);
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "__internalId")]
extern static ref Guid SetObjectInternalId(Object obj);
#else
object obj = objectHandle.Target;
{
ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<IntPtr>(unmanagedPtrFieldOffset, ref obj);
fieldRef = unmanagedPtr;
@@ -136,6 +159,7 @@ namespace FlaxEngine.Interop
ref Guid fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<Guid>(internalIdFieldOffset, ref obj);
fieldRef = nativeId;
}
#endif
}
[UnmanagedCallersOnly]
@@ -155,7 +179,7 @@ namespace FlaxEngine.Interop
}
#endif
internal static void* NativeAlloc(int byteCount)
internal static void* NativeAlloc(int byteCount)
{
return NativeMemory.AlignedAlloc((UIntPtr)byteCount, 16);
}
@@ -1272,6 +1296,9 @@ namespace FlaxEngine.Interop
internal class MethodHolder
{
internal Type[] parameterTypes;
#if NET8_0_OR_GREATER
internal MethodInvoker methodInvoker;
#endif
internal MethodInfo method;
internal Type returnType;
#if !USE_AOT
@@ -1284,6 +1311,10 @@ namespace FlaxEngine.Interop
this.method = method;
returnType = method.ReturnType;
parameterTypes = method.GetParameterTypes();
#if NET8_0_OR_GREATER
methodInvoker = MethodInvoker.Create(method);
#endif
}
#if !USE_AOT
@@ -1388,7 +1419,11 @@ namespace FlaxEngine.Interop
{
internal Type type;
internal Type wrappedType;
#if NET8_0_OR_GREATER
internal MethodInvoker ctorInvoker;
#else
internal ConstructorInfo ctor;
#endif
internal IntPtr managedClassPointer; // MClass*
internal TypeHolder(Type type)
@@ -1404,7 +1439,13 @@ namespace FlaxEngine.Interop
wrappedType = abstractWrapper;
}
#if NET8_0_OR_GREATER
ConstructorInfo ctor = wrappedType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);
if (ctor != null)
ctorInvoker = MethodInvoker.Create(ctor);
#else
ctor = wrappedType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);
#endif
}
internal object CreateObject()
@@ -1416,9 +1457,29 @@ namespace FlaxEngine.Interop
internal object CreateScriptingObject(IntPtr unmanagedPtr, IntPtr idPtr)
{
object obj = RuntimeHelpers.GetUninitializedObject(wrappedType);
#if NET8_0_OR_GREATER
if (obj is Object obj2)
{
{
ref IntPtr fieldRef = ref SetObjectUnmanagedPtr(obj2);
fieldRef = unmanagedPtr;
}
if (idPtr != IntPtr.Zero)
{
ref Guid nativeId = ref Unsafe.AsRef<Guid>(idPtr.ToPointer());
ref Guid fieldRef = ref SetObjectInternalId(obj2);
fieldRef = nativeId;
}
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "__unmanagedPtr")]
extern static ref IntPtr SetObjectUnmanagedPtr(Object obj);
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "__internalId")]
extern static ref Guid SetObjectInternalId(Object obj);
}
#else
if (obj is Object)
{
// TODO: use UnsafeAccessorAttribute on .NET 8 and use this path on all platforms (including non-Desktop, see MCore::ScriptingObject::CreateScriptingObject)
{
ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<IntPtr>(unmanagedPtrFieldOffset, ref obj);
fieldRef = unmanagedPtr;
@@ -1430,8 +1491,14 @@ namespace FlaxEngine.Interop
fieldRef = nativeId;
}
}
#endif
#if NET8_0_OR_GREATER
if (ctorInvoker != null)
ctorInvoker.Invoke(obj);
#else
if (ctor != null)
ctor.Invoke(obj, null);
#endif
else
throw new NativeInteropException($"Missing empty constructor in type '{wrappedType}'.");
return obj;