123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- using System;
- using System.Runtime.InteropServices;
- using AOT;
- using NUnit.Framework;
- using Unity.Burst;
- using UnityEngine;
- using UnityEngine.TestTools;
- #if UNITY_2021_2_OR_NEWER
- using System.Runtime.CompilerServices;
- using Unity.Collections;
- using Unity.Collections.LowLevel.Unsafe;
- using Unity.Jobs;
- #if UNITY_EDITOR
- using OverloadedFunctionPointers;
- #endif
- #endif
-
- [TestFixture, BurstCompile]
- public class FunctionPointerTests
- {
- [BurstCompile(CompileSynchronously = true)]
- private static T StaticFunctionNoArgsGenericReturnType<T>()
- {
- return default;
- }
-
- private delegate int DelegateNoArgsIntReturnType();
-
- [Test]
- public void TestCompileFunctionPointerNoArgsGenericReturnType()
- {
- Assert.Throws<InvalidOperationException>(
- () => BurstCompiler.CompileFunctionPointer<DelegateNoArgsIntReturnType>(StaticFunctionNoArgsGenericReturnType<int>),
- "The method `Int32 StaticFunctionNoArgsGenericReturnType[Int32]()` must be a non-generic method");
- }
-
- [BurstCompile(CompileSynchronously = true)]
- private static int StaticFunctionConcreteReturnType()
- {
- return default;
- }
-
- private delegate T DelegateGenericReturnType<T>();
-
- [Test]
- public void TestCompileFunctionPointerDelegateNoArgsGenericReturnType()
- {
- Assert.Throws<InvalidOperationException>(
- () => BurstCompiler.CompileFunctionPointer<DelegateGenericReturnType<int>>(StaticFunctionConcreteReturnType),
- "The delegate type `FunctionPointerTests+DelegateGenericReturnType`1[System.Int32]` must be a non-generic type");
- }
-
- private static class GenericClass<T>
- {
- public delegate int DelegateNoArgsIntReturnType();
- }
-
- [Test]
- public void TestCompileFunctionPointerDelegateNoArgsGenericDeclaringType()
- {
- Assert.Throws<InvalidOperationException>(
- () => BurstCompiler.CompileFunctionPointer<GenericClass<int>.DelegateNoArgsIntReturnType>(StaticFunctionConcreteReturnType),
- "The delegate type `FunctionPointerTests+GenericClass`1+DelegateNoArgsIntReturnType[System.Int32]` must be a non-generic type");
- }
-
- // Doesn't work with IL2CPP yet - waiting for Unity fix to land. Once it does, remove `&& UNITY_EDITOR`
- #if UNITY_2021_2_OR_NEWER && UNITY_EDITOR
- [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
- [BurstCompile]
- private static int CSharpFunctionPointerCallback(int value) => value * 2;
-
- [BurstCompile(CompileSynchronously = true)]
- public unsafe struct StructWithCSharpFunctionPointer : IJob
- {
- [NativeDisableUnsafePtrRestriction]
- [ReadOnly]
- public IntPtr Callback;
-
- [ReadOnly]
- public NativeArray<int> Input;
-
- [WriteOnly]
- public NativeArray<int> Output;
-
- public void Execute()
- {
- delegate* unmanaged[Cdecl]<int, int> callback = (delegate* unmanaged[Cdecl]<int, int>)Callback;
- Output[0] = callback(Input[0]);
- }
- }
-
- [Test]
- public unsafe void CSharpFunctionPointerInsideJobStructTest()
- {
- using (var input = new NativeArray<int>(new int[1] { 40 }, Allocator.Persistent))
- using (var output = new NativeArray<int>(new int[1], Allocator.Persistent))
- {
- delegate* unmanaged[Cdecl]<int, int> callback = &CSharpFunctionPointerCallback;
-
- var job = new StructWithCSharpFunctionPointer
- {
- Callback = (IntPtr)callback,
- Input = input,
- Output = output
- };
-
- job.Run();
-
- Assert.AreEqual(40 * 2, output[0]);
- }
- }
-
- [Test]
- public unsafe void CSharpFunctionPointerInStaticMethodSignature()
- {
- var fp = BurstCompiler.CompileFunctionPointer<DelegateWithCSharpFunctionPointerParameter>(EntryPointWithCSharpFunctionPointerParameter);
- delegate* unmanaged[Cdecl]<int, int> callback = &CSharpFunctionPointerCallback;
-
- var result = fp.Invoke((IntPtr)callback);
-
- Assert.AreEqual(10, result);
- }
-
- [BurstCompile(CompileSynchronously = true)]
- private static unsafe int EntryPointWithCSharpFunctionPointerParameter(IntPtr callback)
- {
- delegate* unmanaged[Cdecl]<int, int> typedCallback = (delegate* unmanaged[Cdecl]<int, int>)callback;
- return typedCallback(5);
- }
-
- private unsafe delegate int DelegateWithCSharpFunctionPointerParameter(IntPtr callback);
-
- [Test]
- public unsafe void FunctionPointerReturnedFromBurstFunction()
- {
- var fp = BurstCompiler.CompileFunctionPointer<DelegateWithCSharpFunctionPointerReturn>(EntryPointWithCSharpFunctionPointerReturn);
-
- var fpInner = fp.Invoke();
-
- delegate* unmanaged[Cdecl]<float, float, float, float, float, float, float> callback = (delegate* unmanaged[Cdecl]<float, float, float, float, float, float, float>)fpInner;
-
- var result = callback(1, 2, 4, 8, 16, 32);
-
- Assert.AreEqual((float)(1 + 2 + 4 + 8 + 16 + 32), result);
- }
-
- [BurstCompile(CompileSynchronously = true)]
- private static unsafe IntPtr EntryPointWithCSharpFunctionPointerReturn()
- {
- delegate* unmanaged[Cdecl]<float, float, float, float, float, float, float> fp = &EntryPointWithCSharpFunctionPointerReturnHelper;
- return (IntPtr)fp;
- }
-
- [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
- [BurstCompile(CompileSynchronously = true)]
- private static unsafe float EntryPointWithCSharpFunctionPointerReturnHelper(float p1, float p2, float p3, float p4, float p5, float p6)
- {
- return p1 + p2 + p3 + p4 + p5 + p6;
- }
-
- [BurstCompile]
- [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvCdecl)})]
- static long UnmanagedFunction(long burstCount) => 1;
-
- [BurstCompile]
- static unsafe void GetUnmanagedCallableWithReturn(out Callable fn)
- {
- fn = Callable.Create<long, long>(&UnmanagedFunction);
- }
-
- [Test]
- [UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
-
- public void CallOverloadedFunctionWithFpArg()
- {
- GetUnmanagedCallableWithReturn(out var a);
- Assert.AreEqual(3, a.Value);
- }
-
- private delegate int Doer(int x);
-
- static int DoCompileFunctionPointerNestedStaticMethod(int x)
- {
- [BurstCompile]
- static int DoIt(int x) => x * 2 - 1;
-
- return BurstCompiler.CompileFunctionPointer<Doer>(DoIt).Invoke(x);
- }
-
- [Test]
- public void TestCompileFunctionPointerNestedStaticMethod()
- {
- Assert.AreEqual(3, DoCompileFunctionPointerNestedStaticMethod(2));
- }
-
- private unsafe delegate IntPtr DelegateWithCSharpFunctionPointerReturn();
-
- // Note that there are 6 float parameters to try to catch any issues with calling conventions.
- private unsafe delegate float DelegateWithCSharpFunctionPointerReturnHelper(float p1, float p2, float p3, float p4, float p5, float p6);
- #endif
-
- [Test]
- public void TestDelegateWithCustomAttributeThatIsNotUnmanagedFunctionPointerAttribute()
- {
- var fp = BurstCompiler.CompileFunctionPointer<TestDelegateWithCustomAttributeThatIsNotUnmanagedFunctionPointerAttributeDelegate>(TestDelegateWithCustomAttributeThatIsNotUnmanagedFunctionPointerAttributeHelper);
-
- var result = fp.Invoke(42);
-
- Assert.AreEqual(43, result);
- }
- [BurstCompile(CompileSynchronously = true)]
- private static int TestDelegateWithCustomAttributeThatIsNotUnmanagedFunctionPointerAttributeHelper(int x) => x + 1;
-
- [MyCustomAttribute("Foo")]
- private delegate int TestDelegateWithCustomAttributeThatIsNotUnmanagedFunctionPointerAttributeDelegate(int x);
-
- private sealed class MyCustomAttributeAttribute : Attribute
- {
- public MyCustomAttributeAttribute(string param) { }
- }
- }
-
- #if UNITY_2021_2_OR_NEWER
- // UnmanagedCallersOnlyAttribute is new in .NET 5.0. This attribute is required
- // when you declare an unmanaged function pointer with an explicit calling convention.
- // Fortunately, Roslyn lets us declare the attribute class ourselves, and it will be used.
- // Users will need this same declaration in their own projects, in order to use
- // C# 9.0 function pointers.
- namespace System.Runtime.InteropServices
- {
- [AttributeUsage(System.AttributeTargets.Method, Inherited = false)]
- public sealed class UnmanagedCallersOnlyAttribute : Attribute
- {
- public Type[] CallConvs;
- }
- }
- #endif
|