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