797 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			797 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | #if UNITY_EDITOR | ||
|  | using System; | ||
|  | using System.Collections.Generic; | ||
|  | using System.IO; | ||
|  | using System.Linq; | ||
|  | using System.Reflection; | ||
|  | using System.Runtime.CompilerServices; | ||
|  | using System.Runtime.InteropServices; | ||
|  | using Unity.Burst.LowLevel; | ||
|  | using Unity.Profiling; | ||
|  | using Unity.Profiling.LowLevel; | ||
|  | using Unity.Profiling.LowLevel.Unsafe; | ||
|  | using UnityEditor; | ||
|  | using UnityEditor.Compilation; | ||
|  | using UnityEditor.Scripting.ScriptCompilation; | ||
|  | using UnityEngine; | ||
|  | 
 | ||
|  | namespace Unity.Burst.Editor | ||
|  | { | ||
|  |     /// <summary> | ||
|  |     /// Main entry point for initializing the burst compiler service for both JIT and AOT | ||
|  |     /// </summary> | ||
|  |     [InitializeOnLoad] | ||
|  |     internal class BurstLoader | ||
|  |     { | ||
|  |         private const int BURST_PROTOCOL_VERSION = 1; | ||
|  | 
 | ||
|  |         // Cache the delegate to make sure it doesn't get collected. | ||
|  |         private static readonly BurstCompilerService.ExtractCompilerFlags TryGetOptionsFromMemberDelegate = TryGetOptionsFromMember; | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Gets the location to the runtime path of burst. | ||
|  |         /// </summary> | ||
|  |         public static string RuntimePath { get; private set; } | ||
|  | 
 | ||
|  |         public static BclConfiguration BclConfiguration { get; private set; } | ||
|  | 
 | ||
|  |         public static bool IsDebugging { get; private set; } | ||
|  | 
 | ||
|  |         public static bool SafeShutdown { get; private set; } | ||
|  | 
 | ||
|  |         public static int ProtocolVersion { get; private set; } | ||
|  | 
 | ||
|  |         private static void VersionUpdateCheck() | ||
|  |         { | ||
|  |             var seek = "com.unity.burst@"; | ||
|  |             var first = RuntimePath.LastIndexOf(seek); | ||
|  |             var last = RuntimePath.LastIndexOf(".Runtime"); | ||
|  |             string version; | ||
|  |             if (first == -1 || last == -1 || last <= first) | ||
|  |             { | ||
|  |                 version = "Unknown"; | ||
|  |             } | ||
|  |             else | ||
|  |             { | ||
|  |                 first += seek.Length; | ||
|  |                 last -= 1; | ||
|  |                 version = RuntimePath.Substring(first, last - first); | ||
|  |             } | ||
|  | 
 | ||
|  |             var result = BurstCompiler.VersionNotify(version); | ||
|  |             // result will be empty if we are shutting down, and thus we shouldn't popup a dialog | ||
|  |             if (!String.IsNullOrEmpty(result) && result != version) | ||
|  |             { | ||
|  |                 if (IsDebugging) | ||
|  |                 { | ||
|  |                     UnityEngine.Debug.LogWarning($"[com.unity.burst] - '{result}' != '{version}'"); | ||
|  |                 } | ||
|  |                 OnVersionChangeDetected(); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         private static bool UnityBurstRuntimePathOverwritten(out string path) | ||
|  |         { | ||
|  |             path = Environment.GetEnvironmentVariable("UNITY_BURST_RUNTIME_PATH"); | ||
|  |             return Directory.Exists(path); | ||
|  |         } | ||
|  | 
 | ||
|  |         private static void OnVersionChangeDetected() | ||
|  |         { | ||
|  |             // Write marker file to tell Burst to delete the cache at next startup. | ||
|  |             try | ||
|  |             { | ||
|  |                 File.Create(Path.Combine(BurstCompilerOptions.DefaultCacheFolder, BurstCompilerOptions.DeleteCacheMarkerFileName)).Dispose(); | ||
|  |             } | ||
|  |             catch (IOException) | ||
|  |             { | ||
|  |                 // In the unlikely scenario that two processes are creating this marker file at the same time, | ||
|  |                 // and one of them fails, do nothing because the other one has hopefully succeeded. | ||
|  |             } | ||
|  | 
 | ||
|  |             // Skip checking if we are using an explicit runtime path. | ||
|  |             if (!UnityBurstRuntimePathOverwritten(out var _)) | ||
|  |             { | ||
|  |                 EditorUtility.DisplayDialog("Burst Package Update Detected", "The version of Burst used by your project has changed. Please restart the Editor to continue.", "OK"); | ||
|  |                 BurstCompiler.Shutdown(); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         private static CompilationTaskReason _currentBuildKind; | ||
|  | 
 | ||
|  |         static BurstLoader() | ||
|  |         { | ||
|  |             if (BurstCompilerOptions.ForceDisableBurstCompilation) | ||
|  |             { | ||
|  |                 if (!BurstCompilerOptions.IsSecondaryUnityProcess) | ||
|  |                 { | ||
|  |                     UnityEngine.Debug.LogWarning("[com.unity.burst] Burst is disabled entirely from the command line or environment variable"); | ||
|  |                 } | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  | #if UNITY_2021_1_OR_NEWER | ||
|  |             // If Burst was previously disabled (either via the command line or an environment variable), | ||
|  |             // and is now enabled, then we need to force script recompilation. | ||
|  |             // | ||
|  |             // Why? Because when Burst is disabled, we also don't run Burst's IL postprocessor. If the last | ||
|  |             // script compilation occurred when Burst was disabled, then those compiled script assemblies | ||
|  |             // may still be cached, and wouldn't be recompiled / re-IL-postprocessed unless we specifically | ||
|  |             // make that happen. | ||
|  |             // | ||
|  |             // We use "is Burst assembly not currently IL-postprocessed" as a proxy for | ||
|  |             // "was Burst previously disabled". | ||
|  |             if (typeof(BurstLoader).Assembly.GetType("$BurstDirectCallInitializer") == null) | ||
|  |             { | ||
|  |                 UnityEngine.Debug.Log("[com.unity.burst] Burst was previously disabled and is now enabled; recompiling scripts"); | ||
|  | #if UNITY_2021_2_OR_NEWER | ||
|  |                 CompilationPipeline.RequestScriptCompilation(RequestScriptCompilationOptions.CleanBuildCache); | ||
|  | #else | ||
|  |                 CompilationPipeline.RequestScriptCompilation(); | ||
|  | #endif | ||
|  |             } | ||
|  | #endif | ||
|  | 
 | ||
|  |             // This can be setup to get more diagnostics | ||
|  |             var debuggingStr = Environment.GetEnvironmentVariable("UNITY_BURST_DEBUG"); | ||
|  |             IsDebugging = debuggingStr != null && int.TryParse(debuggingStr, out var debugLevel) && debugLevel > 0; | ||
|  |             if (IsDebugging) | ||
|  |             { | ||
|  |                 UnityEngine.Debug.LogWarning("[com.unity.burst] Extra debugging is turned on."); | ||
|  |             } | ||
|  | 
 | ||
|  |             // Try to load the runtime through an environment variable | ||
|  |             var isRuntimePathOverwritten = UnityBurstRuntimePathOverwritten(out var path); | ||
|  |             if (!isRuntimePathOverwritten) | ||
|  |             { | ||
|  |                 // Otherwise try to load it from the package itself | ||
|  | #if UNITY_2021_3_OR_NEWER | ||
|  |                 path = FileUtil.GetPhysicalPath("Packages/com.unity.burst/.Runtime"); | ||
|  | #else | ||
|  |                 path = Path.GetFullPath("Packages/com.unity.burst/.Runtime"); | ||
|  | #endif | ||
|  |             } | ||
|  | 
 | ||
|  |             RuntimePath = path; | ||
|  | 
 | ||
|  |             BclConfiguration = GetBclConfiguration(path, isRuntimePathOverwritten); | ||
|  | 
 | ||
|  |             if (IsDebugging) | ||
|  |             { | ||
|  |                 UnityEngine.Debug.LogWarning($"[com.unity.burst] Runtime directory set to {RuntimePath}"); | ||
|  |             } | ||
|  | 
 | ||
|  |             BurstCompilerService.Initialize(RuntimePath, TryGetOptionsFromMemberDelegate); | ||
|  | 
 | ||
|  |             ProtocolVersion = BurstCompiler.RequestSetProtocolVersion(BURST_PROTOCOL_VERSION); | ||
|  | 
 | ||
|  |             BurstCompiler.Initialize(GetAssemblyFolders(),BurstAssemblyDisable.GetDisabledAssemblies(BurstAssemblyDisable.DisableType.Editor, "")); | ||
|  | 
 | ||
|  |             // It's important that this call comes *after* BurstCompilerService.Initialize, | ||
|  |             // otherwise any calls from within EnsureSynchronized to BurstCompilerService, | ||
|  |             // such as BurstCompiler.Disable(), will silently fail. | ||
|  |             BurstEditorOptions.EnsureSynchronized(); | ||
|  | 
 | ||
|  |             EditorApplication.quitting += OnEditorApplicationQuitting; | ||
|  | 
 | ||
|  |             CompilationPipeline.compilationStarted += OnCompilationStarted; | ||
|  |             CompilationPipeline.compilationFinished += OnCompilationFinished; | ||
|  | 
 | ||
|  |             // We use this internal event because it's the only way to get access to the ScriptAssembly.HasCompileErrors, | ||
|  |             // which tells us whether C# compilation succeeded or failed for this assembly. | ||
|  |             EditorCompilationInterface.Instance.assemblyCompilationFinished += OnAssemblyCompilationFinished; | ||
|  | 
 | ||
|  | #if UNITY_2022_2_OR_NEWER | ||
|  |             CompilationPipeline.assemblyCompilationNotRequired += OnAssemblyCompilationNotRequired; | ||
|  | #endif | ||
|  | 
 | ||
|  |             EditorApplication.playModeStateChanged += EditorApplicationOnPlayModeStateChanged; | ||
|  |             AppDomain.CurrentDomain.DomainUnload += OnDomainUnload; | ||
|  | 
 | ||
|  |             SafeShutdown = false; | ||
|  |             UnityEditor.PackageManager.Events.registeringPackages += PackageRegistrationEvent; | ||
|  |             SafeShutdown = BurstCompiler.IsApiAvailable("SafeShutdown"); | ||
|  | 
 | ||
|  |             if (!SafeShutdown) | ||
|  |             { | ||
|  |                 VersionUpdateCheck(); | ||
|  |             } | ||
|  | 
 | ||
|  |             // Notify the compiler about a domain reload | ||
|  |             if (IsDebugging) | ||
|  |             { | ||
|  |                 UnityEngine.Debug.Log("Burst - Domain Reload"); | ||
|  |             } | ||
|  | 
 | ||
|  |             BurstCompiler.OnProgress += OnProgress; | ||
|  | 
 | ||
|  |             BurstCompiler.EagerCompilationLoggingEnabled = true; | ||
|  | 
 | ||
|  |             // Make sure BurstRuntime is initialized. This needs to happen before BurstCompiler.DomainReload, | ||
|  |             // because that can cause calls to BurstRuntime.Log. | ||
|  |             BurstRuntime.Initialize(); | ||
|  | 
 | ||
|  |             // Notify the JitCompilerService about a domain reload | ||
|  |             BurstCompiler.SetDefaultOptions(); | ||
|  |             BurstCompiler.DomainReload(); | ||
|  | 
 | ||
|  |             BurstCompiler.OnProfileBegin += OnProfileBegin; | ||
|  |             BurstCompiler.OnProfileEnd += OnProfileEnd; | ||
|  |             BurstCompiler.SetProfilerCallbacks(); | ||
|  | 
 | ||
|  |             BurstCompiler.InitialiseDebuggerHooks(); | ||
|  |         } | ||
|  | 
 | ||
|  |         private static bool _isQuitting; | ||
|  |         private static void OnEditorApplicationQuitting() | ||
|  |         { | ||
|  |             _isQuitting = true; | ||
|  |         } | ||
|  | 
 | ||
|  |         public static Action OnBurstShutdown; | ||
|  | 
 | ||
|  |         private static void PackageRegistrationEvent(UnityEditor.PackageManager.PackageRegistrationEventArgs obj) | ||
|  |         { | ||
|  |             bool requireCleanup = false; | ||
|  |             if (SafeShutdown) | ||
|  |             { | ||
|  |                 foreach (var changed in obj.changedFrom) | ||
|  |                 { | ||
|  |                     if (changed.name.Contains("com.unity.burst")) | ||
|  |                     { | ||
|  |                         requireCleanup = true; | ||
|  |                         break; | ||
|  |                     } | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             foreach (var removed in obj.removed) | ||
|  |             { | ||
|  |                 if (removed.name.Contains("com.unity.burst")) | ||
|  |                 { | ||
|  |                     requireCleanup = true; | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             if (requireCleanup) | ||
|  |             { | ||
|  |                 OnBurstShutdown?.Invoke(); | ||
|  |                 if (!SafeShutdown) | ||
|  |                 { | ||
|  |                     EditorUtility.DisplayDialog("Burst Package Has Been Removed", "Please restart the Editor to continue.", "OK"); | ||
|  |                 } | ||
|  |                 BurstCompiler.Shutdown(); | ||
|  |             } | ||
|  |             else | ||
|  |             { | ||
|  |                 BurstCompiler.NotifyPackagesChanged(); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         private static BclConfiguration GetBclConfiguration(string runtimePath, bool isRuntimePathOverwritten) | ||
|  |         { | ||
|  |             string bclFolderPath; | ||
|  |             if (isRuntimePathOverwritten) | ||
|  |             { | ||
|  |                 return new BclConfiguration | ||
|  |                 { | ||
|  |                     FolderPath = runtimePath, | ||
|  |                     ExecutablePath = Path.Combine(runtimePath, "bcl.exe"), | ||
|  |                     IsExecutableNative = false, | ||
|  |                 }; | ||
|  |             } | ||
|  |             else | ||
|  |             { | ||
|  |                 bclFolderPath = Path.Combine(runtimePath, "bcl", GetBclPlatformFolderName()); | ||
|  |                 if (Directory.Exists(bclFolderPath)) | ||
|  |                 { | ||
|  |                     var bclFileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) | ||
|  |                         ? "bcl.exe" | ||
|  |                         : "bcl"; | ||
|  | 
 | ||
|  |                     return new BclConfiguration | ||
|  |                     { | ||
|  |                         FolderPath = bclFolderPath, | ||
|  |                         ExecutablePath = Path.Combine(bclFolderPath, bclFileName), | ||
|  |                         IsExecutableNative = true, | ||
|  |                     }; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 return new BclConfiguration | ||
|  |                 { | ||
|  |                     FolderPath = runtimePath, | ||
|  |                     ExecutablePath = Path.Combine(runtimePath, "bcl.exe"), | ||
|  |                     IsExecutableNative = false, | ||
|  |                 }; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         private static string GetBclPlatformFolderName() | ||
|  |         { | ||
|  |             var hostPlatform = Application.platform; | ||
|  |             var hostArchitecture = RuntimeInformation.OSArchitecture; | ||
|  | 
 | ||
|  |             switch (hostPlatform) | ||
|  |             { | ||
|  |                 case RuntimePlatform.WindowsEditor: | ||
|  |                     return "win-x64"; | ||
|  | 
 | ||
|  |                 case RuntimePlatform.OSXEditor when hostArchitecture == Architecture.X64: | ||
|  |                     return "osx-x64"; | ||
|  | 
 | ||
|  |                 case RuntimePlatform.OSXEditor when hostArchitecture == Architecture.Arm64: | ||
|  |                     return "osx-arm64"; | ||
|  | 
 | ||
|  |                 case RuntimePlatform.LinuxEditor: | ||
|  |                     return "linux-x64"; | ||
|  | 
 | ||
|  |                 default: | ||
|  |                     throw new InvalidOperationException($"Current OS platform {hostPlatform} and architecture {hostArchitecture} combination is not supported"); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         // Don't initialize to 0 because that could be a valid progress ID. | ||
|  |         private static int BurstProgressId = -1; | ||
|  | 
 | ||
|  |         // If this enum changes, update the benchmarks tool accordingly as we rely on integer value related to this enum | ||
|  |         internal enum BurstEagerCompilationStatus | ||
|  |         { | ||
|  |             NotScheduled, | ||
|  |             Scheduled, | ||
|  |             Completed | ||
|  |         } | ||
|  | 
 | ||
|  |         // For the time being, this field is only read through reflection | ||
|  |         internal static BurstEagerCompilationStatus EagerCompilationStatus; | ||
|  | 
 | ||
|  |         private static void OnProgress(int current, int total) | ||
|  |         { | ||
|  |             if (current == total) | ||
|  |             { | ||
|  |                 EagerCompilationStatus = BurstEagerCompilationStatus.Completed; | ||
|  |             } | ||
|  | 
 | ||
|  |             // OnProgress is called from a background thread, | ||
|  |             // but we need to update the progress UI on the main thread. | ||
|  |             EditorApplication.CallDelayed(() => | ||
|  |             { | ||
|  |                 if (current == total) | ||
|  |                 { | ||
|  |                     // We've finished - remove progress bar. | ||
|  |                     if (Progress.Exists(BurstProgressId)) | ||
|  |                     { | ||
|  |                         Progress.Remove(BurstProgressId); | ||
|  |                         BurstProgressId = -1; | ||
|  |                     } | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     // Do we need to create the progress bar? | ||
|  |                     if (!Progress.Exists(BurstProgressId)) | ||
|  |                     { | ||
|  |                         BurstProgressId = Progress.Start( | ||
|  |                             "Burst", | ||
|  |                             "Compiling...", | ||
|  |                             Progress.Options.Unmanaged); | ||
|  |                     } | ||
|  | 
 | ||
|  |                     Progress.Report( | ||
|  |                         BurstProgressId, | ||
|  |                         current / (float)total, | ||
|  |                         $"Compiled {current} / {total} libraries"); | ||
|  |                 } | ||
|  |             }); | ||
|  |         } | ||
|  | 
 | ||
|  |         [ThreadStatic] | ||
|  |         private static Dictionary<string, IntPtr> ProfilerMarkers; | ||
|  | 
 | ||
|  |         private static unsafe void OnProfileBegin(string markerName, string metadataName, string metadataValue) | ||
|  |         { | ||
|  |             if (ProfilerMarkers == null) | ||
|  |             { | ||
|  |                 // Initialize thread-static dictionary. | ||
|  |                 ProfilerMarkers = new Dictionary<string, IntPtr>(); | ||
|  |             } | ||
|  | 
 | ||
|  |             if (!ProfilerMarkers.TryGetValue(markerName, out var markerPtr)) | ||
|  |             { | ||
|  |                 ProfilerMarkers.Add(markerName, markerPtr = ProfilerUnsafeUtility.CreateMarker( | ||
|  |                     markerName, | ||
|  |                     ProfilerUnsafeUtility.CategoryScripts, | ||
|  |                     MarkerFlags.Script, | ||
|  |                     metadataName != null ? 1 : 0)); | ||
|  | 
 | ||
|  |                 // metadataName is assumed to be consistent for a given markerName. | ||
|  |                 if (metadataName != null) | ||
|  |                 { | ||
|  |                     ProfilerUnsafeUtility.SetMarkerMetadata( | ||
|  |                         markerPtr, | ||
|  |                         0, | ||
|  |                         metadataName, | ||
|  |                         (byte)ProfilerMarkerDataType.String16, | ||
|  |                         (byte)ProfilerMarkerDataUnit.Undefined); | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             if (metadataName != null && metadataValue != null) | ||
|  |             { | ||
|  |                 fixed (char* methodNamePtr = metadataValue) | ||
|  |                 { | ||
|  |                     var metadata = new ProfilerMarkerData | ||
|  |                     { | ||
|  |                         Type = (byte)ProfilerMarkerDataType.String16, | ||
|  |                         Size = ((uint)metadataValue.Length + 1) * 2, | ||
|  |                         Ptr = methodNamePtr | ||
|  |                     }; | ||
|  |                     ProfilerUnsafeUtility.BeginSampleWithMetadata(markerPtr, 1, &metadata); | ||
|  |                 } | ||
|  |             } | ||
|  |             else | ||
|  |             { | ||
|  |                 ProfilerUnsafeUtility.BeginSample(markerPtr); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         private static void OnProfileEnd(string markerName) | ||
|  |         { | ||
|  |             if (ProfilerMarkers == null) | ||
|  |             { | ||
|  |                 // If we got here it means we had a domain reload between when we called profile begin and | ||
|  |                 // now profile end, and so we need to bail out. | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             if (!ProfilerMarkers.TryGetValue(markerName, out var markerPtr)) | ||
|  |             { | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             ProfilerUnsafeUtility.EndSample(markerPtr); | ||
|  |         } | ||
|  | 
 | ||
|  |         private static void EditorApplicationOnPlayModeStateChanged(PlayModeStateChange state) | ||
|  |         { | ||
|  |             if (IsDebugging) | ||
|  |             { | ||
|  |                 UnityEngine.Debug.Log($"Burst - Change of Editor State: {state}"); | ||
|  |             } | ||
|  | 
 | ||
|  |             switch (state) | ||
|  |             { | ||
|  |                 case PlayModeStateChange.ExitingPlayMode: | ||
|  |                     // Cleanup any loaded burst natives so users have a clean point to update the libraries. | ||
|  |                     BurstCompiler.UnloadAdditionalLibraries(); | ||
|  |                     break; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         enum CompilationTaskReason | ||
|  |         { | ||
|  |             IsForEditor,                // Compilation should proceed as its for an editor build | ||
|  |             IsForPlayer,                // Skip this compilation | ||
|  |             IsForPreviousScriptingMode, // We are about to enter a domain reload, don't start any new compilations | ||
|  |             IsForAssemblyBuilder,       // Request is coming from an 'AssemblyBuilder' and should be skipped as not supported | ||
|  |         } | ||
|  | 
 | ||
|  |         static CompilationTaskReason CurrentCompilationTaskShouldStart() | ||
|  |         { | ||
|  |             try | ||
|  |             { | ||
|  |                 if (BurstCompiler.WasScriptDebugInfoEnabledAtDomainReload != UnityEditor.Compilation.CompilationPipeline.IsScriptDebugInfoEnabled()) | ||
|  |                 { | ||
|  |                     // If the scripting compilation mode has changed since we last had our domain reloaded, then we ignore all requests, and act as if | ||
|  |                     //loading for the first time. This is to avoid having compilations kick off right before a Shutdown triggered by domain reload, that | ||
|  |                     //would cause the a significant stall as we had to wait for those compilations to finish, thus blocking the main thread. | ||
|  |                     return CompilationTaskReason.IsForPreviousScriptingMode; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 var inst = EditorCompilationInterface.Instance; | ||
|  | 
 | ||
|  | #if UNITY_2021_1_OR_NEWER | ||
|  |                 var editorCompilationType = inst.GetType(); | ||
|  |                 var activeBeeBuildField = editorCompilationType.GetField("_currentBeeScriptCompilationState", BindingFlags.Instance | BindingFlags.NonPublic); | ||
|  |                 if (activeBeeBuildField == null) | ||
|  |                 { | ||
|  |                     activeBeeBuildField = editorCompilationType.GetField("activeBeeBuild", BindingFlags.Instance | BindingFlags.NonPublic); | ||
|  |                 } | ||
|  |                 var activeBeeBuild = activeBeeBuildField.GetValue(inst); | ||
|  | 
 | ||
|  |                 // If a user is doing an `AssemblyBuilder` compilation, we do not support that in Burst. | ||
|  |                 // This seems to manifest as a null `activeBeeBuild`, so we bail here if that happens. | ||
|  |                 if (activeBeeBuild == null) | ||
|  |                 { | ||
|  |                     return CompilationTaskReason.IsForAssemblyBuilder; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 var settings = activeBeeBuild.GetType().GetProperty("settings", BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance).GetValue(activeBeeBuild); | ||
|  |                 var opt = (EditorScriptCompilationOptions)settings.GetType().GetProperty("CompilationOptions").GetValue(settings); | ||
|  | #else | ||
|  |                 var task = inst.GetType() | ||
|  |                     .GetField("compilationTask", BindingFlags.Instance | BindingFlags.NonPublic) | ||
|  |                     .GetValue(inst); | ||
|  | 
 | ||
|  |                 // If a user is doing an `AssemblyBuilder` compilation, we do not support that in Burst. | ||
|  |                 // This seems to manifest as a null `task`, so we bail here if that happens. | ||
|  |                 if (task == null) | ||
|  |                 { | ||
|  |                     return CompilationTaskReason.IsForAssemblyBuilder; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 var opt = (EditorScriptCompilationOptions)task.GetType() | ||
|  |                     .GetField("options", BindingFlags.Instance | BindingFlags.NonPublic) | ||
|  |                     .GetValue(task); | ||
|  | #endif | ||
|  | 
 | ||
|  | #if UNITY_2022_2_OR_NEWER | ||
|  |                 if ((opt & EditorScriptCompilationOptions.BuildingSkipCompile) != 0) | ||
|  |                 { | ||
|  |                     return CompilationTaskReason.IsForPlayer; | ||
|  |                 } | ||
|  | #endif | ||
|  | 
 | ||
|  |                 if ((opt & EditorScriptCompilationOptions.BuildingForEditor) != 0) | ||
|  |                 { | ||
|  |                     return CompilationTaskReason.IsForEditor; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 return CompilationTaskReason.IsForPlayer; | ||
|  |             } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 UnityEngine.Debug.LogWarning("Burst - Unknown private compilation pipeline API\nAssuming editor build\n" + ex.ToString()); | ||
|  | 
 | ||
|  |                 return CompilationTaskReason.IsForEditor; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         private static void OnCompilationStarted(object value) | ||
|  |         { | ||
|  |             _currentBuildKind = CurrentCompilationTaskShouldStart(); | ||
|  |             if (_currentBuildKind != CompilationTaskReason.IsForEditor) | ||
|  |             { | ||
|  |                 if (IsDebugging) | ||
|  |                 { | ||
|  |                     UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - not handling '{value}' because '{_currentBuildKind}'"); | ||
|  |                 } | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             if (IsDebugging) | ||
|  |             { | ||
|  |                 UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - compilation started for '{value}'"); | ||
|  |             } | ||
|  | 
 | ||
|  |             BurstCompiler.NotifyCompilationStarted(GetAssemblyFolders(), | ||
|  |                 BurstAssemblyDisable.GetDisabledAssemblies(BurstAssemblyDisable.DisableType.Editor,"") ); | ||
|  |         } | ||
|  | 
 | ||
|  |         private static string[] GetAssemblyFolders() | ||
|  |         { | ||
|  |             var assemblyFolders = new HashSet<string>(); | ||
|  | 
 | ||
|  |             // First, we get the path to Mono system libraries. This will be something like | ||
|  |             // <EditorPath>/Data/MonoBleedingEdge/lib/mono/unityjit-win32 | ||
|  |             // | ||
|  |             // You might think we could use MonoLibraryHelpers.GetSystemReferenceDirectories | ||
|  |             // here, but we can't, because that returns the _reference assembly_ directories, | ||
|  |             // not the actual implementation assembly directory. | ||
|  |             var systemLibraryDirectory = Path.GetDirectoryName(typeof(object).Assembly.Location); | ||
|  |             assemblyFolders.Add(systemLibraryDirectory); | ||
|  | 
 | ||
|  |             // Also add the Facades directory, since that contains netstandard. Without this, | ||
|  |             // we'll potentially resolve the "wrong" netstandard from a dotnet compiler host. | ||
|  |             assemblyFolders.Add(Path.Combine(systemLibraryDirectory, "Facades")); | ||
|  | 
 | ||
|  |             // Now add the default assembly search paths. | ||
|  |             // This will include | ||
|  |             // - Unity dlls in <EditorPath>/Data/Managed and <EditorPath>/Data/Managed/UnityEngine | ||
|  |             // - Platform support dlls e.g. <EditorPath>/Data/PlaybackEngines/WindowsStandaloneSupport | ||
|  |             // - Package paths. These are interesting because they are "virtual" paths, of the form | ||
|  |             //   Packages/<MyPackageName>. They need to be resolved to physical paths. | ||
|  |             // - Library/ScriptAssemblies. This needs to be resolved to the full path. | ||
|  |             var defaultAssemblySearchPaths = AssemblyHelper.GetDefaultAssemblySearchPaths(); | ||
|  | #if UNITY_2021_3_OR_NEWER | ||
|  |             foreach (var searchPath in defaultAssemblySearchPaths) | ||
|  |             { | ||
|  |                 var resolvedPath = FileUtil.PathToAbsolutePath(searchPath); | ||
|  |                 if (!string.IsNullOrEmpty(resolvedPath)) | ||
|  |                 { | ||
|  |                     assemblyFolders.Add(resolvedPath); | ||
|  |                 } | ||
|  |             } | ||
|  | #else | ||
|  |             var packagesLookup = GetPackagesLookup(); | ||
|  |             foreach (var searchPath in defaultAssemblySearchPaths) | ||
|  |             { | ||
|  |                 if (TryResolvePath(searchPath, packagesLookup, out var resolvedPath)) | ||
|  |                 { | ||
|  |                     assemblyFolders.Add(resolvedPath); | ||
|  |                 } | ||
|  |             } | ||
|  | #endif | ||
|  | 
 | ||
|  |             if (IsDebugging) | ||
|  |             { | ||
|  |                 UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - AssemblyFolders : \n{string.Join("\n", assemblyFolders)}"); | ||
|  |             } | ||
|  | 
 | ||
|  |             return assemblyFolders.ToArray(); | ||
|  |         } | ||
|  | 
 | ||
|  | #if !UNITY_2021_3_OR_NEWER | ||
|  |         private static Dictionary<string, string> GetPackagesLookup() | ||
|  |         { | ||
|  |             var packages = new Dictionary<string, string>(); | ||
|  | 
 | ||
|  |             // Fetch list of packages | ||
|  | #if UNITY_2021_1_OR_NEWER | ||
|  |             var allPackages = UnityEditor.PackageManager.PackageInfo.GetAllRegisteredPackages(); | ||
|  | #else | ||
|  |             var allPackages = UnityEditor.PackageManager.PackageInfo.GetAll(); | ||
|  | #endif | ||
|  |             foreach (var p in allPackages) | ||
|  |             { | ||
|  |                 packages.Add(p.name, p.resolvedPath); | ||
|  |             } | ||
|  | 
 | ||
|  |             return packages; | ||
|  |         } | ||
|  | 
 | ||
|  |         private const string PackagesPath = "Packages/"; | ||
|  |         private static readonly int PackagesPathLength = PackagesPath.Length; | ||
|  | 
 | ||
|  |         private static bool TryResolvePath(string path, Dictionary<string, string> packagesLookup, out string resolvedPath) | ||
|  |         { | ||
|  |             if (string.IsNullOrEmpty(path)) | ||
|  |             { | ||
|  |                 resolvedPath = null; | ||
|  |                 return false; | ||
|  |             } | ||
|  |             else if (path.StartsWith("Packages/", StringComparison.InvariantCulture)) | ||
|  |             { | ||
|  |                 var secondSlashIndex = path.IndexOf('/', PackagesPathLength); | ||
|  |                 var packageName = secondSlashIndex > -1 | ||
|  |                     ? path.Substring(PackagesPathLength, secondSlashIndex - PackagesPathLength) | ||
|  |                     : path.Substring(PackagesPathLength); | ||
|  | 
 | ||
|  |                 if (packagesLookup.TryGetValue(packageName, out var resolvedPathTemp)) | ||
|  |                 { | ||
|  |                     path = secondSlashIndex > -1 | ||
|  |                         ? Path.Combine(resolvedPathTemp, path.Substring(secondSlashIndex + 1)) | ||
|  |                         : resolvedPathTemp; | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     if (IsDebugging) | ||
|  |                     { | ||
|  |                         UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - unknown package path '{path}'"); | ||
|  |                     } | ||
|  |                     resolvedPath = null; | ||
|  |                     return false; | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             resolvedPath = Path.GetFullPath(path); | ||
|  |             return true; | ||
|  |         } | ||
|  | #endif | ||
|  | 
 | ||
|  |         private static void OnCompilationFinished(object value) | ||
|  |         { | ||
|  |             if (_currentBuildKind!=CompilationTaskReason.IsForEditor) | ||
|  |             { | ||
|  |                 if (IsDebugging) | ||
|  |                 { | ||
|  |                     UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - ignoring finished compilation '{value}' because it's '{_currentBuildKind}'"); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 _currentBuildKind = CompilationTaskReason.IsForEditor; | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             if (IsDebugging) | ||
|  |             { | ||
|  |                 UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - compilation finished for '{value}'"); | ||
|  |             } | ||
|  | 
 | ||
|  |             BurstCompiler.NotifyCompilationFinished(); | ||
|  |         } | ||
|  | 
 | ||
|  | #if UNITY_2021_1_OR_NEWER | ||
|  |         private static void OnAssemblyCompilationFinished(ScriptAssembly assembly, CompilerMessage[] messages) | ||
|  | #else | ||
|  |         private static void OnAssemblyCompilationFinished(ScriptAssembly assembly, CompilerMessage[] messages, EditorScriptCompilationOptions options) | ||
|  | #endif | ||
|  |         { | ||
|  |             if (_currentBuildKind!=CompilationTaskReason.IsForEditor) | ||
|  |             { | ||
|  |                 if (IsDebugging) | ||
|  |                 { | ||
|  |                     UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - ignoring '{assembly.Filename}' because it's '{_currentBuildKind}'"); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             if (IsDebugging) | ||
|  |             { | ||
|  |                 UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - Assembly compilation finished for '{assembly.Filename}'"); | ||
|  |             } | ||
|  | 
 | ||
|  |             if (assembly.HasCompileErrors) | ||
|  |             { | ||
|  |                 if (IsDebugging) | ||
|  |                 { | ||
|  |                     UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - ignoring '{assembly.Filename}' because it failed C# compilation"); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             BurstCompiler.NotifyAssemblyCompilationFinished(Path.GetFileNameWithoutExtension(assembly.Filename), assembly.Defines); | ||
|  |         } | ||
|  | 
 | ||
|  |         private static void OnAssemblyCompilationNotRequired(string arg1) | ||
|  |         { | ||
|  |             if (_currentBuildKind!=CompilationTaskReason.IsForEditor) | ||
|  |             { | ||
|  |                 if (IsDebugging) | ||
|  |                 { | ||
|  |                     UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - ignoring '{arg1}' because it's '{_currentBuildKind}'"); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             if (IsDebugging) | ||
|  |             { | ||
|  |                 UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - Assembly compilation not required for '{arg1}'"); | ||
|  |             } | ||
|  | 
 | ||
|  |             BurstCompiler.NotifyAssemblyCompilationNotRequired(Path.GetFileNameWithoutExtension(arg1)); | ||
|  |         } | ||
|  | 
 | ||
|  |         private static bool TryGetOptionsFromMember(MemberInfo member, out string flagsOut) | ||
|  |         { | ||
|  |             return BurstCompiler.Options.TryGetOptions(member, out flagsOut); | ||
|  |         } | ||
|  | 
 | ||
|  |         private static void OnDomainUnload(object sender, EventArgs e) | ||
|  |         { | ||
|  |             if (IsDebugging) | ||
|  |             { | ||
|  |                 UnityEngine.Debug.Log($"Burst - OnDomainUnload"); | ||
|  |             } | ||
|  | 
 | ||
|  |             BurstCompiler.Cancel(); | ||
|  | 
 | ||
|  |             // This check here is to execute shutdown after all OnDisable's. EditorApplication.quitting event is called before OnDisable's, so we need to shutdown in here. | ||
|  |             if (_isQuitting) | ||
|  |             { | ||
|  |                 BurstCompiler.Shutdown(); | ||
|  |             } | ||
|  | 
 | ||
|  |             // Because of a check in Unity (specifically SCRIPTINGAPI_THREAD_AND_SERIALIZATION_CHECK), | ||
|  |             // we are not allowed to call thread-unsafe methods (like Progress.Exists) after the | ||
|  |             // kApplicationTerminating bit has been set. And because the domain is unloaded | ||
|  |             // (thus triggering AppDomain.DomainUnload) *after* that bit is set, we can't call Progress.Exists | ||
|  |             // during shutdown. So we check _isQuitting here. When quitting, it's fine for the progress item | ||
|  |             // not to be removed since it's all being torn down anyway. | ||
|  |             if (!_isQuitting && Progress.Exists(BurstProgressId)) | ||
|  |             { | ||
|  |                 Progress.Remove(BurstProgressId); | ||
|  |                 BurstProgressId = -1; | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     internal class BclConfiguration | ||
|  |     { | ||
|  |         public string FolderPath { get; set; } | ||
|  |         public string ExecutablePath { get; set; } | ||
|  |         public bool IsExecutableNative { get; set; } | ||
|  |     } | ||
|  | } | ||
|  | #endif |