public void OnPreprocessBuild(BuildReport report) { var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(report.summary.platform); // Early exit if burst is not activated if (aotSettingsForTarget.DisableBurstCompilation) { return; } if (report.summary.platform == BuildTarget.Switch) { // add the static lib, and the c++ shim string burstCppLinkFile = "lib_burst_generated.cpp"; string burstStaticLibFile = "lib_burst_generated.a"; string cppPath = Path.Combine(TempSourceLibrary, burstCppLinkFile); string libPath = Path.Combine(TempStaticLibrary, burstStaticLibFile); if (!Directory.Exists(TempSourceLibrary)) { Directory.CreateDirectory(TempSourceLibrary); Directory.CreateDirectory(TempSourceLibrary); } File.WriteAllText(cppPath, @" extern ""C"" { void Staticburst_initialize(void* ); void* StaticBurstStaticMethodLookup(void* ); int burst_enable_static_linkage = 1; void burst_initialize(void* i) { Staticburst_initialize(i); } void* BurstStaticMethodLookup(void* i) { return StaticBurstStaticMethodLookup(i); } } "); } }
internal static BurstPlatformAotSettings GetOrCreateSettings(BuildTarget target) { BurstPlatformAotSettings settings = new BurstPlatformAotSettings(); settings.DisableOptimisations = false; settings.DisableSafetyChecks = !BurstEditorOptions.EnableBurstSafetyChecks; settings.DisableBurstCompilation = !BurstEditorOptions.EnableBurstCompilation; return(settings); }
internal static BurstPlatformAotSettings GetOrCreateSettings(BuildTarget target) { BurstPlatformAotSettings settings = new BurstPlatformAotSettings(); settings.EnableOptimisations = true; settings.EnableSafetyChecks = BurstEditorOptions.EnableBurstSafetyChecks; settings.EnableBurstCompilation = BurstEditorOptions.EnableBurstCompilation; settings.UsePlatformSDKLinker = false; settings.EnableDebugInAllBuilds = false; return(settings); }
public static void OnPostProcessBuild(BuildTarget target, string path) { // We only support AOT compilation for ios from a macos host (we require xcrun and the apple tool chains) //for other hosts, we simply act as if burst is not being used (an error will be generated by the build aot step) //this keeps the behaviour consistent with how it was before static linkage was introduced if (target == BuildTarget.iOS) { var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(BuildTarget.iOS); // Early exit if burst is not activated if (!aotSettingsForTarget.EnableBurstCompilation) { return; } PostAddStaticLibraries(path); } }
internal static BurstPlatformAotSettings GetOrCreateSettings(BuildTarget target) { BurstPlatformAotSettings settings = new BurstPlatformAotSettings(target); string path = GetPath(target); if (File.Exists(path)) { var json = File.ReadAllText(path); EditorJsonUtility.FromJsonOverwrite(json, settings); } else { settings.Save(target); } return(settings); }
public override void OnActivate(string searchContext, VisualElement rootElement) { var platformFields = typeof(BurstPlatformAotSettings).GetFields(BindingFlags.NonPublic | BindingFlags.Instance); for (int p = 0; p < validPlatforms.Length; p++) { m_PlatformSettings[p] = BurstPlatformAotSettings.GetSerializedSettings(validPlatforms[p].defaultTarget); m_PlatformProperties[p] = new SerializedProperty[platformFields.Length]; m_PlatformToolTips[p] = new GUIContent[platformFields.Length]; for (int i = 0; i < platformFields.Length; i++) { m_PlatformProperties[p][i] = m_PlatformSettings[p].FindProperty(platformFields[i].Name); m_PlatformToolTips[p][i] = EditorGUIUtility.TrTextContent( typeof(BurstPlatformAotSettings).GetField(platformFields[i].Name + "_DisplayName", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as string, typeof(BurstPlatformAotSettings).GetField(platformFields[i].Name + "_ToolTip", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as string); } } }
internal static BurstPlatformAotSettings GetOrCreateSettings(BuildTarget target) { BurstPlatformAotSettings settings = ScriptableObject.CreateInstance <BurstPlatformAotSettings>(); settings.InitialiseDefaults(target); string path = GetPath(target); if (File.Exists(path)) { var json = File.ReadAllText(path); EditorJsonUtility.FromJsonOverwrite(json, settings); } else { settings.Save(target); } return(settings); }
internal static BurstPlatformAotSettings GetOrCreateSettings(BuildTarget target) { target = ResolveTarget(target); BurstPlatformAotSettings settings = ScriptableObject.CreateInstance <BurstPlatformAotSettings>(); settings.InitialiseDefaults(target); string path = GetPath(target); if (File.Exists(path)) { var json = File.ReadAllText(path); settings = SerialiseIn(target, json); } else { settings.Save(target); } return(settings); }
private void InitialiseSettingsForPlatform(int platform, FieldInfo[] platformFields) { if (validPlatforms[platform].targetGroup == BuildTargetGroup.Standalone) { m_PlatformSettings[platform] = BurstPlatformAotSettings.GetSerializedSettings(EditorUserBuildSettings.selectedStandaloneTarget); } else { m_PlatformSettings[platform] = BurstPlatformAotSettings.GetSerializedSettings(validPlatforms[platform].defaultTarget); } m_PlatformProperties[platform] = new SerializedProperty[platformFields.Length]; m_PlatformToolTips[platform] = new GUIContent[platformFields.Length]; for (int i = 0; i < platformFields.Length; i++) { m_PlatformProperties[platform][i] = m_PlatformSettings[platform].FindProperty(platformFields[i].Name); var displayName = typeof(BurstPlatformAotSettings).GetField(platformFields[i].Name + "_DisplayName", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as string; var toolTip = typeof(BurstPlatformAotSettings).GetField(platformFields[i].Name + "_ToolTip", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as string; m_PlatformToolTips[platform][i] = EditorGUIUtility.TrTextContent(displayName, toolTip); } }
internal static BurstPlatformAotSettings SerialiseIn(BuildTarget target, string json) { // Deal with pre version 2 format BurstPlatformLegacySettings legacy = (BurstPlatformLegacySettings)ScriptableObject.CreateInstance <BurstPlatformLegacySettings>(); EditorJsonUtility.FromJsonOverwrite(json, legacy); BurstPlatformAotSettings versioned = (BurstPlatformAotSettings)ScriptableObject.CreateInstance <BurstPlatformAotSettings>(); EditorJsonUtility.FromJsonOverwrite(json, versioned); if (versioned.Version == 0) { // legacy file, upgrade it versioned.InitialiseDefaults(target); versioned.EnableOptimisations = !legacy.DisableOptimisations; versioned.EnableBurstCompilation = !legacy.DisableBurstCompilation; versioned.EnableSafetyChecks = !legacy.DisableSafetyChecks; } // Otherwise should be a modern file with a valid version (we can use that to upgrade when the time comes) return(versioned); }
private void OnPostBuildPlayerScriptDLLsImpl(BuildReport report) { var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(report.summary.platform); // Early exit if burst is not activated or the platform is not supported if (aotSettingsForTarget.DisableBurstCompilation || !IsSupportedPlatform(report.summary.platform)) { return; } var commonOptions = new List <string>(); var stagingFolder = Path.GetFullPath(TempStagingManaged); var playerAssemblies = GetPlayerAssemblies(report); // grab the location of the root of the player folder - for handling nda platforms that require keys var keyFolder = BuildPipeline.GetPlaybackEngineDirectory(report.summary.platform, BuildOptions.None); commonOptions.Add(GetOption(OptionAotKeyFolder, keyFolder)); commonOptions.Add(GetOption(OptionAotDecodeFolder, Path.Combine(Environment.CurrentDirectory, "Library", "Burst"))); // Extract the TargetPlatform and Cpu from the current build settings TargetCpu targetCpu; var targetPlatform = GetTargetPlatformAndDefaultCpu(report.summary.platform, out targetCpu); commonOptions.Add(GetOption(OptionPlatform, targetPlatform)); // -------------------------------------------------------------------------------------------------------- // 1) Calculate AssemblyFolders // These are the folders to look for assembly resolution // -------------------------------------------------------------------------------------------------------- var assemblyFolders = new List <string> { stagingFolder }; if (report.summary.platform == BuildTarget.WSAPlayer || report.summary.platform == BuildTarget.XboxOne) { // On UWP, not all assemblies are copied to StagingArea, so we want to // find all directories that we can reference assemblies from // If we don't do this, we will crash with AssemblyResolutionException // when following type references. foreach (var assembly in playerAssemblies) { foreach (var assemblyRef in assembly.compiledAssemblyReferences) { // Exclude folders with assemblies already compiled in the `folder` var assemblyName = Path.GetFileName(assemblyRef); if (assemblyName != null && File.Exists(Path.Combine(stagingFolder, assemblyName))) { continue; } var directory = Path.GetDirectoryName(assemblyRef); if (directory != null) { var fullPath = Path.GetFullPath(directory); if (IsMonoReferenceAssemblyDirectory(fullPath) || IsDotNetStandardAssemblyDirectory(fullPath)) { // Don't pass reference assemblies to burst because they contain methods without implementation // If burst accidentally resolves them, it will emit calls to burst_abort. fullPath = Path.Combine(EditorApplication.applicationContentsPath, "MonoBleedingEdge/lib/mono/unityaot"); fullPath = Path.GetFullPath(fullPath); // GetFullPath will normalize path separators to OS native format if (!assemblyFolders.Contains(fullPath)) { assemblyFolders.Add(fullPath); } fullPath = Path.Combine(fullPath, "Facades"); if (!assemblyFolders.Contains(fullPath)) { assemblyFolders.Add(fullPath); } } else if (!assemblyFolders.Contains(fullPath)) { assemblyFolders.Add(fullPath); } } } } } // Copy assembly used during staging to have a trace if (BurstLoader.IsDebugging) { try { var copyAssemblyFolder = Path.Combine(Environment.CurrentDirectory, "Logs", "StagingAssemblies"); try { if (Directory.Exists(copyAssemblyFolder)) { Directory.Delete(copyAssemblyFolder); } } catch { } if (!Directory.Exists(copyAssemblyFolder)) { Directory.CreateDirectory(copyAssemblyFolder); } foreach (var file in Directory.EnumerateFiles(stagingFolder)) { File.Copy(file, Path.Combine(copyAssemblyFolder, Path.GetFileName(file))); } } catch { } } // -------------------------------------------------------------------------------------------------------- // 2) Calculate root assemblies // These are the assemblies that the compiler will look for methods to compile // This list doesn't typically include .NET runtime assemblies but only assemblies compiled as part // of the current Unity project // -------------------------------------------------------------------------------------------------------- var rootAssemblies = new List <string>(); foreach (var playerAssembly in playerAssemblies) { // the file at path `playerAssembly.outputPath` is actually not on the disk // while it is in the staging folder because OnPostBuildPlayerScriptDLLs is being called once the files are already // transferred to the staging folder, so we are going to work from it but we are reusing the file names that we got earlier var playerAssemblyPathToStaging = Path.Combine(stagingFolder, Path.GetFileName(playerAssembly.outputPath)); if (!File.Exists(playerAssemblyPathToStaging)) { Debug.LogWarning($"Unable to find player assembly: {playerAssemblyPathToStaging}"); } else { rootAssemblies.Add(playerAssemblyPathToStaging); } } commonOptions.AddRange(assemblyFolders.Select(folder => GetOption(OptionAotAssemblyFolder, folder))); // -------------------------------------------------------------------------------------------------------- // 3) Calculate the different target CPU combinations for the specified OS // // Typically, on some platforms like iOS we can be asked to compile a ARM32 and ARM64 CPU version // -------------------------------------------------------------------------------------------------------- var combinations = CollectCombinations(targetPlatform, targetCpu, report); // -------------------------------------------------------------------------------------------------------- // 4) Compile each combination // // Here bcl.exe is called for each target CPU combination // -------------------------------------------------------------------------------------------------------- string debugLogFile = null; if (BurstLoader.IsDebugging) { // Reset log files try { var logDir = Path.Combine(Environment.CurrentDirectory, "Logs"); debugLogFile = Path.Combine(logDir, "burst_bcl_editor.log"); if (!Directory.Exists(logDir)) { Directory.CreateDirectory(logDir); } File.WriteAllText(debugLogFile, string.Empty); } catch { debugLogFile = null; } } // Log the targets generated by BurstReflection.FindExecuteMethods foreach (var combination in combinations) { // Gets the output folder var stagingOutputFolder = Path.GetFullPath(Path.Combine(TempStaging, combination.OutputPath)); var outputFilePrefix = Path.Combine(stagingOutputFolder, combination.LibraryName); var options = new List <string>(commonOptions) { GetOption(OptionAotOutputPath, outputFilePrefix), GetOption(OptionTarget, combination.TargetCpu) }; if (targetPlatform == TargetPlatform.iOS || targetPlatform == TargetPlatform.Switch) { options.Add(GetOption(OptionStaticLinkage)); } // finally add method group options options.AddRange(rootAssemblies.Select(path => GetOption(OptionRootAssembly, path))); // Log the targets generated by BurstReflection.FindExecuteMethods if (BurstLoader.IsDebugging && debugLogFile != null) { try { var writer = new StringWriter(); writer.WriteLine("-----------------------------------------------------------"); writer.WriteLine("Combination: " + combination); writer.WriteLine("-----------------------------------------------------------"); foreach (var option in options) { writer.WriteLine(option); } writer.WriteLine("Assemblies in AssemblyFolders:"); foreach (var assemblyFolder in assemblyFolders) { writer.WriteLine("|- Folder: " + assemblyFolder); foreach (var assemblyOrDll in Directory.EnumerateFiles(assemblyFolder, "*.dll")) { var fileInfo = new FileInfo(assemblyOrDll); writer.WriteLine(" |- " + assemblyOrDll + " Size: " + fileInfo.Length + " Date: " + fileInfo.LastWriteTime); } } File.AppendAllText(debugLogFile, writer.ToString()); } catch { // ignored } } // Write current options to the response file var responseFile = Path.GetTempFileName(); File.WriteAllLines(responseFile, options); if (BurstLoader.IsDebugging) { Debug.Log($"bcl @{responseFile}\n\nResponse File:\n" + string.Join("\n", options)); } try { string generatedDebugInformationInOutput = ""; if ((report.summary.options & BuildOptions.Development) != 0) { generatedDebugInformationInOutput = GetOption(OptionDebug); } BclRunner.RunManagedProgram(Path.Combine(BurstLoader.RuntimePath, BurstAotCompilerExecutable), $"{generatedDebugInformationInOutput} \"@{responseFile}\"", new BclOutputErrorParser(), report); } catch (BuildFailedException) { throw; } catch (Exception e) { throw new BuildFailedException(e); } } }
private void OnPostBuildPlayerScriptDLLsImpl(BuildReport report) { BurstPlatformAotSettings aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(report.summary.platform); // Early exit if burst is not activated or the platform is not supported if (aotSettingsForTarget.DisableBurstCompilation || !IsSupportedPlatform(report.summary.platform)) { return; } // Collect all method signatures var methodsToCompile = BurstReflection.FindExecuteMethods(AssembliesType.Player); if (methodsToCompile.Count == 0) { return; // Nothing to do } // Prepare options // We are grouping methods per their compiler options (float precision...etc) var methodGroups = new Dictionary <string, List <string> >(); for (var i = 0; i < methodsToCompile.Count; i++) { var burstCompileTarget = methodsToCompile[i]; if (!burstCompileTarget.IsSupported) { continue; } var methodStr = BurstCompilerService.GetMethodSignature(burstCompileTarget.Method); var methodFullSignature = methodStr + "--" + Hash128.Compute(methodStr); if (aotSettingsForTarget.DisableOptimisations) { burstCompileTarget.Options.DisableOptimizations = true; } burstCompileTarget.Options.EnableBurstSafetyChecks = !aotSettingsForTarget.DisableSafetyChecks; string optionsAsStr; if (burstCompileTarget.TryGetOptionsAsString(false, out optionsAsStr)) { List <string> methodOptions; if (!methodGroups.TryGetValue(optionsAsStr, out methodOptions)) { methodOptions = new List <string>(); methodGroups.Add(optionsAsStr, methodOptions); } methodOptions.Add(GetOption(OptionAotMethod, methodFullSignature)); } } var methodGroupOptions = new List <string>(); // We should have something like this in the end: // // --group 1st group of method with the following shared options // --float-mode=xxx // --method=... // --method=... // // --group 2nd group of methods with the different shared options // --float-mode=yyy // --method=... // --method=... if (methodGroups.Count == 1) { var methodGroup = methodGroups.FirstOrDefault(); // No need to create a group if we don't have multiple methodGroupOptions.Add(methodGroup.Key); foreach (var methodOption in methodGroup.Value) { methodGroupOptions.Add(methodOption); } } else { foreach (var methodGroup in methodGroups) { methodGroupOptions.Add(GetOption(OptionGroup)); methodGroupOptions.Add(methodGroup.Key); foreach (var methodOption in methodGroup.Value) { methodGroupOptions.Add(methodOption); } } } var commonOptions = new List <string>(); var targetCpu = TargetCpu.Auto; var targetPlatform = GetTargetPlatformAndDefaultCpu(report.summary.platform, out targetCpu); commonOptions.Add(GetOption(OptionPlatform, targetPlatform)); // TODO: Add support for configuring the optimizations/CPU // TODO: Add support for per method options var stagingFolder = Path.GetFullPath(TempStagingManaged); //Debug.Log($"Burst CompileAot - To Folder {stagingFolder}"); // Prepare assembly folder list var assemblyFolders = new List <string>(); assemblyFolders.Add(stagingFolder); var playerAssemblies = CompilationPipeline.GetAssemblies(AssembliesType.Player); foreach (var assembly in playerAssemblies) { foreach (var assemblyRef in assembly.compiledAssemblyReferences) { // Exclude folders with assemblies already compiled in the `folder` var assemblyName = Path.GetFileName(assemblyRef); if (assemblyName != null && File.Exists(Path.Combine(stagingFolder, assemblyName))) { continue; } var directory = Path.GetDirectoryName(assemblyRef); if (directory != null) { var fullPath = Path.GetFullPath(directory); if (IsMonoReferenceAssemblyDirectory(fullPath) || IsDotNetStandardAssemblyDirectory(fullPath)) { // Don't pass reference assemblies to burst because they contain methods without implementation // If burst accidentally resolves them, it will emit calls to burst_abort. fullPath = Path.Combine(EditorApplication.applicationContentsPath, "MonoBleedingEdge/lib/mono/unityaot"); fullPath = Path.GetFullPath(fullPath); // GetFullPath will normalize path separators to OS native format if (!assemblyFolders.Contains(fullPath)) { assemblyFolders.Add(fullPath); } fullPath = Path.Combine(fullPath, "Facades"); if (!assemblyFolders.Contains(fullPath)) { assemblyFolders.Add(fullPath); } } else if (!assemblyFolders.Contains(fullPath)) { assemblyFolders.Add(fullPath); } } } } commonOptions.AddRange(assemblyFolders.Select(folder => GetOption(OptionAotAssemblyFolder, folder))); var combinations = new List <BurstOutputCombination>(); if (targetPlatform == TargetPlatform.macOS) { // NOTE: OSX has a special folder for the plugin // Declared in GetStagingAreaPluginsFolder // PlatformDependent\OSXPlayer\Extensions\Managed\OSXDesktopStandalonePostProcessor.cs combinations.Add(new BurstOutputCombination("UnityPlayer.app/Contents/Plugins", targetCpu)); } else if (targetPlatform == TargetPlatform.iOS) { if (Application.platform != RuntimePlatform.OSXEditor) { Debug.LogWarning("Burst Cross Compilation to iOS for standalone player, is only supported on OSX Editor at this time, burst is disabled for this build."); } else { var targetArchitecture = (IOSArchitecture)UnityEditor.PlayerSettings.GetArchitecture(report.summary.platformGroup); if (targetArchitecture == IOSArchitecture.ARMv7 || targetArchitecture == IOSArchitecture.Universal) { // PlatformDependent\iPhonePlayer\Extensions\Common\BuildPostProcessor.cs combinations.Add(new BurstOutputCombination("StaticLibraries", TargetCpu.ARMV7A_NEON32, DefaultLibraryName + "32")); } if (targetArchitecture == IOSArchitecture.ARM64 || targetArchitecture == IOSArchitecture.Universal) { // PlatformDependent\iPhonePlayer\Extensions\Common\BuildPostProcessor.cs combinations.Add(new BurstOutputCombination("StaticLibraries", TargetCpu.ARMV8A_AARCH64, DefaultLibraryName + "64")); } } } else if (targetPlatform == TargetPlatform.Android) { //TODO: would be better to query AndroidNdkRoot (but thats not exposed from unity) string ndkRoot = null; // 2019.1 now has an embedded ndk #if UNITY_2019_1_OR_NEWER if (EditorPrefs.HasKey("NdkUseEmbedded")) { if (EditorPrefs.GetBool("NdkUseEmbedded")) { ndkRoot = Path.Combine(BuildPipeline.GetPlaybackEngineDirectory(BuildTarget.Android, BuildOptions.None), "NDK"); } else { ndkRoot = EditorPrefs.GetString("AndroidNdkRootR16b"); } } #endif // If we still don't have a valid root, try the old key if (string.IsNullOrEmpty(ndkRoot)) { ndkRoot = EditorPrefs.GetString("AndroidNdkRoot"); } // Verify the directory at least exists, if not we fall back to ANDROID_NDK_ROOT current setting if (!string.IsNullOrEmpty(ndkRoot) && !Directory.Exists(ndkRoot)) { ndkRoot = null; } // Always set the ANDROID_NDK_ROOT (if we got a valid result from above), so BCL knows where to find the Android toolchain and its the one the user expects if (!string.IsNullOrEmpty(ndkRoot)) { Environment.SetEnvironmentVariable("ANDROID_NDK_ROOT", ndkRoot); } var androidTargetArch = UnityEditor.PlayerSettings.Android.targetArchitectures; if ((androidTargetArch & AndroidArchitecture.ARMv7) != 0) { combinations.Add(new BurstOutputCombination("libs/armeabi-v7a", TargetCpu.ARMV7A_NEON32)); } if ((androidTargetArch & AndroidArchitecture.ARM64) != 0) { combinations.Add(new BurstOutputCombination("libs/arm64-v8a", TargetCpu.ARMV8A_AARCH64)); } if ((androidTargetArch & AndroidArchitecture.X86) != 0) { combinations.Add(new BurstOutputCombination("libs/x86", TargetCpu.X86_SSE2)); } } else if (targetPlatform == TargetPlatform.UWP) { // TODO: Make it configurable for x86 (sse2, sse4) combinations.Add(new BurstOutputCombination("Plugins/x64", TargetCpu.X64_SSE4)); combinations.Add(new BurstOutputCombination("Plugins/x86", TargetCpu.X86_SSE2)); combinations.Add(new BurstOutputCombination("Plugins/ARM", TargetCpu.THUMB2_NEON32)); combinations.Add(new BurstOutputCombination("Plugins/ARM64", TargetCpu.ARMV8A_AARCH64)); } else { combinations.Add(new BurstOutputCombination("Data/Plugins/", targetCpu)); } foreach (var combination in combinations) { // Gets the output folder var stagingOutputFolder = Path.GetFullPath(Path.Combine(TempStaging, combination.OutputPath)); var outputFilePrefix = Path.Combine(stagingOutputFolder, combination.LibraryName); var options = new List <string>(commonOptions); options.Add(GetOption(OptionAotOutputPath, outputFilePrefix)); options.Add(GetOption(OptionTarget, combination.TargetCpu)); if (targetPlatform == TargetPlatform.iOS) { options.Add(GetOption(OptionStaticLinkage)); } // finally add method group options options.AddRange(methodGroupOptions); var responseFile = Path.GetTempFileName(); File.WriteAllLines(responseFile, options); //Debug.Log("Burst compile with response file: " + responseFile); try { string generatedDebugInformationInOutput = ""; if ((report.summary.options & BuildOptions.Development) != 0) { // Workaround for apple clang development issue (due to latest being 7.0.0) - IOS is only affected because its a source level IR compatability issue if (targetPlatform != TargetPlatform.iOS) { generatedDebugInformationInOutput = GetOption(OptionDebug); } } BclRunner.RunManagedProgram(Path.Combine(BurstLoader.RuntimePath, BurstAotCompilerExecutable), $"{generatedDebugInformationInOutput} @{responseFile}", new BclOutputErrorParser(), report); } catch (Exception e) { // We don't expect any error, but in case we have one, we identify that it is burst and we print the details here Debug.LogError("Unexpected error while running the burst compiler: " + e); } } }
public void OnPostBuildPlayerScriptDLLs(BuildReport report) { BurstPlatformAotSettings aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(report.summary.platform); // Early exit if burst is not activated or the platform is not supported if (aotSettingsForTarget.DisableBurstCompilation || !IsSupportedPlatform(report.summary.platform)) { return; } // Collect all method signatures var methodsToCompile = BurstReflection.FindExecuteMethods(AssembliesType.Player); if (methodsToCompile.Count == 0) { return; // Nothing to do } // Prepare options var commonOptions = new List <string>(); for (var i = 0; i < methodsToCompile.Count; i++) { var burstCompileTarget = methodsToCompile[i]; if (!burstCompileTarget.SupportsBurst) { continue; } var methodStr = BurstCompilerService.GetMethodSignature(burstCompileTarget.Method); var methodFullSignature = methodStr + "--" + Hash128.Compute(methodStr); commonOptions.Add(GetOption(OptionAotMethod, methodFullSignature)); } var targetCpu = TargetCpu.Auto; var targetPlatform = GetTargetPlatformAndDefaultCpu(report.summary.platform, out targetCpu); commonOptions.Add(GetOption(OptionPlatform, targetPlatform)); if (!BurstEditorOptions.EnableBurstSafetyChecks) { commonOptions.Add(GetOption(OptionDisableSafetyChecks)); } // TODO: Add support for configuring the optimizations/CPU // TODO: Add support for per method options var stagingFolder = Path.GetFullPath(TempStagingManaged); //Debug.Log($"Burst CompileAot - To Folder {stagingFolder}"); // Prepare assembly folder list var assemblyFolders = new List <string>(); assemblyFolders.Add(stagingFolder); var playerAssemblies = CompilationPipeline.GetAssemblies(AssembliesType.Player); foreach (var assembly in playerAssemblies) { foreach (var assemblyRef in assembly.compiledAssemblyReferences) { // Exclude folders with assemblies already compiled in the `folder` var assemblyName = Path.GetFileName(assemblyRef); if (assemblyName != null && File.Exists(Path.Combine(stagingFolder, assemblyName))) { continue; } var directory = Path.GetDirectoryName(assemblyRef); if (directory != null) { var fullPath = Path.GetFullPath(directory); if (IsMonoReferenceAssemblyDirectory(fullPath) || IsDotNetStandardAssemblyDirectory(fullPath)) { // Don't pass reference assemblies to burst because they contain methods without implementation // If burst accidentally resolves them, it will emit calls to burst_abort. fullPath = Path.Combine(EditorApplication.applicationContentsPath, "MonoBleedingEdge/lib/mono/unityaot"); fullPath = Path.GetFullPath(fullPath); // GetFullPath will normalize path separators to OS native format if (!assemblyFolders.Contains(fullPath)) { assemblyFolders.Add(fullPath); } fullPath = Path.Combine(fullPath, "Facades"); if (!assemblyFolders.Contains(fullPath)) { assemblyFolders.Add(fullPath); } } else if (!assemblyFolders.Contains(fullPath)) { assemblyFolders.Add(fullPath); } } } } commonOptions.AddRange(assemblyFolders.Select(folder => GetOption(OptionAotAssemblyFolder, folder))); // Gets platform specific IL2CPP plugin folder // Only the following platforms are providing a dedicated Tools directory switch (report.summary.platform) { case BuildTarget.XboxOne: case BuildTarget.PS4: case BuildTarget.Android: case BuildTarget.iOS: var pluginFolder = BuildPipeline.GetBuildToolsDirectory(report.summary.platform); commonOptions.Add(GetOption(OptionAotIL2CPPPluginFolder, pluginFolder)); break; } var combinations = new List <BurstOutputCombination>(); if (targetPlatform == TargetPlatform.macOS) { // NOTE: OSX has a special folder for the plugin // Declared in GetStagingAreaPluginsFolder // PlatformDependent\OSXPlayer\Extensions\Managed\OSXDesktopStandalonePostProcessor.cs combinations.Add(new BurstOutputCombination("UnityPlayer.app/Contents/Plugins", targetCpu)); } else if (targetPlatform == TargetPlatform.iOS) { // Check if we are under il2cpp as we may have to force a CPU backend for it // TODO: Should use report.summary.platformGroup instead? bool isUsingIL2CPP = PlayerSettings.GetScriptingBackend(BuildTargetGroup.Standalone) == ScriptingImplementation.IL2CPP; if (!isUsingIL2CPP) { var targetArchitecture = (IOSArchitecture)UnityEditor.PlayerSettings.GetArchitecture(report.summary.platformGroup); switch (targetArchitecture) { case IOSArchitecture.ARMv7: targetCpu = TargetCpu.ARMV7A_NEON32; break; case IOSArchitecture.ARM64: targetCpu = TargetCpu.ARMV8A_AARCH64; break; case IOSArchitecture.Universal: // TODO: How do we proceed here? targetCpu = TargetCpu.ARMV7A_NEON32; break; } } // PlatformDependent\iPhonePlayer\Extensions\Common\BuildPostProcessor.cs combinations.Add(new BurstOutputCombination("Frameworks", targetCpu)); } else if (targetPlatform == TargetPlatform.Android) { // Set the ANDROID_NDK_ROOT so IL2CPP knows where to find the Android toolchain if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ANDROID_NDK_ROOT"))) { var ndkRoot = EditorPrefs.GetString("AndroidNdkRoot"); if (!string.IsNullOrEmpty(ndkRoot)) { Environment.SetEnvironmentVariable("ANDROID_NDK_ROOT", ndkRoot); } } var androidTargetArch = UnityEditor.PlayerSettings.Android.targetArchitectures; if ((androidTargetArch & AndroidArchitecture.ARMv7) != 0) { combinations.Add(new BurstOutputCombination("libs/armeabi-v7a", TargetCpu.ARMV7A_NEON32)); } if ((androidTargetArch & AndroidArchitecture.ARM64) != 0) { combinations.Add(new BurstOutputCombination("libs/arm64-v8a", TargetCpu.ARMV8A_AARCH64)); } if ((androidTargetArch & AndroidArchitecture.X86) != 0) { combinations.Add(new BurstOutputCombination("libs/x86", TargetCpu.X86_SSE2)); } } else if (targetPlatform == TargetPlatform.UWP) { // TODO: Make it configurable for x86 (sse2, sse4) combinations.Add(new BurstOutputCombination("Plugins/x64", TargetCpu.X64_SSE4)); combinations.Add(new BurstOutputCombination("Plugins/x86", TargetCpu.X86_SSE2)); combinations.Add(new BurstOutputCombination("Plugins/ARM", TargetCpu.THUMB2_NEON32)); combinations.Add(new BurstOutputCombination("Plugins/ARM64", TargetCpu.ARMV8A_AARCH64)); } else { combinations.Add(new BurstOutputCombination("Data/Plugins/", targetCpu)); } foreach (var combination in combinations) { // Gets the output folder var stagingOutputFolder = Path.GetFullPath(Path.Combine(TempStaging, combination.OutputPath)); var outputFilePrefix = Path.Combine(stagingOutputFolder, DefaultLibraryName); var options = new List <string>(commonOptions); options.Add(GetOption(OptionAotOutputPath, outputFilePrefix)); options.Add(GetOption(OptionTarget, combination.TargetCpu)); if (aotSettingsForTarget.DisableOptimisations) { options.Add(GetOption(OptionDisableOpt)); } if (aotSettingsForTarget.DisableSafetyChecks) { options.Add(GetOption(OptionDisableSafetyChecks)); } else { options.Add(GetOption(OptionSafetyChecks)); } var responseFile = Path.GetTempFileName(); File.WriteAllLines(responseFile, options); //Debug.Log("Burst compile with response file: " + responseFile); try { Runner.RunManagedProgram(Path.Combine(BurstLoader.RuntimePath, BurstAotCompilerExecutable), "--debug=true " + "@" + responseFile, Application.dataPath + "/..", new BclParser(), null); } catch (Exception e) { Debug.LogError(e.ToString()); } } }
private static TargetPlatform?TryGetTargetPlatform(BuildTarget target, out MinMaxTargetCpu targetCpu) { var aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(target); // TODO: Add support for multi-CPU switch targetCpu.min = TargetCpu.Auto; targetCpu.max = TargetCpu.Auto; switch (target) { case BuildTarget.StandaloneWindows: targetCpu = aotSettingsForTarget.GetDesktopCpu32Bit(); return(TargetPlatform.Windows); case BuildTarget.StandaloneWindows64: targetCpu = aotSettingsForTarget.GetDesktopCpu64Bit(); return(TargetPlatform.Windows); case BuildTarget.StandaloneOSX: targetCpu = aotSettingsForTarget.GetDesktopCpu64Bit(); return(TargetPlatform.macOS); #if !UNITY_2019_2_OR_NEWER //32 bit linux support was deprecated case BuildTarget.StandaloneLinux: targetCpu = aotSettingsForTarget.GetDesktopCpu32Bit(); return(TargetPlatform.Linux); #endif case BuildTarget.StandaloneLinux64: targetCpu = aotSettingsForTarget.GetDesktopCpu64Bit(); return(TargetPlatform.Linux); case BuildTarget.WSAPlayer: targetCpu = new MinMaxTargetCpu(TargetCpu.X64_SSE4); return(TargetPlatform.UWP); case BuildTarget.XboxOne: targetCpu = new MinMaxTargetCpu(TargetCpu.X64_SSE4); return(TargetPlatform.XboxOne); case BuildTarget.PS4: targetCpu = new MinMaxTargetCpu(TargetCpu.X64_SSE4); return(TargetPlatform.PS4); case BuildTarget.Android: targetCpu = new MinMaxTargetCpu(TargetCpu.ARMV7A_NEON32); return(TargetPlatform.Android); case BuildTarget.iOS: targetCpu = new MinMaxTargetCpu(TargetCpu.ARMV7A_NEON32); return(TargetPlatform.iOS); case BuildTarget.Lumin: targetCpu = new MinMaxTargetCpu(TargetCpu.ARMV8A_AARCH64); return(TargetPlatform.Lumin); case BuildTarget.Switch: targetCpu = new MinMaxTargetCpu(TargetCpu.ARMV8A_AARCH64); return(TargetPlatform.Switch); #if UNITY_2019_3_OR_NEWER case BuildTarget.Stadia: targetCpu = new MinMaxTargetCpu(TargetCpu.AVX2); return(TargetPlatform.Stadia); #endif } return(null); }
public void OnPostBuildPlayerScriptDLLs(BuildReport report) { BurstPlatformAotSettings aotSettingsForTarget = BurstPlatformAotSettings.GetOrCreateSettings(report.summary.platform); // Early exit if burst is not activated or the platform is not supported if (aotSettingsForTarget.DisableBurstCompilation || !IsSupportedPlatform(report.summary.platform)) { return; } // Collect all method signatures var methodsToCompile = BurstReflection.FindExecuteMethods(AssembliesType.Player); if (methodsToCompile.Count == 0) { return; // Nothing to do } // Prepare options // We are grouping methods per their compiler options (float precision...etc) var methodGroups = new Dictionary <string, List <string> >(); for (var i = 0; i < methodsToCompile.Count; i++) { var burstCompileTarget = methodsToCompile[i]; if (!burstCompileTarget.IsSupported) { continue; } var methodStr = BurstCompilerService.GetMethodSignature(burstCompileTarget.Method); var methodFullSignature = methodStr + "--" + Hash128.Compute(methodStr); if (aotSettingsForTarget.DisableOptimisations) { burstCompileTarget.Options.DisableOptimizations = true; } burstCompileTarget.Options.EnableBurstSafetyChecks = !aotSettingsForTarget.DisableSafetyChecks; string optionsAsStr; if (burstCompileTarget.TryGetOptionsAsString(false, out optionsAsStr)) { List <string> methodOptions; if (!methodGroups.TryGetValue(optionsAsStr, out methodOptions)) { methodOptions = new List <string>(); methodGroups.Add(optionsAsStr, methodOptions); } methodOptions.Add(GetOption(OptionAotMethod, methodFullSignature)); } } var methodGroupOptions = new List <string>(); // We should have something like this in the end: // // --group 1st group of method with the following shared options // --float-mode=xxx // --method=... // --method=... // // --group 2nd group of methods with the different shared options // --float-mode=yyy // --method=... // --method=... if (methodGroups.Count == 1) { var methodGroup = methodGroups.FirstOrDefault(); // No need to create a group if we don't have multiple methodGroupOptions.Add(methodGroup.Key); foreach (var methodOption in methodGroup.Value) { methodGroupOptions.Add(methodOption); } } else { foreach (var methodGroup in methodGroups) { methodGroupOptions.Add(GetOption(OptionGroup)); methodGroupOptions.Add(methodGroup.Key); foreach (var methodOption in methodGroup.Value) { methodGroupOptions.Add(methodOption); } } } var commonOptions = new List <string>(); var targetCpu = TargetCpu.Auto; var targetPlatform = GetTargetPlatformAndDefaultCpu(report.summary.platform, out targetCpu); commonOptions.Add(GetOption(OptionPlatform, targetPlatform)); // TODO: Add support for configuring the optimizations/CPU // TODO: Add support for per method options var stagingFolder = Path.GetFullPath(TempStagingManaged); //Debug.Log($"Burst CompileAot - To Folder {stagingFolder}"); // Prepare assembly folder list var assemblyFolders = new List <string>(); assemblyFolders.Add(stagingFolder); var playerAssemblies = CompilationPipeline.GetAssemblies(AssembliesType.Player); foreach (var assembly in playerAssemblies) { foreach (var assemblyRef in assembly.compiledAssemblyReferences) { // Exclude folders with assemblies already compiled in the `folder` var assemblyName = Path.GetFileName(assemblyRef); if (assemblyName != null && File.Exists(Path.Combine(stagingFolder, assemblyName))) { continue; } var directory = Path.GetDirectoryName(assemblyRef); if (directory != null) { var fullPath = Path.GetFullPath(directory); if (IsMonoReferenceAssemblyDirectory(fullPath) || IsDotNetStandardAssemblyDirectory(fullPath)) { // Don't pass reference assemblies to burst because they contain methods without implementation // If burst accidentally resolves them, it will emit calls to burst_abort. fullPath = Path.Combine(EditorApplication.applicationContentsPath, "MonoBleedingEdge/lib/mono/unityaot"); fullPath = Path.GetFullPath(fullPath); // GetFullPath will normalize path separators to OS native format if (!assemblyFolders.Contains(fullPath)) { assemblyFolders.Add(fullPath); } fullPath = Path.Combine(fullPath, "Facades"); if (!assemblyFolders.Contains(fullPath)) { assemblyFolders.Add(fullPath); } } else if (!assemblyFolders.Contains(fullPath)) { assemblyFolders.Add(fullPath); } } } } commonOptions.AddRange(assemblyFolders.Select(folder => GetOption(OptionAotAssemblyFolder, folder))); var combinations = new List <BurstOutputCombination>(); if (targetPlatform == TargetPlatform.macOS) { // NOTE: OSX has a special folder for the plugin // Declared in GetStagingAreaPluginsFolder // PlatformDependent\OSXPlayer\Extensions\Managed\OSXDesktopStandalonePostProcessor.cs combinations.Add(new BurstOutputCombination("UnityPlayer.app/Contents/Plugins", targetCpu)); } else if (targetPlatform == TargetPlatform.iOS) { var targetArchitecture = (IOSArchitecture)UnityEditor.PlayerSettings.GetArchitecture(report.summary.platformGroup); if (targetArchitecture == IOSArchitecture.ARMv7 || targetArchitecture == IOSArchitecture.Universal) { // PlatformDependent\iPhonePlayer\Extensions\Common\BuildPostProcessor.cs combinations.Add(new BurstOutputCombination("StaticLibraries", TargetCpu.ARMV7A_NEON32, DefaultLibraryName + "32")); } if (targetArchitecture == IOSArchitecture.ARM64 || targetArchitecture == IOSArchitecture.Universal) { // PlatformDependent\iPhonePlayer\Extensions\Common\BuildPostProcessor.cs combinations.Add(new BurstOutputCombination("StaticLibraries", TargetCpu.ARMV8A_AARCH64, DefaultLibraryName + "64")); } } else if (targetPlatform == TargetPlatform.Android) { // Set the ANDROID_NDK_ROOT so BCL knows where to find the Android toolchain if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ANDROID_NDK_ROOT"))) { var ndkRoot = EditorPrefs.GetString("AndroidNdkRoot"); if (!string.IsNullOrEmpty(ndkRoot)) { Environment.SetEnvironmentVariable("ANDROID_NDK_ROOT", ndkRoot); } } var androidTargetArch = UnityEditor.PlayerSettings.Android.targetArchitectures; if ((androidTargetArch & AndroidArchitecture.ARMv7) != 0) { combinations.Add(new BurstOutputCombination("libs/armeabi-v7a", TargetCpu.ARMV7A_NEON32)); } if ((androidTargetArch & AndroidArchitecture.ARM64) != 0) { combinations.Add(new BurstOutputCombination("libs/arm64-v8a", TargetCpu.ARMV8A_AARCH64)); } if ((androidTargetArch & AndroidArchitecture.X86) != 0) { combinations.Add(new BurstOutputCombination("libs/x86", TargetCpu.X86_SSE2)); } } else if (targetPlatform == TargetPlatform.UWP) { // TODO: Make it configurable for x86 (sse2, sse4) combinations.Add(new BurstOutputCombination("Plugins/x64", TargetCpu.X64_SSE4)); combinations.Add(new BurstOutputCombination("Plugins/x86", TargetCpu.X86_SSE2)); combinations.Add(new BurstOutputCombination("Plugins/ARM", TargetCpu.THUMB2_NEON32)); combinations.Add(new BurstOutputCombination("Plugins/ARM64", TargetCpu.ARMV8A_AARCH64)); } else { combinations.Add(new BurstOutputCombination("Data/Plugins/", targetCpu)); } foreach (var combination in combinations) { // Gets the output folder var stagingOutputFolder = Path.GetFullPath(Path.Combine(TempStaging, combination.OutputPath)); var outputFilePrefix = Path.Combine(stagingOutputFolder, combination.LibraryName); var options = new List <string>(commonOptions); options.Add(GetOption(OptionAotOutputPath, outputFilePrefix)); options.Add(GetOption(OptionTarget, combination.TargetCpu)); if (targetPlatform == TargetPlatform.iOS) { options.Add(GetOption(OptionStaticLinkage)); } // finally add method group options options.AddRange(methodGroupOptions); var responseFile = Path.GetTempFileName(); File.WriteAllLines(responseFile, options); //Debug.Log("Burst compile with response file: " + responseFile); try { Runner.RunManagedProgram(Path.Combine(BurstLoader.RuntimePath, BurstAotCompilerExecutable), "--debug=true " + "@" + responseFile, Application.dataPath + "/..", new BclParser(), null); } catch (Exception e) { Debug.LogError(e.ToString()); } } }