first commit
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Burst.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Allow disabling of the Burst compiler for specific assemblies.
|
||||
/// ProjectSettings/Burst_DisableAssembliesForEditorCompilation.json
|
||||
/// ProjectSettings/Burst_DisableAssembliesForPlayerCompilation.json
|
||||
/// ProjectSettings/Burst_DisableAssembliesForPlayerCompilation_{platform}.json // if exists taken in preference to the one immediately above
|
||||
/// </summary>
|
||||
internal static class BurstAssemblyDisable
|
||||
{
|
||||
public enum DisableType
|
||||
{
|
||||
Editor,
|
||||
Player
|
||||
}
|
||||
|
||||
private static string GetPath(DisableType type, string platformIdentifier)
|
||||
{
|
||||
if (DisableType.Editor == type)
|
||||
{
|
||||
return "ProjectSettings/Burst_DisableAssembliesForEditorCompilation.json";
|
||||
}
|
||||
var platformSpecicPath = $"ProjectSettings/Burst_DisableAssembliesForPlayerCompilation_{platformIdentifier}.json";
|
||||
if (File.Exists(platformSpecicPath))
|
||||
{
|
||||
return platformSpecicPath;
|
||||
}
|
||||
return "ProjectSettings/Burst_DisableAssembliesForPlayerCompilation.json";
|
||||
}
|
||||
|
||||
public static string[] GetDisabledAssemblies(DisableType type, string platformIdentifier)
|
||||
{
|
||||
var pathForSettings = GetPath(type, platformIdentifier);
|
||||
if (!File.Exists(pathForSettings))
|
||||
{
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
var settings = new BackwardsCompatWrapper();
|
||||
JsonUtility.FromJsonOverwrite(File.ReadAllText(pathForSettings),settings);
|
||||
if (settings == null || settings.MonoBehaviour == null || settings.MonoBehaviour.DisabledAssemblies == null)
|
||||
{
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
return settings.MonoBehaviour.DisabledAssemblies;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Settings file -
|
||||
///
|
||||
///{
|
||||
/// "MonoBehaviour": {
|
||||
/// "DisabledAssemblies":
|
||||
/// [
|
||||
/// "Example.Assembly"
|
||||
/// ]
|
||||
/// }
|
||||
///}
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
class BackwardsCompatWrapper
|
||||
{
|
||||
public BurstDisableSettings MonoBehaviour;
|
||||
}
|
||||
[Serializable]
|
||||
class BurstDisableSettings
|
||||
{
|
||||
public string[] DisabledAssemblies;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24e5242d7f213ce49a52eefb5d544d24
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,183 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Unity.Burst.Editor
|
||||
{
|
||||
[DebuggerDisplay("{GetDisplayName(),nq}")]
|
||||
internal class BurstCompileTarget
|
||||
{
|
||||
public BurstCompileTarget(MethodInfo method, Type jobType, Type interfaceType, bool isStaticMethod)
|
||||
{
|
||||
Method = method ?? throw new ArgumentNullException(nameof(method));
|
||||
JobType = jobType ?? throw new ArgumentNullException(nameof(jobType));
|
||||
JobInterfaceType = interfaceType; // can be null
|
||||
// This is important to clone the options as we don't want to modify the global instance
|
||||
Options = BurstCompiler.Options.Clone();
|
||||
Options.EnableBurstCompilation = true;
|
||||
// Enable safety checks by default to match inspector default behavior
|
||||
Options.EnableBurstSafetyChecks = true;
|
||||
TargetCpu = BurstTargetCpu.Auto;
|
||||
// The BurstCompilerAttribute can be either on the type or on the method
|
||||
IsStaticMethod = isStaticMethod;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <c>true</c> if the <see cref="Method"/> is directly tagged with a [BurstCompile] attribute
|
||||
/// </summary>
|
||||
public readonly bool IsStaticMethod;
|
||||
|
||||
/// <summary>
|
||||
/// The Execute method of the target's producer type.
|
||||
/// </summary>
|
||||
public readonly MethodInfo Method;
|
||||
|
||||
/// <summary>
|
||||
/// The type of the actual job (i.e. BoidsSimulationJob).
|
||||
/// </summary>
|
||||
public readonly Type JobType;
|
||||
|
||||
/// <summary>
|
||||
/// The interface of the job (IJob, IJobParallelFor...)
|
||||
/// </summary>
|
||||
public readonly Type JobInterfaceType;
|
||||
|
||||
/// <summary>
|
||||
/// The default compiler options
|
||||
/// </summary>
|
||||
public readonly BurstCompilerOptions Options;
|
||||
|
||||
public BurstTargetCpu TargetCpu { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if burst compilation is actually requested via proper `[BurstCompile]` attribute:
|
||||
/// - On the job if it is a job only
|
||||
/// - On the method and parent class it if is a static method
|
||||
/// </summary>
|
||||
public bool HasRequiredBurstCompileAttributes => BurstCompilerOptions.HasBurstCompileAttribute(JobType) && (!IsStaticMethod || BurstCompilerOptions.HasBurstCompileAttribute(Method));
|
||||
|
||||
/// <summary>
|
||||
/// Generated raw disassembly (IR, IL, ASM...), or null if disassembly failed (only valid for the current TargetCpu)
|
||||
/// </summary>
|
||||
public string RawDisassembly;
|
||||
|
||||
/// <summary>
|
||||
/// Formatted disassembly for the associated <see cref="RawDisassembly"/>, currently only valid for <see cref="Unity.Burst.Editor.DisassemblyKind.Asm"/>
|
||||
/// </summary>
|
||||
public string FormattedDisassembly;
|
||||
|
||||
public DisassemblyKind DisassemblyKind;
|
||||
|
||||
public bool IsDarkMode { get; set; }
|
||||
|
||||
public bool IsBurstError { get; set; }
|
||||
|
||||
public bool IsLoading = false;
|
||||
|
||||
public bool JustLoaded = false;
|
||||
|
||||
public string GetDisplayName()
|
||||
{
|
||||
var displayName = IsStaticMethod ? Pretty(Method) : $"{Pretty(JobType)} - ({Pretty(JobInterfaceType)})";
|
||||
|
||||
// Remove the '<>c__DisplayClass_' part of the name - this is only added for C# Entities.ForEach jobs to trick the C# debugging tools into
|
||||
// treating them like lambdas. This is removed wherever possible from user facing tools (like the Unity profiler), so we should do the same.
|
||||
return displayName.Replace("<>c__DisplayClass_", "");
|
||||
}
|
||||
|
||||
private static string Pretty(MethodInfo method)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.Append(Pretty(method.DeclaringType));
|
||||
builder.Append(".");
|
||||
builder.Append(method.Name);
|
||||
builder.Append("(");
|
||||
var parameters = method.GetParameters();
|
||||
for (var i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var param = parameters[i];
|
||||
if (i > 0) builder.Append(", ");
|
||||
builder.Append(Pretty(param.ParameterType));
|
||||
}
|
||||
|
||||
builder.Append(")");
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
internal static string Pretty(Type type)
|
||||
{
|
||||
if (type == typeof(bool))
|
||||
{
|
||||
return "bool";
|
||||
}
|
||||
if (type == typeof(int))
|
||||
{
|
||||
return "int";
|
||||
}
|
||||
if (type == typeof(long))
|
||||
{
|
||||
return "long";
|
||||
}
|
||||
if (type == typeof(uint))
|
||||
{
|
||||
return "uint";
|
||||
}
|
||||
if (type == typeof(ulong))
|
||||
{
|
||||
return "ulong";
|
||||
}
|
||||
if (type == typeof(short))
|
||||
{
|
||||
return "short";
|
||||
}
|
||||
if (type == typeof(ushort))
|
||||
{
|
||||
return "ushort";
|
||||
}
|
||||
if (type == typeof(byte))
|
||||
{
|
||||
return "byte";
|
||||
}
|
||||
if (type == typeof(sbyte))
|
||||
{
|
||||
return "sbyte";
|
||||
}
|
||||
if (type == typeof(float))
|
||||
{
|
||||
return "float";
|
||||
}
|
||||
if (type == typeof(double))
|
||||
{
|
||||
return "double";
|
||||
}
|
||||
if (type == typeof(string))
|
||||
{
|
||||
return "string";
|
||||
}
|
||||
if (type == typeof(object))
|
||||
{
|
||||
return "object";
|
||||
}
|
||||
if (type == typeof(char))
|
||||
{
|
||||
return "char";
|
||||
}
|
||||
|
||||
// When displaying job interface type, display the interface name of Unity.Jobs namespace
|
||||
var typeName = type.IsInterface && type.Name.StartsWith("IJob") ? type.Name : type.ToString();
|
||||
return typeName.Replace("+", ".");
|
||||
}
|
||||
}
|
||||
|
||||
internal enum DisassemblyKind
|
||||
{
|
||||
Asm = 0,
|
||||
IL = 1,
|
||||
UnoptimizedIR = 2,
|
||||
OptimizedIR = 3,
|
||||
IRPassAnalysis = 4
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e0e4e7742393f00808df3e4508a7327
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,124 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.Diagnostics;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Unity.Burst.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Responsible to synchronize <see cref="BurstCompiler.Options"/> with the menu
|
||||
/// </summary>
|
||||
internal static class BurstEditorOptions
|
||||
{
|
||||
// Properties stored in SessionState (survive between domain reloads, but stays alive only during the life of the editor)
|
||||
private const string EnableBurstSafetyChecksText = "BurstSafetyChecks";
|
||||
|
||||
// Properties stored in EditorPrefs (survive between editor restart)
|
||||
private const string EnableBurstCompilationText = "BurstCompilation";
|
||||
private const string EnableBurstTimingsText = "BurstShowTimings";
|
||||
private const string EnableBurstCompileSynchronouslyText = "BurstCompileSynchronously";
|
||||
private const string EnableBurstDebugText = "BurstDebug";
|
||||
private const string ForceEnableBurstSafetyChecksText = "BurstForceSafetyChecks";
|
||||
|
||||
/// <summary>
|
||||
/// <c>true</c> if the menu options are synchronized with <see cref="BurstCompiler.Options"/>
|
||||
/// </summary>
|
||||
private static bool _isSynchronized;
|
||||
|
||||
public static void EnsureSynchronized()
|
||||
{
|
||||
GetGlobalOptions();
|
||||
}
|
||||
|
||||
public static bool EnableBurstCompilation
|
||||
{
|
||||
get => GetGlobalOptions().EnableBurstCompilation;
|
||||
set
|
||||
{
|
||||
var enabled = GetGlobalOptions().EnableBurstCompilation;
|
||||
GetGlobalOptions().EnableBurstCompilation = value;
|
||||
if (!enabled && value)
|
||||
{
|
||||
// Must be called AFTER we actually enable compilation
|
||||
BurstCompiler.TriggerUnsafeStaticMethodRecompilation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool EnableBurstSafetyChecks
|
||||
{
|
||||
get => GetGlobalOptions().EnableBurstSafetyChecks;
|
||||
set => GetGlobalOptions().EnableBurstSafetyChecks = value;
|
||||
}
|
||||
|
||||
public static bool EnableBurstCompileSynchronously
|
||||
{
|
||||
get => GetGlobalOptions().EnableBurstCompileSynchronously;
|
||||
set => GetGlobalOptions().EnableBurstCompileSynchronously = value;
|
||||
}
|
||||
|
||||
public static bool EnableBurstTimings
|
||||
{
|
||||
get => GetGlobalOptions().EnableBurstTimings;
|
||||
set => GetGlobalOptions().EnableBurstTimings = value;
|
||||
}
|
||||
|
||||
public static bool EnableBurstDebug
|
||||
{
|
||||
get => GetGlobalOptions().EnableBurstDebug;
|
||||
set => GetGlobalOptions().EnableBurstDebug = value;
|
||||
}
|
||||
|
||||
public static bool ForceEnableBurstSafetyChecks
|
||||
{
|
||||
get => GetGlobalOptions().ForceEnableBurstSafetyChecks;
|
||||
set => GetGlobalOptions().ForceEnableBurstSafetyChecks = value;
|
||||
}
|
||||
|
||||
private static BurstCompilerOptions GetGlobalOptions()
|
||||
{
|
||||
var global = BurstCompiler.Options;
|
||||
// If options are not synchronize with our global instance, setup the sync
|
||||
if (!_isSynchronized)
|
||||
{
|
||||
global.IsInitializing = true;
|
||||
|
||||
try
|
||||
{
|
||||
// Setup the synchronization
|
||||
global.EnableBurstCompilation = EditorPrefs.GetBool(EnableBurstCompilationText, true);
|
||||
global.EnableBurstCompileSynchronously = EditorPrefs.GetBool(EnableBurstCompileSynchronouslyText, false);
|
||||
global.EnableBurstTimings = EditorPrefs.GetBool(EnableBurstTimingsText, false);
|
||||
global.EnableBurstDebug = EditorPrefs.GetBool(EnableBurstDebugText, false);
|
||||
global.ForceEnableBurstSafetyChecks = EditorPrefs.GetBool(ForceEnableBurstSafetyChecksText, false);
|
||||
|
||||
// Session only properties
|
||||
global.EnableBurstSafetyChecks = SessionState.GetBool(EnableBurstSafetyChecksText, true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
global.IsInitializing = false;
|
||||
}
|
||||
|
||||
global.OptionsChanged += GlobalOnOptionsChanged;
|
||||
_isSynchronized = true;
|
||||
}
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
private static void GlobalOnOptionsChanged()
|
||||
{
|
||||
var global = BurstCompiler.Options;
|
||||
// We are not optimizing anything here, so whenever one option is set, we reset all of them
|
||||
EditorPrefs.SetBool(EnableBurstCompilationText, global.EnableBurstCompilation);
|
||||
EditorPrefs.SetBool(EnableBurstCompileSynchronouslyText, global.EnableBurstCompileSynchronously);
|
||||
EditorPrefs.SetBool(EnableBurstTimingsText, global.EnableBurstTimings);
|
||||
EditorPrefs.SetBool(EnableBurstDebugText, global.EnableBurstDebug);
|
||||
EditorPrefs.SetBool(ForceEnableBurstSafetyChecksText, global.ForceEnableBurstSafetyChecks);
|
||||
|
||||
// Session only properties
|
||||
SessionState.SetBool(EnableBurstSafetyChecksText, global.EnableBurstSafetyChecks);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d81d96b8ea1936359196f069c6e225d8
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,770 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aeb3ace3d48330a3b182da7ace6b5f65
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,644 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Jobs.LowLevel.Unsafe;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Compilation;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
[assembly: InternalsVisibleTo("Unity.Burst.Editor.Tests")]
|
||||
namespace Unity.Burst.Editor
|
||||
{
|
||||
using static BurstCompilerOptions;
|
||||
|
||||
internal static class BurstReflection
|
||||
{
|
||||
// The TypeCache API was added in 2019.2. So there are two versions of FindExecuteMethods,
|
||||
// one that uses TypeCache and one that doesn't.
|
||||
public static FindExecuteMethodsResult FindExecuteMethods(List<System.Reflection.Assembly> assemblyList, BurstReflectionAssemblyOptions options)
|
||||
{
|
||||
var methodsToCompile = new List<BurstCompileTarget>();
|
||||
var methodsToCompileSet = new HashSet<MethodInfo>();
|
||||
var logMessages = new List<LogMessage>();
|
||||
var interfaceToProducer = new Dictionary<Type, Type>();
|
||||
|
||||
var assemblySet = new HashSet<System.Reflection.Assembly>(assemblyList);
|
||||
|
||||
void AddTarget(BurstCompileTarget target)
|
||||
{
|
||||
if (target.Method.Name.EndsWith("$BurstManaged")) return;
|
||||
|
||||
// We will not try to record more than once a method in the methods to compile
|
||||
// This can happen if a job interface is inheriting from another job interface which are using in the end the same
|
||||
// job producer type
|
||||
if (!target.IsStaticMethod && !methodsToCompileSet.Add(target.Method))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.HasFlag(BurstReflectionAssemblyOptions.ExcludeTestAssemblies) &&
|
||||
target.JobType.Assembly.GetReferencedAssemblies().Any(x => IsNUnitDll(x.Name)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
methodsToCompile.Add(target);
|
||||
}
|
||||
|
||||
var staticMethodTypes = new HashSet<Type>();
|
||||
|
||||
// -------------------------------------------
|
||||
// Find job structs using TypeCache.
|
||||
// -------------------------------------------
|
||||
|
||||
var jobProducerImplementations = TypeCache.GetTypesWithAttribute<JobProducerTypeAttribute>();
|
||||
foreach (var jobProducerImplementation in jobProducerImplementations)
|
||||
{
|
||||
var attrs = jobProducerImplementation.GetCustomAttributes(typeof(JobProducerTypeAttribute), false);
|
||||
if (attrs.Length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
staticMethodTypes.Add(jobProducerImplementation);
|
||||
|
||||
var attr = (JobProducerTypeAttribute)attrs[0];
|
||||
interfaceToProducer.Add(jobProducerImplementation, attr.ProducerType);
|
||||
}
|
||||
|
||||
foreach (var jobProducerImplementation in jobProducerImplementations)
|
||||
{
|
||||
if (!jobProducerImplementation.IsInterface)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var jobTypes = TypeCache.GetTypesDerivedFrom(jobProducerImplementation);
|
||||
|
||||
foreach (var jobType in jobTypes)
|
||||
{
|
||||
if (jobType.IsGenericType || !jobType.IsValueType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ScanJobType(jobType, interfaceToProducer, logMessages, AddTarget);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------
|
||||
// Find static methods using TypeCache.
|
||||
// -------------------------------------------
|
||||
|
||||
void AddStaticMethods(TypeCache.MethodCollection methods)
|
||||
{
|
||||
foreach (var method in methods)
|
||||
{
|
||||
if (HasBurstCompileAttribute(method.DeclaringType))
|
||||
{
|
||||
staticMethodTypes.Add(method.DeclaringType);
|
||||
|
||||
// NOTE: Make sure that we don't use a value type generic definition (e.g `class Outer<T> { struct Inner { } }`)
|
||||
// We are only working on plain type or generic type instance!
|
||||
if (!method.DeclaringType.IsGenericTypeDefinition &&
|
||||
method.IsStatic &&
|
||||
!method.ContainsGenericParameters)
|
||||
{
|
||||
AddTarget(new BurstCompileTarget(method, method.DeclaringType, null, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add [BurstCompile] static methods.
|
||||
AddStaticMethods(TypeCache.GetMethodsWithAttribute<BurstCompileAttribute>());
|
||||
|
||||
// Add [TestCompiler] static methods.
|
||||
if (!options.HasFlag(BurstReflectionAssemblyOptions.ExcludeTestAssemblies))
|
||||
{
|
||||
var testCompilerAttributeType = Type.GetType("Burst.Compiler.IL.Tests.TestCompilerAttribute, Unity.Burst.Tests.UnitTests, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
|
||||
if (testCompilerAttributeType != null)
|
||||
{
|
||||
AddStaticMethods(TypeCache.GetMethodsWithAttribute(testCompilerAttributeType));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------
|
||||
// Find job types and static methods based on
|
||||
// generic instances types. These will not be
|
||||
// found by the TypeCache scanning above.
|
||||
// -------------------------------------------
|
||||
FindExecuteMethodsForGenericInstances(
|
||||
assemblySet,
|
||||
staticMethodTypes,
|
||||
interfaceToProducer,
|
||||
AddTarget,
|
||||
logMessages);
|
||||
|
||||
return new FindExecuteMethodsResult(methodsToCompile, logMessages);
|
||||
}
|
||||
|
||||
private static void ScanJobType(
|
||||
Type jobType,
|
||||
Dictionary<Type, Type> interfaceToProducer,
|
||||
List<LogMessage> logMessages,
|
||||
Action<BurstCompileTarget> addTarget)
|
||||
{
|
||||
foreach (var interfaceType in jobType.GetInterfaces())
|
||||
{
|
||||
var genericLessInterface = interfaceType;
|
||||
if (interfaceType.IsGenericType)
|
||||
{
|
||||
genericLessInterface = interfaceType.GetGenericTypeDefinition();
|
||||
}
|
||||
|
||||
if (interfaceToProducer.TryGetValue(genericLessInterface, out var foundProducer))
|
||||
{
|
||||
var genericParams = new List<Type> { jobType };
|
||||
if (interfaceType.IsGenericType)
|
||||
{
|
||||
genericParams.AddRange(interfaceType.GenericTypeArguments);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var executeType = foundProducer.MakeGenericType(genericParams.ToArray());
|
||||
var executeMethod = executeType.GetMethod("Execute", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
|
||||
if (executeMethod == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Burst reflection error. The type `{executeType}` does not contain an `Execute` method");
|
||||
}
|
||||
|
||||
addTarget(new BurstCompileTarget(executeMethod, jobType, interfaceType, false));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logMessages.Add(new LogMessage(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void FindExecuteMethodsForGenericInstances(
|
||||
HashSet<System.Reflection.Assembly> assemblyList,
|
||||
HashSet<Type> staticMethodTypes,
|
||||
Dictionary<Type, Type> interfaceToProducer,
|
||||
Action<BurstCompileTarget> addTarget,
|
||||
List<LogMessage> logMessages)
|
||||
{
|
||||
var valueTypes = new List<TypeToVisit>();
|
||||
|
||||
//Debug.Log("Filtered Assembly List: " + string.Join(", ", assemblyList.Select(assembly => assembly.GetName().Name)));
|
||||
|
||||
// Find all ways to execute job types (via producer attributes)
|
||||
var typesVisited = new HashSet<string>();
|
||||
var typesToVisit = new HashSet<string>();
|
||||
var allTypesAssembliesCollected = new HashSet<Type>();
|
||||
foreach (var assembly in assemblyList)
|
||||
{
|
||||
var types = new List<Type>();
|
||||
try
|
||||
{
|
||||
// Collect all generic type instances (excluding indirect instances)
|
||||
CollectGenericTypeInstances(
|
||||
assembly,
|
||||
x => assemblyList.Contains(x.Assembly),
|
||||
types,
|
||||
allTypesAssembliesCollected);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logMessages.Add(new LogMessage(LogType.Warning, "Unexpected exception while collecting types in assembly `" + assembly.FullName + "` Exception: " + ex));
|
||||
}
|
||||
|
||||
for (var i = 0; i < types.Count; i++)
|
||||
{
|
||||
var t = types[i];
|
||||
if (typesToVisit.Add(t.AssemblyQualifiedName))
|
||||
{
|
||||
// Because the list of types returned by CollectGenericTypeInstances does not detect nested generic classes that are not
|
||||
// used explicitly, we need to create them if a declaring type is actually used
|
||||
// so for example if we have:
|
||||
// class MyClass<T> { class MyNestedClass { } }
|
||||
// class MyDerived : MyClass<int> { }
|
||||
// The CollectGenericTypeInstances will return typically the type MyClass<int>, but will not list MyClass<int>.MyNestedClass
|
||||
// So the following code is correcting this in order to fully query the full graph of generic instance types, including indirect types
|
||||
var nestedTypes = t.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic);
|
||||
foreach (var nestedType in nestedTypes)
|
||||
{
|
||||
if (t.IsGenericType && !t.IsGenericTypeDefinition)
|
||||
{
|
||||
var parentGenericTypeArguments = t.GetGenericArguments();
|
||||
// Only create nested types that are closed generic types (full generic instance types)
|
||||
// It happens if for example the parent class is `class MClass<T> { class MyNestedGeneric<T1> {} }`
|
||||
// In that case, MyNestedGeneric<T1> is opened in the context of MClass<int>, so we don't process them
|
||||
if (nestedType.GetGenericArguments().Length == parentGenericTypeArguments.Length)
|
||||
{
|
||||
try
|
||||
{
|
||||
var instanceNestedType = nestedType.MakeGenericType(parentGenericTypeArguments);
|
||||
types.Add(instanceNestedType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var error = $"Unexpected Burst Inspector error. Invalid generic type instance. Trying to instantiate the generic type {nestedType.FullName} with the generic arguments <{string.Join(", ", parentGenericTypeArguments.Select(x => x.FullName))}> is not supported: {ex}";
|
||||
logMessages.Add(new LogMessage(LogType.Warning, error));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
types.Add(nestedType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var t in types)
|
||||
{
|
||||
// If the type has been already visited, don't try to visit it
|
||||
if (!typesVisited.Add(t.AssemblyQualifiedName) || (t.IsGenericTypeDefinition && !t.IsInterface))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// collect methods with types having a [BurstCompile] attribute
|
||||
var staticMethodDeclaringType = t;
|
||||
if (t.IsGenericType)
|
||||
{
|
||||
staticMethodDeclaringType = t.GetGenericTypeDefinition();
|
||||
}
|
||||
bool visitStaticMethods = staticMethodTypes.Contains(staticMethodDeclaringType);
|
||||
bool isValueType = false;
|
||||
|
||||
if (t.IsValueType)
|
||||
{
|
||||
// NOTE: Make sure that we don't use a value type generic definition (e.g `class Outer<T> { struct Inner { } }`)
|
||||
// We are only working on plain type or generic type instance!
|
||||
if (!t.IsGenericTypeDefinition)
|
||||
isValueType = true;
|
||||
}
|
||||
|
||||
if (isValueType || visitStaticMethods)
|
||||
{
|
||||
valueTypes.Add(new TypeToVisit(t, visitStaticMethods));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logMessages.Add(new LogMessage(LogType.Warning,
|
||||
"Unexpected exception while inspecting type `" + t +
|
||||
"` IsConstructedGenericType: " + t.IsConstructedGenericType +
|
||||
" IsGenericTypeDef: " + t.IsGenericTypeDefinition +
|
||||
" IsGenericParam: " + t.IsGenericParameter +
|
||||
" Exception: " + ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Revisit all types to find things that are compilable using the above producers.
|
||||
foreach (var typePair in valueTypes)
|
||||
{
|
||||
var type = typePair.Type;
|
||||
|
||||
// collect static [BurstCompile] methods
|
||||
if (typePair.CollectStaticMethods)
|
||||
{
|
||||
try
|
||||
{
|
||||
var methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
foreach (var method in methods)
|
||||
{
|
||||
if (HasBurstCompileAttribute(method))
|
||||
{
|
||||
addTarget(new BurstCompileTarget(method, type, null, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logMessages.Add(new LogMessage(ex));
|
||||
}
|
||||
}
|
||||
|
||||
// If the type is not a value type, we don't need to proceed with struct Jobs
|
||||
if (!type.IsValueType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ScanJobType(type, interfaceToProducer, logMessages, addTarget);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FindExecuteMethodsResult
|
||||
{
|
||||
public readonly List<BurstCompileTarget> CompileTargets;
|
||||
public readonly List<LogMessage> LogMessages;
|
||||
|
||||
public FindExecuteMethodsResult(List<BurstCompileTarget> compileTargets, List<LogMessage> logMessages)
|
||||
{
|
||||
CompileTargets = compileTargets;
|
||||
LogMessages = logMessages;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class LogMessage
|
||||
{
|
||||
public readonly LogType LogType;
|
||||
public readonly string Message;
|
||||
public readonly Exception Exception;
|
||||
|
||||
public LogMessage(LogType logType, string message)
|
||||
{
|
||||
LogType = logType;
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public LogMessage(Exception exception)
|
||||
{
|
||||
LogType = LogType.Exception;
|
||||
Exception = exception;
|
||||
}
|
||||
}
|
||||
|
||||
public enum LogType
|
||||
{
|
||||
Warning,
|
||||
Exception,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method exists solely to ensure that the static constructor has been called.
|
||||
/// </summary>
|
||||
public static void EnsureInitialized() { }
|
||||
|
||||
public static readonly List<System.Reflection.Assembly> EditorAssembliesThatCanPossiblyContainJobs;
|
||||
public static readonly List<System.Reflection.Assembly> EditorAssembliesThatCanPossiblyContainJobsExcludingTestAssemblies;
|
||||
|
||||
/// <summary>
|
||||
/// Collects (and caches) all editor assemblies - transitively.
|
||||
/// </summary>
|
||||
static BurstReflection()
|
||||
{
|
||||
EditorAssembliesThatCanPossiblyContainJobs = new List<System.Reflection.Assembly>();
|
||||
EditorAssembliesThatCanPossiblyContainJobsExcludingTestAssemblies = new List<System.Reflection.Assembly>();
|
||||
|
||||
// TODO: Not sure there is a better way to match assemblies returned by CompilationPipeline.GetAssemblies
|
||||
// with runtime assemblies contained in the AppDomain.CurrentDomain.GetAssemblies()
|
||||
|
||||
// Filter the assemblies
|
||||
var assemblyList = CompilationPipeline.GetAssemblies(AssembliesType.Editor);
|
||||
|
||||
var assemblyNames = new HashSet<string>();
|
||||
foreach (var assembly in assemblyList)
|
||||
{
|
||||
CollectAssemblyNames(assembly, assemblyNames);
|
||||
}
|
||||
|
||||
var allAssemblies = new HashSet<System.Reflection.Assembly>();
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
if (!assemblyNames.Contains(assembly.GetName().Name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
CollectAssembly(assembly, allAssemblies);
|
||||
}
|
||||
}
|
||||
|
||||
// For an assembly to contain something "interesting" when we're scanning for things to compile,
|
||||
// it needs to either:
|
||||
// (a) be one of these assemblies, or
|
||||
// (b) reference one of these assemblies
|
||||
private static readonly string[] ScanMarkerAssemblies = new[]
|
||||
{
|
||||
// Contains [BurstCompile] attribute
|
||||
"Unity.Burst",
|
||||
|
||||
// Contains [JobProducerType] attribute
|
||||
"UnityEngine.CoreModule"
|
||||
};
|
||||
|
||||
private static void CollectAssembly(System.Reflection.Assembly assembly, HashSet<System.Reflection.Assembly> collect)
|
||||
{
|
||||
if (!collect.Add(assembly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var referencedAssemblies = assembly.GetReferencedAssemblies();
|
||||
|
||||
var shouldCollectReferences = false;
|
||||
|
||||
var name = assembly.GetName().Name;
|
||||
if (ScanMarkerAssemblies.Contains(name) || referencedAssemblies.Any(x => ScanMarkerAssemblies.Contains(x.Name)))
|
||||
{
|
||||
EditorAssembliesThatCanPossiblyContainJobs.Add(assembly);
|
||||
shouldCollectReferences = true;
|
||||
|
||||
if (!assembly.GetReferencedAssemblies().Any(x => IsNUnitDll(x.Name)))
|
||||
{
|
||||
EditorAssembliesThatCanPossiblyContainJobsExcludingTestAssemblies.Add(assembly);
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldCollectReferences)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var assemblyName in referencedAssemblies)
|
||||
{
|
||||
try
|
||||
{
|
||||
CollectAssembly(System.Reflection.Assembly.Load(assemblyName), collect);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (BurstLoader.IsDebugging)
|
||||
{
|
||||
Debug.LogWarning("Could not load assembly " + assemblyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsNUnitDll(string value)
|
||||
{
|
||||
return CultureInfo.InvariantCulture.CompareInfo.IndexOf(value, "nunit.framework") >= 0;
|
||||
}
|
||||
|
||||
private static void CollectAssemblyNames(UnityEditor.Compilation.Assembly assembly, HashSet<string> collect)
|
||||
{
|
||||
if (assembly == null || assembly.name == null) return;
|
||||
|
||||
if (!collect.Add(assembly.name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var assemblyRef in assembly.assemblyReferences)
|
||||
{
|
||||
CollectAssemblyNames(assemblyRef, collect);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of concrete generic type instances used in an assembly.
|
||||
/// See remarks
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly</param>
|
||||
/// <param name="types"></param>
|
||||
/// <returns>The list of generic type instances</returns>
|
||||
/// <remarks>
|
||||
/// Note that this method fetchs only direct type instances but
|
||||
/// cannot fetch transitive generic type instances.
|
||||
/// </remarks>
|
||||
private static void CollectGenericTypeInstances(
|
||||
System.Reflection.Assembly assembly,
|
||||
Func<Type, bool> typeFilter,
|
||||
List<Type> types,
|
||||
HashSet<Type> visited)
|
||||
{
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// WARNING: THIS CODE HAS TO BE MAINTAINED IN SYNC WITH BclApp.cs
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
// From: https://gist.github.com/xoofx/710aaf86e0e8c81649d1261b1ef9590e
|
||||
if (assembly == null) throw new ArgumentNullException(nameof(assembly));
|
||||
const int mdMaxCount = 1 << 24;
|
||||
foreach (var module in assembly.Modules)
|
||||
{
|
||||
for (int i = 1; i < mdMaxCount; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Token base id for TypeSpec
|
||||
const int mdTypeSpec = 0x1B000000;
|
||||
var type = module.ResolveType(mdTypeSpec | i);
|
||||
CollectGenericTypeInstances(type, types, visited, typeFilter);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// Can happen on ResolveType on certain generic types, so we continue
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i < mdMaxCount; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Token base id for MethodSpec
|
||||
const int mdMethodSpec = 0x2B000000;
|
||||
var method = module.ResolveMethod(mdMethodSpec | i);
|
||||
var genericArgs = method.GetGenericArguments();
|
||||
foreach (var genArgType in genericArgs)
|
||||
{
|
||||
CollectGenericTypeInstances(genArgType, types, visited, typeFilter);
|
||||
}
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// Can happen on ResolveType on certain generic types, so we continue
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i < mdMaxCount; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Token base id for Field
|
||||
const int mdField = 0x04000000;
|
||||
var field = module.ResolveField(mdField | i);
|
||||
CollectGenericTypeInstances(field.FieldType, types, visited, typeFilter);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// Can happen on ResolveType on certain generic types, so we continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scan for types used in constructor arguments to assembly-level attributes,
|
||||
// such as [RegisterGenericJobType(typeof(...))].
|
||||
foreach (var customAttribute in assembly.CustomAttributes)
|
||||
{
|
||||
foreach (var argument in customAttribute.ConstructorArguments)
|
||||
{
|
||||
if (argument.ArgumentType == typeof(Type))
|
||||
{
|
||||
CollectGenericTypeInstances((Type)argument.Value, types, visited, typeFilter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void CollectGenericTypeInstances(
|
||||
Type type,
|
||||
List<Type> types,
|
||||
HashSet<Type> visited,
|
||||
Func<Type, bool> typeFilter)
|
||||
{
|
||||
if (type.IsPrimitive) return;
|
||||
if (!visited.Add(type)) return;
|
||||
|
||||
// Add only concrete types
|
||||
if (type.IsConstructedGenericType && !type.ContainsGenericParameters && typeFilter(type))
|
||||
{
|
||||
types.Add(type);
|
||||
}
|
||||
|
||||
// Collect recursively generic type arguments
|
||||
var genericTypeArguments = type.GenericTypeArguments;
|
||||
foreach (var genericTypeArgument in genericTypeArguments)
|
||||
{
|
||||
if (!genericTypeArgument.IsPrimitive)
|
||||
{
|
||||
CollectGenericTypeInstances(genericTypeArgument, types, visited, typeFilter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{Type} (static methods: {CollectStaticMethods})")]
|
||||
private struct TypeToVisit
|
||||
{
|
||||
public TypeToVisit(Type type, bool collectStaticMethods)
|
||||
{
|
||||
Type = type;
|
||||
CollectStaticMethods = collectStaticMethods;
|
||||
}
|
||||
|
||||
public readonly Type Type;
|
||||
|
||||
public readonly bool CollectStaticMethods;
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum BurstReflectionAssemblyOptions
|
||||
{
|
||||
None = 0,
|
||||
ExcludeTestAssemblies = 1,
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 661ba2a1bdac339cb55c6a43b9eaa0ee
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"reference": "Unity.Burst"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 268a64be2367b3248a24f317d458861a
|
||||
AssemblyDefinitionReferenceImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user