Bez popisu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FunctionPointerTests.cs 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. using System;
  2. using System.Runtime.InteropServices;
  3. using AOT;
  4. using NUnit.Framework;
  5. using Unity.Burst;
  6. using UnityEngine;
  7. using UnityEngine.TestTools;
  8. #if UNITY_2021_2_OR_NEWER
  9. using System.Runtime.CompilerServices;
  10. using Unity.Collections;
  11. using Unity.Collections.LowLevel.Unsafe;
  12. using Unity.Jobs;
  13. #if UNITY_EDITOR
  14. using OverloadedFunctionPointers;
  15. #endif
  16. #endif
  17. [TestFixture, BurstCompile]
  18. public class FunctionPointerTests
  19. {
  20. [BurstCompile(CompileSynchronously = true)]
  21. private static T StaticFunctionNoArgsGenericReturnType<T>()
  22. {
  23. return default;
  24. }
  25. private delegate int DelegateNoArgsIntReturnType();
  26. [Test]
  27. public void TestCompileFunctionPointerNoArgsGenericReturnType()
  28. {
  29. Assert.Throws<InvalidOperationException>(
  30. () => BurstCompiler.CompileFunctionPointer<DelegateNoArgsIntReturnType>(StaticFunctionNoArgsGenericReturnType<int>),
  31. "The method `Int32 StaticFunctionNoArgsGenericReturnType[Int32]()` must be a non-generic method");
  32. }
  33. [BurstCompile(CompileSynchronously = true)]
  34. private static int StaticFunctionConcreteReturnType()
  35. {
  36. return default;
  37. }
  38. private delegate T DelegateGenericReturnType<T>();
  39. [Test]
  40. public void TestCompileFunctionPointerDelegateNoArgsGenericReturnType()
  41. {
  42. Assert.Throws<InvalidOperationException>(
  43. () => BurstCompiler.CompileFunctionPointer<DelegateGenericReturnType<int>>(StaticFunctionConcreteReturnType),
  44. "The delegate type `FunctionPointerTests+DelegateGenericReturnType`1[System.Int32]` must be a non-generic type");
  45. }
  46. private static class GenericClass<T>
  47. {
  48. public delegate int DelegateNoArgsIntReturnType();
  49. }
  50. [Test]
  51. public void TestCompileFunctionPointerDelegateNoArgsGenericDeclaringType()
  52. {
  53. Assert.Throws<InvalidOperationException>(
  54. () => BurstCompiler.CompileFunctionPointer<GenericClass<int>.DelegateNoArgsIntReturnType>(StaticFunctionConcreteReturnType),
  55. "The delegate type `FunctionPointerTests+GenericClass`1+DelegateNoArgsIntReturnType[System.Int32]` must be a non-generic type");
  56. }
  57. // Doesn't work with IL2CPP yet - waiting for Unity fix to land. Once it does, remove `&& UNITY_EDITOR`
  58. #if UNITY_2021_2_OR_NEWER && UNITY_EDITOR
  59. [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
  60. [BurstCompile]
  61. private static int CSharpFunctionPointerCallback(int value) => value * 2;
  62. [BurstCompile(CompileSynchronously = true)]
  63. public unsafe struct StructWithCSharpFunctionPointer : IJob
  64. {
  65. [NativeDisableUnsafePtrRestriction]
  66. [ReadOnly]
  67. public IntPtr Callback;
  68. [ReadOnly]
  69. public NativeArray<int> Input;
  70. [WriteOnly]
  71. public NativeArray<int> Output;
  72. public void Execute()
  73. {
  74. delegate* unmanaged[Cdecl]<int, int> callback = (delegate* unmanaged[Cdecl]<int, int>)Callback;
  75. Output[0] = callback(Input[0]);
  76. }
  77. }
  78. [Test]
  79. public unsafe void CSharpFunctionPointerInsideJobStructTest()
  80. {
  81. using (var input = new NativeArray<int>(new int[1] { 40 }, Allocator.Persistent))
  82. using (var output = new NativeArray<int>(new int[1], Allocator.Persistent))
  83. {
  84. delegate* unmanaged[Cdecl]<int, int> callback = &CSharpFunctionPointerCallback;
  85. var job = new StructWithCSharpFunctionPointer
  86. {
  87. Callback = (IntPtr)callback,
  88. Input = input,
  89. Output = output
  90. };
  91. job.Run();
  92. Assert.AreEqual(40 * 2, output[0]);
  93. }
  94. }
  95. [Test]
  96. public unsafe void CSharpFunctionPointerInStaticMethodSignature()
  97. {
  98. var fp = BurstCompiler.CompileFunctionPointer<DelegateWithCSharpFunctionPointerParameter>(EntryPointWithCSharpFunctionPointerParameter);
  99. delegate* unmanaged[Cdecl]<int, int> callback = &CSharpFunctionPointerCallback;
  100. var result = fp.Invoke((IntPtr)callback);
  101. Assert.AreEqual(10, result);
  102. }
  103. [BurstCompile(CompileSynchronously = true)]
  104. private static unsafe int EntryPointWithCSharpFunctionPointerParameter(IntPtr callback)
  105. {
  106. delegate* unmanaged[Cdecl]<int, int> typedCallback = (delegate* unmanaged[Cdecl]<int, int>)callback;
  107. return typedCallback(5);
  108. }
  109. private unsafe delegate int DelegateWithCSharpFunctionPointerParameter(IntPtr callback);
  110. [Test]
  111. public unsafe void FunctionPointerReturnedFromBurstFunction()
  112. {
  113. var fp = BurstCompiler.CompileFunctionPointer<DelegateWithCSharpFunctionPointerReturn>(EntryPointWithCSharpFunctionPointerReturn);
  114. var fpInner = fp.Invoke();
  115. delegate* unmanaged[Cdecl]<float, float, float, float, float, float, float> callback = (delegate* unmanaged[Cdecl]<float, float, float, float, float, float, float>)fpInner;
  116. var result = callback(1, 2, 4, 8, 16, 32);
  117. Assert.AreEqual((float)(1 + 2 + 4 + 8 + 16 + 32), result);
  118. }
  119. [BurstCompile(CompileSynchronously = true)]
  120. private static unsafe IntPtr EntryPointWithCSharpFunctionPointerReturn()
  121. {
  122. delegate* unmanaged[Cdecl]<float, float, float, float, float, float, float> fp = &EntryPointWithCSharpFunctionPointerReturnHelper;
  123. return (IntPtr)fp;
  124. }
  125. [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
  126. [BurstCompile(CompileSynchronously = true)]
  127. private static unsafe float EntryPointWithCSharpFunctionPointerReturnHelper(float p1, float p2, float p3, float p4, float p5, float p6)
  128. {
  129. return p1 + p2 + p3 + p4 + p5 + p6;
  130. }
  131. [BurstCompile]
  132. [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvCdecl)})]
  133. static long UnmanagedFunction(long burstCount) => 1;
  134. [BurstCompile]
  135. static unsafe void GetUnmanagedCallableWithReturn(out Callable fn)
  136. {
  137. fn = Callable.Create<long, long>(&UnmanagedFunction);
  138. }
  139. [Test]
  140. [UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
  141. public void CallOverloadedFunctionWithFpArg()
  142. {
  143. GetUnmanagedCallableWithReturn(out var a);
  144. Assert.AreEqual(3, a.Value);
  145. }
  146. private delegate int Doer(int x);
  147. static int DoCompileFunctionPointerNestedStaticMethod(int x)
  148. {
  149. [BurstCompile]
  150. static int DoIt(int x) => x * 2 - 1;
  151. return BurstCompiler.CompileFunctionPointer<Doer>(DoIt).Invoke(x);
  152. }
  153. [Test]
  154. public void TestCompileFunctionPointerNestedStaticMethod()
  155. {
  156. Assert.AreEqual(3, DoCompileFunctionPointerNestedStaticMethod(2));
  157. }
  158. private unsafe delegate IntPtr DelegateWithCSharpFunctionPointerReturn();
  159. // Note that there are 6 float parameters to try to catch any issues with calling conventions.
  160. private unsafe delegate float DelegateWithCSharpFunctionPointerReturnHelper(float p1, float p2, float p3, float p4, float p5, float p6);
  161. #endif
  162. [Test]
  163. public void TestDelegateWithCustomAttributeThatIsNotUnmanagedFunctionPointerAttribute()
  164. {
  165. var fp = BurstCompiler.CompileFunctionPointer<TestDelegateWithCustomAttributeThatIsNotUnmanagedFunctionPointerAttributeDelegate>(TestDelegateWithCustomAttributeThatIsNotUnmanagedFunctionPointerAttributeHelper);
  166. var result = fp.Invoke(42);
  167. Assert.AreEqual(43, result);
  168. }
  169. [BurstCompile(CompileSynchronously = true)]
  170. private static int TestDelegateWithCustomAttributeThatIsNotUnmanagedFunctionPointerAttributeHelper(int x) => x + 1;
  171. [MyCustomAttribute("Foo")]
  172. private delegate int TestDelegateWithCustomAttributeThatIsNotUnmanagedFunctionPointerAttributeDelegate(int x);
  173. private sealed class MyCustomAttributeAttribute : Attribute
  174. {
  175. public MyCustomAttributeAttribute(string param) { }
  176. }
  177. }
  178. #if UNITY_2021_2_OR_NEWER
  179. // UnmanagedCallersOnlyAttribute is new in .NET 5.0. This attribute is required
  180. // when you declare an unmanaged function pointer with an explicit calling convention.
  181. // Fortunately, Roslyn lets us declare the attribute class ourselves, and it will be used.
  182. // Users will need this same declaration in their own projects, in order to use
  183. // C# 9.0 function pointers.
  184. namespace System.Runtime.InteropServices
  185. {
  186. [AttributeUsage(System.AttributeTargets.Method, Inherited = false)]
  187. public sealed class UnmanagedCallersOnlyAttribute : Attribute
  188. {
  189. public Type[] CallConvs;
  190. }
  191. }
  192. #endif