Ei kuvausta
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.

BurstCompiler.cs 36KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  1. using System;
  2. using System.Diagnostics;
  3. using System.IO;
  4. using System.Reflection;
  5. using System.Runtime.InteropServices;
  6. using System.Collections.Generic;
  7. using UnityEngine.Scripting;
  8. using System.Linq;
  9. using System.Text;
  10. namespace Unity.Burst
  11. {
  12. /// <summary>
  13. /// The burst compiler runtime frontend.
  14. /// </summary>
  15. ///
  16. public static class BurstCompiler
  17. {
  18. /// <summary>
  19. /// Check if the LoadAdditionalLibrary API is supported by the current version of Unity
  20. /// </summary>
  21. /// <returns>True if the LoadAdditionalLibrary API can be used by the current version of Unity</returns>
  22. public static bool IsLoadAdditionalLibrarySupported()
  23. {
  24. return IsApiAvailable("LoadBurstLibrary");
  25. }
  26. #if UNITY_EDITOR
  27. static unsafe BurstCompiler()
  28. {
  29. // Store pointers to Log and Compile callback methods.
  30. // For more info about why we need to do this, see comments in CallbackStubManager.
  31. string GetFunctionPointer<TDelegate>(TDelegate callback)
  32. {
  33. GCHandle.Alloc(callback); // Ensure delegate is never garbage-collected.
  34. var callbackFunctionPointer = Marshal.GetFunctionPointerForDelegate(callback);
  35. return "0x" + callbackFunctionPointer.ToInt64().ToString("X16");
  36. }
  37. EagerCompileLogCallbackFunctionPointer = GetFunctionPointer<LogCallbackDelegate>(EagerCompileLogCallback);
  38. ManagedResolverFunctionPointer = GetFunctionPointer<ManagedFnPtrResolverDelegate>(ManagedResolverFunction);
  39. ProgressCallbackFunctionPointer = GetFunctionPointer<ProgressCallbackDelegate>(ProgressCallback);
  40. ProfileBeginCallbackFunctionPointer = GetFunctionPointer<ProfileBeginCallbackDelegate>(ProfileBeginCallback);
  41. ProfileEndCallbackFunctionPointer = GetFunctionPointer<ProfileEndCallbackDelegate>(ProfileEndCallback);
  42. }
  43. #endif
  44. private class CommandBuilder
  45. {
  46. private StringBuilder _builder;
  47. private bool _hasArgs;
  48. public CommandBuilder()
  49. {
  50. _builder = new StringBuilder();
  51. _hasArgs = false;
  52. }
  53. public CommandBuilder Begin(string cmd)
  54. {
  55. _builder.Clear();
  56. _hasArgs = false;
  57. _builder.Append(cmd);
  58. return this;
  59. }
  60. public CommandBuilder With(string arg)
  61. {
  62. if (!_hasArgs) _builder.Append(' ');
  63. _hasArgs = true;
  64. _builder.Append(arg);
  65. return this;
  66. }
  67. public CommandBuilder With(IntPtr arg)
  68. {
  69. if (!_hasArgs) _builder.Append(' ');
  70. _hasArgs = true;
  71. _builder.AppendFormat("0x{0:X16}", arg.ToInt64());
  72. return this;
  73. }
  74. public CommandBuilder And(char sep = '|')
  75. {
  76. _builder.Append(sep);
  77. return this;
  78. }
  79. public string SendToCompiler()
  80. {
  81. return SendRawCommandToCompiler(_builder.ToString());
  82. }
  83. }
  84. [ThreadStatic]
  85. private static CommandBuilder _cmdBuilder;
  86. private static CommandBuilder BeginCompilerCommand(string cmd)
  87. {
  88. if (_cmdBuilder == null)
  89. {
  90. _cmdBuilder = new CommandBuilder();
  91. }
  92. return _cmdBuilder.Begin(cmd);
  93. }
  94. #if BURST_INTERNAL
  95. [ThreadStatic]
  96. public static Func<object, IntPtr> InternalCompiler;
  97. #endif
  98. /// <summary>
  99. /// Internal variable setup by BurstCompilerOptions.
  100. /// </summary>
  101. #if BURST_INTERNAL
  102. [ThreadStatic] // As we are changing this boolean via BurstCompilerOptions in btests and we are running multithread tests
  103. // we would change a global and it would generate random errors, so specifically for btests, we are using a TLS.
  104. public
  105. #else
  106. internal
  107. #endif
  108. static bool _IsEnabled;
  109. /// <summary>
  110. /// Gets a value indicating whether Burst is enabled.
  111. /// </summary>
  112. #if UNITY_EDITOR || BURST_INTERNAL
  113. public static bool IsEnabled => _IsEnabled;
  114. #else
  115. public static bool IsEnabled => _IsEnabled && BurstCompilerHelper.IsBurstGenerated;
  116. #endif
  117. /// <summary>
  118. /// Gets the global options for the burst compiler.
  119. /// </summary>
  120. public static readonly BurstCompilerOptions Options = new BurstCompilerOptions(true);
  121. /// <summary>
  122. /// Sets the execution mode for all jobs spawned from now on.
  123. /// </summary>
  124. /// <param name="mode">Specifiy the required execution mode</param>
  125. public static void SetExecutionMode(BurstExecutionEnvironment mode)
  126. {
  127. Burst.LowLevel.BurstCompilerService.SetCurrentExecutionMode((uint)mode);
  128. }
  129. /// <summary>
  130. /// Retrieve the current execution mode that is configured.
  131. /// </summary>
  132. /// <returns>Currently configured execution mode</returns>
  133. public static BurstExecutionEnvironment GetExecutionMode()
  134. {
  135. return (BurstExecutionEnvironment)Burst.LowLevel.BurstCompilerService.GetCurrentExecutionMode();
  136. }
  137. /// <summary>
  138. /// Compile the following delegate with burst and return a new delegate.
  139. /// </summary>
  140. /// <typeparam name="T"></typeparam>
  141. /// <param name="delegateMethod"></param>
  142. /// <returns></returns>
  143. /// <remarks>NOT AVAILABLE, unsafe to use</remarks>
  144. internal static unsafe T CompileDelegate<T>(T delegateMethod) where T : class
  145. {
  146. // We have added support for runtime CompileDelegate in 2018.2+
  147. void* function = Compile(delegateMethod, false);
  148. object res = System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer((IntPtr)function, delegateMethod.GetType());
  149. return (T)res;
  150. }
  151. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  152. private static void VerifyDelegateIsNotMulticast<T>(T delegateMethod) where T : class
  153. {
  154. var delegateKind = delegateMethod as Delegate;
  155. if (delegateKind.GetInvocationList().Length > 1)
  156. {
  157. throw new InvalidOperationException($"Burst does not support multicast delegates, please use a regular delegate for `{delegateMethod}'");
  158. }
  159. }
  160. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  161. private static void VerifyDelegateHasCorrectUnmanagedFunctionPointerAttribute<T>(T delegateMethod) where T : class
  162. {
  163. var attrib = delegateMethod.GetType().GetCustomAttribute<System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute>();
  164. if (attrib == null || attrib.CallingConvention != CallingConvention.Cdecl)
  165. {
  166. #if !BURST_INTERNAL
  167. UnityEngine.Debug.LogWarning($"The delegate type {delegateMethod.GetType().FullName} should be decorated with [UnmanagedFunctionPointer(CallingConvention.Cdecl)] to ensure runtime interoperabilty between managed code and Burst-compiled code.");
  168. #endif
  169. }
  170. }
  171. /// <summary>
  172. /// DO NOT USE - deprecated.
  173. /// </summary>
  174. /// <param name="burstMethodHandle">The Burst method to compile.</param>
  175. /// <param name="managedMethodHandle">The fallback managed method to use.</param>
  176. /// <param name="delegateTypeHandle">The type of the delegate used to execute these methods.</param>
  177. /// <returns>Nothing</returns>
  178. [Obsolete("This method will be removed in a future version of Burst")]
  179. public static unsafe IntPtr CompileILPPMethod(RuntimeMethodHandle burstMethodHandle, RuntimeMethodHandle managedMethodHandle, RuntimeTypeHandle delegateTypeHandle)
  180. {
  181. throw new NotImplementedException();
  182. }
  183. /// <summary>
  184. /// Compile an IL Post-Processed method.
  185. /// </summary>
  186. /// <param name="burstMethodHandle">The Burst method to compile.</param>
  187. /// <returns>A token that must be passed to <see cref="GetILPPMethodFunctionPointer2"/> to get an actual executable function pointer.</returns>
  188. public static unsafe IntPtr CompileILPPMethod2(RuntimeMethodHandle burstMethodHandle)
  189. {
  190. if (burstMethodHandle.Value == IntPtr.Zero)
  191. {
  192. throw new ArgumentNullException(nameof(burstMethodHandle));
  193. }
  194. OnCompileILPPMethod2?.Invoke();
  195. var burstMethod = (MethodInfo)MethodBase.GetMethodFromHandle(burstMethodHandle);
  196. return (IntPtr)Compile(new FakeDelegate(burstMethod), burstMethod, isFunctionPointer: true, isILPostProcessing: true);
  197. }
  198. internal static Action OnCompileILPPMethod2;
  199. /// <summary>
  200. /// DO NOT USE - deprecated.
  201. /// </summary>
  202. /// <param name="ilppMethod">The result of a previous call to <see cref="CompileILPPMethod"/>.</param>
  203. /// <returns>Nothing.</returns>
  204. [Obsolete("This method will be removed in a future version of Burst")]
  205. public static unsafe void* GetILPPMethodFunctionPointer(IntPtr ilppMethod)
  206. {
  207. throw new NotImplementedException();
  208. }
  209. /// <summary>
  210. /// For a previous call to <see cref="CompileILPPMethod2"/>, get the actual executable function pointer.
  211. /// </summary>
  212. /// <param name="ilppMethod">The result of a previous call to <see cref="CompileILPPMethod"/>.</param>
  213. /// <param name="managedMethodHandle">The fallback managed method to use.</param>
  214. /// <param name="delegateTypeHandle">The type of the delegate used to execute these methods.</param>
  215. /// <returns>A pointer into an executable region, for running the function pointer.</returns>
  216. public static unsafe void* GetILPPMethodFunctionPointer2(IntPtr ilppMethod, RuntimeMethodHandle managedMethodHandle, RuntimeTypeHandle delegateTypeHandle)
  217. {
  218. if (ilppMethod == IntPtr.Zero)
  219. {
  220. throw new ArgumentNullException(nameof(ilppMethod));
  221. }
  222. if (managedMethodHandle.Value == IntPtr.Zero)
  223. {
  224. throw new ArgumentNullException(nameof(managedMethodHandle));
  225. }
  226. if (delegateTypeHandle.Value == IntPtr.Zero)
  227. {
  228. throw new ArgumentNullException(nameof(delegateTypeHandle));
  229. }
  230. // If we are in the editor, we need to route a command to the compiler to start compiling the deferred ILPP compilation.
  231. // Otherwise if we're in Burst's internal testing, or in a player build, we already actually have the actual executable
  232. // pointer address, and we just return that.
  233. #if UNITY_EDITOR
  234. var managedMethod = (MethodInfo)MethodBase.GetMethodFromHandle(managedMethodHandle);
  235. var delegateType = Type.GetTypeFromHandle(delegateTypeHandle);
  236. var managedFallbackDelegate = Delegate.CreateDelegate(delegateType, managedMethod);
  237. var handle = GCHandle.Alloc(managedFallbackDelegate);
  238. var result =
  239. BeginCompilerCommand(BurstCompilerOptions.CompilerCommandILPPCompilation)
  240. .With(ilppMethod).And()
  241. .With(ManagedResolverFunctionPointer).And()
  242. .With(GCHandle.ToIntPtr(handle))
  243. .SendToCompiler();
  244. return new IntPtr(Convert.ToInt64(result, 16)).ToPointer();
  245. #else
  246. return ilppMethod.ToPointer();
  247. #endif
  248. }
  249. /// <summary>
  250. /// DO NOT USE - deprecated.
  251. /// </summary>
  252. /// <param name="handle">A runtime method handle.</param>
  253. /// <returns>Nothing.</returns>
  254. [Obsolete("This method will be removed in a future version of Burst")]
  255. public static unsafe void* CompileUnsafeStaticMethod(RuntimeMethodHandle handle)
  256. {
  257. throw new NotImplementedException();
  258. }
  259. /// <summary>
  260. /// Compile the following delegate into a function pointer with burst, invokable from a Burst Job or from regular C#.
  261. /// </summary>
  262. /// <typeparam name="T">Type of the delegate of the function pointer</typeparam>
  263. /// <param name="delegateMethod">The delegate to compile</param>
  264. /// <returns>A function pointer invokable from a Burst Job or from regular C#</returns>
  265. public static unsafe FunctionPointer<T> CompileFunctionPointer<T>(T delegateMethod) where T : class
  266. {
  267. VerifyDelegateIsNotMulticast<T>(delegateMethod);
  268. VerifyDelegateHasCorrectUnmanagedFunctionPointerAttribute<T>(delegateMethod);
  269. // We have added support for runtime CompileDelegate in 2018.2+
  270. void* function = Compile(delegateMethod, true);
  271. return new FunctionPointer<T>(new IntPtr(function));
  272. }
  273. [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
  274. internal class StaticTypeReinitAttribute : Attribute
  275. {
  276. public readonly Type reinitType;
  277. public StaticTypeReinitAttribute(Type toReinit)
  278. {
  279. reinitType = toReinit;
  280. }
  281. }
  282. private static unsafe void* Compile(object delegateObj, bool isFunctionPointer)
  283. {
  284. if (!(delegateObj is Delegate)) throw new ArgumentException("object instance must be a System.Delegate", nameof(delegateObj));
  285. var delegateMethod = (Delegate)delegateObj;
  286. return Compile(delegateMethod, delegateMethod.Method, isFunctionPointer, false);
  287. }
  288. private static unsafe void* Compile(object delegateObj, MethodInfo methodInfo, bool isFunctionPointer, bool isILPostProcessing)
  289. {
  290. if (delegateObj == null) throw new ArgumentNullException(nameof(delegateObj));
  291. if (delegateObj.GetType().IsGenericType)
  292. {
  293. throw new InvalidOperationException($"The delegate type `{delegateObj.GetType()}` must be a non-generic type");
  294. }
  295. if (!methodInfo.IsStatic)
  296. {
  297. throw new InvalidOperationException($"The method `{methodInfo}` must be static. Instance methods are not supported");
  298. }
  299. if (methodInfo.IsGenericMethod)
  300. {
  301. throw new InvalidOperationException($"The method `{methodInfo}` must be a non-generic method");
  302. }
  303. #if ENABLE_IL2CPP
  304. if (isFunctionPointer && !isILPostProcessing &&
  305. methodInfo.GetCustomAttributes().All(s => s.GetType().Name != "MonoPInvokeCallbackAttribute"))
  306. {
  307. UnityEngine.Debug.Log($"The method `{methodInfo}` must have `MonoPInvokeCallback` attribute to be compatible with IL2CPP!");
  308. }
  309. #endif
  310. void* function;
  311. #if BURST_INTERNAL
  312. // Internally in Burst tests, we callback the C# method instead
  313. function = (void*)InternalCompiler(delegateObj);
  314. #else
  315. Delegate managedFallbackDelegateMethod = null;
  316. if (!isILPostProcessing)
  317. {
  318. managedFallbackDelegateMethod = delegateObj as Delegate;
  319. }
  320. var delegateMethod = delegateObj as Delegate;
  321. #if UNITY_EDITOR
  322. string defaultOptions;
  323. // In case Burst is disabled entirely from the command line
  324. if (BurstCompilerOptions.ForceDisableBurstCompilation)
  325. {
  326. if (isILPostProcessing)
  327. {
  328. return null;
  329. }
  330. else
  331. {
  332. GCHandle.Alloc(managedFallbackDelegateMethod);
  333. function = (void*)Marshal.GetFunctionPointerForDelegate(managedFallbackDelegateMethod);
  334. return function;
  335. }
  336. }
  337. if (isILPostProcessing)
  338. {
  339. defaultOptions = "--" + BurstCompilerOptions.OptionJitIsForFunctionPointer + "\n";
  340. }
  341. else if (isFunctionPointer)
  342. {
  343. defaultOptions = "--" + BurstCompilerOptions.OptionJitIsForFunctionPointer + "\n";
  344. // Make sure that the delegate will never be collected
  345. var delHandle = GCHandle.Alloc(managedFallbackDelegateMethod);
  346. defaultOptions += "--" + BurstCompilerOptions.OptionJitManagedDelegateHandle + "0x" + ManagedResolverFunctionPointer + "|" + "0x" + GCHandle.ToIntPtr(delHandle).ToInt64().ToString("X16");
  347. }
  348. else
  349. {
  350. defaultOptions = "--" + BurstCompilerOptions.OptionJitEnableSynchronousCompilation;
  351. }
  352. string extraOptions;
  353. // The attribute is directly on the method, so we recover the underlying method here
  354. if (Options.TryGetOptions(methodInfo, out extraOptions, isForILPostProcessing: isILPostProcessing))
  355. {
  356. if (!string.IsNullOrWhiteSpace(extraOptions))
  357. {
  358. defaultOptions += "\n" + extraOptions;
  359. }
  360. var delegateMethodId = Unity.Burst.LowLevel.BurstCompilerService.CompileAsyncDelegateMethod(delegateObj, defaultOptions);
  361. function = Unity.Burst.LowLevel.BurstCompilerService.GetAsyncCompiledAsyncDelegateMethod(delegateMethodId);
  362. }
  363. #else
  364. // The attribute is directly on the method, so we recover the underlying method here
  365. if (BurstCompilerOptions.HasBurstCompileAttribute(methodInfo))
  366. {
  367. if (Options.EnableBurstCompilation && BurstCompilerHelper.IsBurstGenerated)
  368. {
  369. var delegateMethodId = Unity.Burst.LowLevel.BurstCompilerService.CompileAsyncDelegateMethod(delegateObj, string.Empty);
  370. function = Unity.Burst.LowLevel.BurstCompilerService.GetAsyncCompiledAsyncDelegateMethod(delegateMethodId);
  371. }
  372. else
  373. {
  374. // If this is for direct-call, and we're in a player, with Burst disabled, then we should return null,
  375. // since we don't actually have a managedFallbackDelegateMethod at this point.
  376. if (isILPostProcessing)
  377. {
  378. return null;
  379. }
  380. // Make sure that the delegate will never be collected
  381. GCHandle.Alloc(managedFallbackDelegateMethod);
  382. // If we are in a standalone player, and burst is disabled and we are actually
  383. // trying to load a function pointer, in that case we need to support it
  384. // so we are then going to use the managed function directly
  385. // NOTE: When running under IL2CPP, this could lead to a `System.NotSupportedException : To marshal a managed method, please add an attribute named 'MonoPInvokeCallback' to the method definition.`
  386. // so in that case, the method needs to have `MonoPInvokeCallback`
  387. // but that's a requirement for IL2CPP, not an issue with burst
  388. function = (void*)Marshal.GetFunctionPointerForDelegate(managedFallbackDelegateMethod);
  389. }
  390. }
  391. #endif
  392. else
  393. {
  394. throw new InvalidOperationException($"Burst cannot compile the function pointer `{methodInfo}` because the `[BurstCompile]` attribute is missing");
  395. }
  396. #endif
  397. // Should not happen but in that case, we are still trying to generated an error
  398. // It can be null if we are trying to compile a function in a standalone player
  399. // and the function was not compiled. In that case, we need to output an error
  400. if (function == null)
  401. {
  402. throw new InvalidOperationException($"Burst failed to compile the function pointer `{methodInfo}`");
  403. }
  404. // When burst compilation is disabled, we are still returning a valid stub function pointer (the a pointer to the managed function)
  405. // so that CompileFunctionPointer actually returns a delegate in all cases
  406. return function;
  407. }
  408. /// <summary>
  409. /// Lets the compiler service know we are shutting down, called by the event on OnDomainUnload, if EditorApplication.quitting was called
  410. /// </summary>
  411. internal static void Shutdown()
  412. {
  413. #if UNITY_EDITOR
  414. SendCommandToCompiler(BurstCompilerOptions.CompilerCommandShutdown);
  415. #endif
  416. }
  417. #if UNITY_EDITOR
  418. internal static void SetDefaultOptions()
  419. {
  420. SendCommandToCompiler(BurstCompilerOptions.CompilerCommandSetDefaultOptions, Options.GetOptions(isForCompilerClient: true));
  421. }
  422. #endif
  423. #if UNITY_EDITOR
  424. // We need this to be queried each domain reload in a static constructor so that it is called on the main thread only!
  425. internal static readonly bool IsScriptDebugInfoEnabled = UnityEditor.Compilation.CompilationPipeline.IsScriptDebugInfoEnabled();
  426. private sealed class DomainReloadStateSingleton : UnityEditor.ScriptableSingleton<DomainReloadStateSingleton>
  427. {
  428. public bool AlreadyLoaded = false;
  429. public bool IsScriptDebugInfoEnabled = false;
  430. }
  431. internal static bool WasScriptDebugInfoEnabledAtDomainReload => DomainReloadStateSingleton.instance.IsScriptDebugInfoEnabled;
  432. internal static void DomainReload()
  433. {
  434. const string parameterSeparator = "***";
  435. const string assemblySeparator = "```";
  436. var isScriptDebugInfoEnabled = IsScriptDebugInfoEnabled;
  437. var cmdBuilder =
  438. BeginCompilerCommand(BurstCompilerOptions.CompilerCommandDomainReload)
  439. .With(ProgressCallbackFunctionPointer)
  440. .With(parameterSeparator)
  441. .With(EagerCompileLogCallbackFunctionPointer)
  442. .With(parameterSeparator)
  443. .With(isScriptDebugInfoEnabled ? "Debug" : "Release")
  444. .With(parameterSeparator);
  445. // We need to send the list of assemblies if
  446. // (a) we have never done that before in this Editor instance, or
  447. // (b) we have done it before, but now the scripting code optimization mode has changed
  448. // from Debug to Release or vice-versa.
  449. // This is because these are the two cases in which CompilerClient will be
  450. // destroyed and recreated.
  451. if (!DomainReloadStateSingleton.instance.AlreadyLoaded ||
  452. DomainReloadStateSingleton.instance.IsScriptDebugInfoEnabled != isScriptDebugInfoEnabled)
  453. {
  454. // Gather list of assemblies to compile (only actually used at Editor startup)
  455. var assemblyNames = UnityEditor.Compilation.CompilationPipeline
  456. .GetAssemblies(UnityEditor.Compilation.AssembliesType.Editor)
  457. .Where(x => File.Exists(x.outputPath)) // If C# compilation fails, it won't exist on disk
  458. .Select(x => $"{x.name}|{string.Join(";", x.defines)}");
  459. foreach (var assemblyName in assemblyNames)
  460. {
  461. cmdBuilder.With(assemblyName)
  462. .With(assemblySeparator);
  463. }
  464. DomainReloadStateSingleton.instance.AlreadyLoaded = true;
  465. DomainReloadStateSingleton.instance.IsScriptDebugInfoEnabled = IsScriptDebugInfoEnabled;
  466. }
  467. cmdBuilder.SendToCompiler();
  468. }
  469. internal static string VersionNotify(string version)
  470. {
  471. return SendCommandToCompiler(BurstCompilerOptions.CompilerCommandVersionNotification, version);
  472. }
  473. internal static string GetTargetCpuFromHost()
  474. {
  475. return SendCommandToCompiler(BurstCompilerOptions.CompilerCommandGetTargetCpuFromHost);
  476. }
  477. #endif
  478. /// <summary>
  479. /// Cancel any compilation being processed by the JIT Compiler in the background.
  480. /// </summary>
  481. internal static void Cancel()
  482. {
  483. #if UNITY_EDITOR
  484. SendCommandToCompiler(BurstCompilerOptions.CompilerCommandCancel);
  485. #endif
  486. }
  487. /// <summary>
  488. /// Check if there is any job pending related to the last compilation ID.
  489. /// </summary>
  490. internal static bool IsCurrentCompilationDone()
  491. {
  492. #if UNITY_EDITOR
  493. return SendCommandToCompiler(BurstCompilerOptions.CompilerCommandIsCurrentCompilationDone) == "True";
  494. #else
  495. return true;
  496. #endif
  497. }
  498. internal static void Enable()
  499. {
  500. #if UNITY_EDITOR
  501. SendCommandToCompiler(BurstCompilerOptions.CompilerCommandEnableCompiler);
  502. #endif
  503. }
  504. internal static void Disable()
  505. {
  506. #if UNITY_EDITOR
  507. SendCommandToCompiler(BurstCompilerOptions.CompilerCommandDisableCompiler);
  508. #endif
  509. }
  510. internal static bool IsHostEditorArm()
  511. {
  512. #if UNITY_EDITOR
  513. return SendCommandToCompiler(BurstCompilerOptions.CompilerCommandIsArmTestEnv)=="true";
  514. #else
  515. return false;
  516. #endif
  517. }
  518. internal static void TriggerUnsafeStaticMethodRecompilation()
  519. {
  520. foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
  521. {
  522. var reinitAttributes = asm.GetCustomAttributes().Where(
  523. x => x.GetType().FullName == "Unity.Burst.BurstCompiler+StaticTypeReinitAttribute"
  524. );
  525. foreach (var attribute in reinitAttributes)
  526. {
  527. var ourAttribute = attribute as StaticTypeReinitAttribute;
  528. var type = ourAttribute.reinitType;
  529. var method = type.GetMethod("Constructor",BindingFlags.Static|BindingFlags.Public);
  530. method.Invoke(null, new object[] { });
  531. }
  532. }
  533. }
  534. internal static void TriggerRecompilation()
  535. {
  536. #if UNITY_EDITOR
  537. SetDefaultOptions();
  538. // This is done separately from CompilerCommandTriggerRecompilation below,
  539. // because CompilerCommandTriggerRecompilation will cause all jobs to re-request
  540. // their function pointers from Burst, and we need to have actually triggered
  541. // compilation by that point.
  542. SendCommandToCompiler(BurstCompilerOptions.CompilerCommandTriggerSetupRecompilation);
  543. SendCommandToCompiler(BurstCompilerOptions.CompilerCommandTriggerRecompilation, Options.RequiresSynchronousCompilation.ToString());
  544. #endif
  545. }
  546. internal static void UnloadAdditionalLibraries()
  547. {
  548. SendCommandToCompiler(BurstCompilerOptions.CompilerCommandUnloadBurstNatives);
  549. }
  550. internal static void InitialiseDebuggerHooks()
  551. {
  552. if (IsApiAvailable("BurstManagedDebuggerPluginV1") && String.IsNullOrEmpty(Environment.GetEnvironmentVariable("BURST_DISABLE_DEBUGGER_HOOKS")))
  553. {
  554. SendCommandToCompiler(SendCommandToCompiler(BurstCompilerOptions.CompilerCommandRequestInitialiseDebuggerCommmand));
  555. }
  556. }
  557. internal static bool IsApiAvailable(string apiName)
  558. {
  559. return SendCommandToCompiler(BurstCompilerOptions.CompilerCommandIsNativeApiAvailable, apiName) == "True";
  560. }
  561. internal static int RequestSetProtocolVersion(int version)
  562. {
  563. // Ask editor for the maximum version of the protocol we support, then inform the rest of the systems the negotiated version
  564. var editorVersion = SendCommandToCompiler(BurstCompilerOptions.CompilerCommandRequestSetProtocolVersionEditor, $"{version}");
  565. if (string.IsNullOrEmpty(editorVersion) || !int.TryParse(editorVersion, out var result))
  566. {
  567. result=0;
  568. }
  569. SendCommandToCompiler(BurstCompilerOptions.CompilerCommandSetProtocolVersionBurst, $"{result}");
  570. return result;
  571. }
  572. #if UNITY_EDITOR
  573. private unsafe delegate void LogCallbackDelegate(void* userData, int logType, byte* message, byte* fileName, int lineNumber);
  574. private static unsafe void EagerCompileLogCallback(void* userData, int logType, byte* message, byte* fileName, int lineNumber)
  575. {
  576. if (EagerCompilationLoggingEnabled)
  577. {
  578. BurstRuntime.Log(message, logType, fileName, lineNumber);
  579. }
  580. }
  581. [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  582. private delegate IntPtr ManagedFnPtrResolverDelegate(IntPtr handleVal);
  583. private static IntPtr ManagedResolverFunction(IntPtr handleVal)
  584. {
  585. var delegateObj = GCHandle.FromIntPtr(handleVal).Target;
  586. var fnptr = Marshal.GetFunctionPointerForDelegate(delegateObj);
  587. return fnptr;
  588. }
  589. internal static bool EagerCompilationLoggingEnabled = false;
  590. private static readonly string EagerCompileLogCallbackFunctionPointer;
  591. private static readonly string ManagedResolverFunctionPointer;
  592. #endif
  593. internal static void Initialize(string[] assemblyFolders, string[] ignoreAssemblies)
  594. {
  595. #if UNITY_EDITOR
  596. var glued = new string[2];
  597. glued[0] = SafeStringArrayHelper.SerialiseStringArraySafe(assemblyFolders);
  598. glued[1] = SafeStringArrayHelper.SerialiseStringArraySafe(ignoreAssemblies);
  599. var optionsSet = SafeStringArrayHelper.SerialiseStringArraySafe(glued);
  600. SendCommandToCompiler(BurstCompilerOptions.CompilerCommandInitialize, optionsSet);
  601. #endif
  602. }
  603. internal static void NotifyCompilationStarted(string[] assemblyFolders, string[] ignoreAssemblies)
  604. {
  605. #if UNITY_EDITOR
  606. var glued = new string[2];
  607. glued[0] = SafeStringArrayHelper.SerialiseStringArraySafe(assemblyFolders);
  608. glued[1] = SafeStringArrayHelper.SerialiseStringArraySafe(ignoreAssemblies);
  609. var optionsSet = SafeStringArrayHelper.SerialiseStringArraySafe(glued);
  610. SendCommandToCompiler(BurstCompilerOptions.CompilerCommandNotifyCompilationStarted, optionsSet);
  611. #endif
  612. }
  613. internal static void NotifyAssemblyCompilationNotRequired(string assemblyName)
  614. {
  615. #if UNITY_EDITOR
  616. SendCommandToCompiler(BurstCompilerOptions.CompilerCommandNotifyAssemblyCompilationNotRequired, assemblyName);
  617. #endif
  618. }
  619. internal static void NotifyAssemblyCompilationFinished(string assemblyName, string[] defines)
  620. {
  621. #if UNITY_EDITOR
  622. BeginCompilerCommand(BurstCompilerOptions.CompilerCommandNotifyAssemblyCompilationFinished)
  623. .With(assemblyName).And()
  624. .With(string.Join(";", defines))
  625. .SendToCompiler();
  626. #endif
  627. }
  628. internal static void NotifyCompilationFinished()
  629. {
  630. #if UNITY_EDITOR
  631. SendCommandToCompiler(BurstCompilerOptions.CompilerCommandNotifyCompilationFinished);
  632. #endif
  633. }
  634. internal static string AotCompilation(string[] assemblyFolders, string[] assemblyRoots, string options)
  635. {
  636. var result = "failed";
  637. #if UNITY_EDITOR
  638. result = SendCommandToCompiler(
  639. BurstCompilerOptions.CompilerCommandAotCompilation,
  640. BurstCompilerOptions.SerialiseCompilationOptionsSafe(assemblyRoots, assemblyFolders, options));
  641. #endif
  642. return result;
  643. }
  644. #if UNITY_EDITOR
  645. private static readonly string ProgressCallbackFunctionPointer;
  646. private delegate void ProgressCallbackDelegate(int current, int total);
  647. private static void ProgressCallback(int current, int total)
  648. {
  649. OnProgress?.Invoke(current, total);
  650. }
  651. internal static event Action<int, int> OnProgress;
  652. #endif
  653. internal static void SetProfilerCallbacks()
  654. {
  655. #if UNITY_EDITOR
  656. BeginCompilerCommand(BurstCompilerOptions.CompilerCommandSetProfileCallbacks)
  657. .With(ProfileBeginCallbackFunctionPointer).And(';')
  658. .With(ProfileEndCallbackFunctionPointer)
  659. .SendToCompiler();
  660. #endif
  661. }
  662. #if UNITY_EDITOR
  663. internal delegate void ProfileBeginCallbackDelegate(string markerName, string metadataName, string metadataValue);
  664. internal delegate void ProfileEndCallbackDelegate(string markerName);
  665. private static readonly string ProfileBeginCallbackFunctionPointer;
  666. private static readonly string ProfileEndCallbackFunctionPointer;
  667. private static void ProfileBeginCallback(string markerName, string metadataName, string metadataValue) => OnProfileBegin?.Invoke(markerName, metadataName, metadataValue);
  668. private static void ProfileEndCallback(string markerName) => OnProfileEnd?.Invoke(markerName);
  669. internal static event ProfileBeginCallbackDelegate OnProfileBegin;
  670. internal static event ProfileEndCallbackDelegate OnProfileEnd;
  671. #endif
  672. private static string SendRawCommandToCompiler(string command)
  673. {
  674. var results = Unity.Burst.LowLevel.BurstCompilerService.GetDisassembly(DummyMethodInfo, command);
  675. if (!string.IsNullOrEmpty(results))
  676. return results.TrimStart('\n');
  677. return "";
  678. }
  679. private static string SendCommandToCompiler(string commandName, string commandArgs = null)
  680. {
  681. if (commandName == null) throw new ArgumentNullException(nameof(commandName));
  682. if (commandArgs == null)
  683. {
  684. // If there are no arguments then there's no reason to go through the builder
  685. return SendRawCommandToCompiler(commandName);
  686. }
  687. // Otherwise use the builder for building the final command
  688. return BeginCompilerCommand(commandName)
  689. .With(commandArgs)
  690. .SendToCompiler();
  691. }
  692. private static readonly MethodInfo DummyMethodInfo = typeof(BurstCompiler).GetMethod(nameof(DummyMethod), BindingFlags.Static | BindingFlags.NonPublic);
  693. /// <summary>
  694. /// Dummy empty method for being able to send a command to the compiler
  695. /// </summary>
  696. private static void DummyMethod() { }
  697. #if !UNITY_EDITOR && !BURST_INTERNAL
  698. /// <summary>
  699. /// Internal class to detect at standalone player time if AOT settings were enabling burst.
  700. /// </summary>
  701. [BurstCompile]
  702. internal static class BurstCompilerHelper
  703. {
  704. [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  705. private delegate bool IsBurstEnabledDelegate();
  706. private static readonly IsBurstEnabledDelegate IsBurstEnabledImpl = new IsBurstEnabledDelegate(IsBurstEnabled);
  707. [BurstCompile]
  708. [AOT.MonoPInvokeCallback(typeof(IsBurstEnabledDelegate))]
  709. private static bool IsBurstEnabled()
  710. {
  711. bool result = true;
  712. DiscardedMethod(ref result);
  713. return result;
  714. }
  715. [BurstDiscard]
  716. private static void DiscardedMethod(ref bool value)
  717. {
  718. value = false;
  719. }
  720. private static unsafe bool IsCompiledByBurst(Delegate del)
  721. {
  722. var delegateMethodId = Unity.Burst.LowLevel.BurstCompilerService.CompileAsyncDelegateMethod(del, string.Empty);
  723. // We don't try to run the method, having a pointer is already enough to tell us that burst was active for AOT settings
  724. return Unity.Burst.LowLevel.BurstCompilerService.GetAsyncCompiledAsyncDelegateMethod(delegateMethodId) != (void*)0;
  725. }
  726. /// <summary>
  727. /// Gets a boolean indicating whether burst was enabled for standalone player, used only at runtime.
  728. /// </summary>
  729. public static readonly bool IsBurstGenerated = IsCompiledByBurst(IsBurstEnabledImpl);
  730. }
  731. #endif // !UNITY_EDITOR && !BURST_INTERNAL
  732. /// <summary>
  733. /// Fake delegate class to make BurstCompilerService.CompileAsyncDelegateMethod happy
  734. /// so that it can access the underlying static method via the property get_Method.
  735. /// So this class is not a delegate.
  736. /// </summary>
  737. private class FakeDelegate
  738. {
  739. public FakeDelegate(MethodInfo method)
  740. {
  741. Method = method;
  742. }
  743. [Preserve]
  744. public MethodInfo Method { get; }
  745. }
  746. }
  747. }