You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
873 lines
36 KiB
873 lines
36 KiB
using System; |
|
using System.Diagnostics; |
|
using System.Reflection; |
|
using System.Runtime.InteropServices; |
|
#if !UNITY_DOTSPLAYER && !NET_DOTS |
|
using System.Collections.Generic; |
|
using UnityEngine.Scripting; |
|
using System.Linq; |
|
#endif |
|
using System.Text; |
|
|
|
namespace Unity.Burst |
|
{ |
|
/// <summary> |
|
/// The burst compiler runtime frontend. |
|
/// </summary> |
|
/// |
|
public static class BurstCompiler |
|
{ |
|
/// <summary> |
|
/// Check if the LoadAdditionalLibrary API is supported by the current version of Unity |
|
/// </summary> |
|
/// <returns>True if the LoadAdditionalLibrary API can be used by the current version of Unity</returns> |
|
public static bool IsLoadAdditionalLibrarySupported() |
|
{ |
|
return IsApiAvailable("LoadBurstLibrary"); |
|
} |
|
|
|
#if !UNITY_DOTSPLAYER && !NET_DOTS |
|
#if UNITY_EDITOR |
|
static unsafe BurstCompiler() |
|
{ |
|
// Store pointers to Log and Compile callback methods. |
|
// For more info about why we need to do this, see comments in CallbackStubManager. |
|
string GetFunctionPointer<TDelegate>(TDelegate callback) |
|
{ |
|
GCHandle.Alloc(callback); // Ensure delegate is never garbage-collected. |
|
var callbackFunctionPointer = Marshal.GetFunctionPointerForDelegate(callback); |
|
return "0x" + callbackFunctionPointer.ToInt64().ToString("X16"); |
|
} |
|
|
|
EagerCompileLogCallbackFunctionPointer = GetFunctionPointer<LogCallbackDelegate>(EagerCompileLogCallback); |
|
ManagedResolverFunctionPointer = GetFunctionPointer<ManagedFnPtrResolverDelegate>(ManagedResolverFunction); |
|
#if UNITY_2020_1_OR_NEWER |
|
ProgressCallbackFunctionPointer = GetFunctionPointer<ProgressCallbackDelegate>(ProgressCallback); |
|
ProfileBeginCallbackFunctionPointer = GetFunctionPointer<ProfileBeginCallbackDelegate>(ProfileBeginCallback); |
|
ProfileEndCallbackFunctionPointer = GetFunctionPointer<ProfileEndCallbackDelegate>(ProfileEndCallback); |
|
#else |
|
ProgressCallbackFunctionPointer = ""; |
|
#endif |
|
} |
|
#endif |
|
|
|
private class CommandBuilder |
|
{ |
|
private StringBuilder _builder; |
|
private bool _hasArgs; |
|
|
|
public CommandBuilder() |
|
{ |
|
_builder = new StringBuilder(); |
|
_hasArgs = false; |
|
} |
|
|
|
public CommandBuilder Begin(string cmd) |
|
{ |
|
_builder.Clear(); |
|
_hasArgs = false; |
|
_builder.Append(cmd); |
|
return this; |
|
} |
|
|
|
public CommandBuilder With(string arg) |
|
{ |
|
if (!_hasArgs) _builder.Append(' '); |
|
_hasArgs = true; |
|
_builder.Append(arg); |
|
return this; |
|
} |
|
|
|
public CommandBuilder With(IntPtr arg) |
|
{ |
|
if (!_hasArgs) _builder.Append(' '); |
|
_hasArgs = true; |
|
_builder.AppendFormat("0x{0:X16}", arg.ToInt64()); |
|
return this; |
|
} |
|
|
|
public CommandBuilder And(char sep = '|') |
|
{ |
|
_builder.Append(sep); |
|
return this; |
|
} |
|
|
|
public string SendToCompiler() |
|
{ |
|
return SendRawCommandToCompiler(_builder.ToString()); |
|
} |
|
} |
|
|
|
[ThreadStatic] |
|
private static CommandBuilder _cmdBuilder; |
|
|
|
private static CommandBuilder BeginCompilerCommand(string cmd) |
|
{ |
|
if (_cmdBuilder == null) |
|
{ |
|
_cmdBuilder = new CommandBuilder(); |
|
} |
|
|
|
return _cmdBuilder.Begin(cmd); |
|
} |
|
|
|
#if BURST_INTERNAL |
|
[ThreadStatic] |
|
public static Func<object, IntPtr> InternalCompiler; |
|
#endif |
|
|
|
/// <summary> |
|
/// Internal variable setup by BurstCompilerOptions. |
|
/// </summary> |
|
#if BURST_INTERNAL |
|
|
|
[ThreadStatic] // As we are changing this boolean via BurstCompilerOptions in btests and we are running multithread tests |
|
// we would change a global and it would generate random errors, so specifically for btests, we are using a TLS. |
|
public |
|
#else |
|
internal |
|
#endif |
|
static bool _IsEnabled; |
|
|
|
/// <summary> |
|
/// Gets a value indicating whether Burst is enabled. |
|
/// </summary> |
|
#if UNITY_EDITOR || BURST_INTERNAL |
|
public static bool IsEnabled => _IsEnabled; |
|
#else |
|
public static bool IsEnabled => _IsEnabled && BurstCompilerHelper.IsBurstGenerated; |
|
#endif |
|
|
|
/// <summary> |
|
/// Gets the global options for the burst compiler. |
|
/// </summary> |
|
public static readonly BurstCompilerOptions Options = new BurstCompilerOptions(true); |
|
|
|
#if UNITY_2019_3_OR_NEWER |
|
/// <summary> |
|
/// Sets the execution mode for all jobs spawned from now on. |
|
/// </summary> |
|
/// <param name="mode">Specifiy the required execution mode</param> |
|
public static void SetExecutionMode(BurstExecutionEnvironment mode) |
|
{ |
|
Burst.LowLevel.BurstCompilerService.SetCurrentExecutionMode((uint)mode); |
|
} |
|
/// <summary> |
|
/// Retrieve the current execution mode that is configured. |
|
/// </summary> |
|
/// <returns>Currently configured execution mode</returns> |
|
public static BurstExecutionEnvironment GetExecutionMode() |
|
{ |
|
return (BurstExecutionEnvironment)Burst.LowLevel.BurstCompilerService.GetCurrentExecutionMode(); |
|
} |
|
#endif |
|
|
|
/// <summary> |
|
/// Compile the following delegate with burst and return a new delegate. |
|
/// </summary> |
|
/// <typeparam name="T"></typeparam> |
|
/// <param name="delegateMethod"></param> |
|
/// <returns></returns> |
|
/// <remarks>NOT AVAILABLE, unsafe to use</remarks> |
|
internal static unsafe T CompileDelegate<T>(T delegateMethod) where T : class |
|
{ |
|
// We have added support for runtime CompileDelegate in 2018.2+ |
|
void* function = Compile(delegateMethod, false); |
|
object res = System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer((IntPtr)function, delegateMethod.GetType()); |
|
return (T)res; |
|
} |
|
|
|
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] |
|
private static void VerifyDelegateIsNotMulticast<T>(T delegateMethod) where T : class |
|
{ |
|
var delegateKind = delegateMethod as Delegate; |
|
if (delegateKind.GetInvocationList().Length > 1) |
|
{ |
|
throw new InvalidOperationException($"Burst does not support multicast delegates, please use a regular delegate for `{delegateMethod}'"); |
|
} |
|
} |
|
|
|
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] |
|
private static void VerifyDelegateHasCorrectUnmanagedFunctionPointerAttribute<T>(T delegateMethod) where T : class |
|
{ |
|
var attrib = delegateMethod.GetType().GetCustomAttribute<System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute>(); |
|
if (attrib == null || attrib.CallingConvention != CallingConvention.Cdecl) |
|
{ |
|
#if !BURST_INTERNAL |
|
UnityEngine.Debug.LogWarning($"The delegate type {delegateMethod.GetType().FullName} should be decorated with [UnmanagedFunctionPointer(CallingConvention.Cdecl)] to ensure runtime interoperabilty between managed code and Burst-compiled code."); |
|
#endif |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// DO NOT USE - deprecated. |
|
/// </summary> |
|
/// <param name="burstMethodHandle">The Burst method to compile.</param> |
|
/// <param name="managedMethodHandle">The fallback managed method to use.</param> |
|
/// <param name="delegateTypeHandle">The type of the delegate used to execute these methods.</param> |
|
/// <returns>Nothing</returns> |
|
[Obsolete("This method will be removed in a future version of Burst")] |
|
public static unsafe IntPtr CompileILPPMethod(RuntimeMethodHandle burstMethodHandle, RuntimeMethodHandle managedMethodHandle, RuntimeTypeHandle delegateTypeHandle) |
|
{ |
|
throw new NotImplementedException(); |
|
} |
|
|
|
/// <summary> |
|
/// Compile an IL Post-Processed method. |
|
/// </summary> |
|
/// <param name="burstMethodHandle">The Burst method to compile.</param> |
|
/// <returns>A token that must be passed to <see cref="GetILPPMethodFunctionPointer2"/> to get an actual executable function pointer.</returns> |
|
public static unsafe IntPtr CompileILPPMethod2(RuntimeMethodHandle burstMethodHandle) |
|
{ |
|
if (burstMethodHandle.Value == IntPtr.Zero) |
|
{ |
|
throw new ArgumentNullException(nameof(burstMethodHandle)); |
|
} |
|
|
|
OnCompileILPPMethod2?.Invoke(); |
|
|
|
var burstMethod = (MethodInfo)MethodBase.GetMethodFromHandle(burstMethodHandle); |
|
|
|
return (IntPtr)Compile(new FakeDelegate(burstMethod), burstMethod, isFunctionPointer: true, isILPostProcessing: true); |
|
} |
|
|
|
internal static Action OnCompileILPPMethod2; |
|
|
|
/// <summary> |
|
/// DO NOT USE - deprecated. |
|
/// </summary> |
|
/// <param name="ilppMethod">The result of a previous call to <see cref="CompileILPPMethod"/>.</param> |
|
/// <returns>Nothing.</returns> |
|
[Obsolete("This method will be removed in a future version of Burst")] |
|
public static unsafe void* GetILPPMethodFunctionPointer(IntPtr ilppMethod) |
|
{ |
|
throw new NotImplementedException(); |
|
} |
|
|
|
/// <summary> |
|
/// For a previous call to <see cref="CompileILPPMethod2"/>, get the actual executable function pointer. |
|
/// </summary> |
|
/// <param name="ilppMethod">The result of a previous call to <see cref="CompileILPPMethod"/>.</param> |
|
/// <param name="managedMethodHandle">The fallback managed method to use.</param> |
|
/// <param name="delegateTypeHandle">The type of the delegate used to execute these methods.</param> |
|
/// <returns>A pointer into an executable region, for running the function pointer.</returns> |
|
public static unsafe void* GetILPPMethodFunctionPointer2(IntPtr ilppMethod, RuntimeMethodHandle managedMethodHandle, RuntimeTypeHandle delegateTypeHandle) |
|
{ |
|
if (ilppMethod == IntPtr.Zero) |
|
{ |
|
throw new ArgumentNullException(nameof(ilppMethod)); |
|
} |
|
|
|
if (managedMethodHandle.Value == IntPtr.Zero) |
|
{ |
|
throw new ArgumentNullException(nameof(managedMethodHandle)); |
|
} |
|
|
|
if (delegateTypeHandle.Value == IntPtr.Zero) |
|
{ |
|
throw new ArgumentNullException(nameof(delegateTypeHandle)); |
|
} |
|
|
|
// If we are in the editor, we need to route a command to the compiler to start compiling the deferred ILPP compilation. |
|
// Otherwise if we're in Burst's internal testing, or in a player build, we already actually have the actual executable |
|
// pointer address, and we just return that. |
|
#if UNITY_EDITOR |
|
var managedMethod = (MethodInfo)MethodBase.GetMethodFromHandle(managedMethodHandle); |
|
var delegateType = Type.GetTypeFromHandle(delegateTypeHandle); |
|
var managedFallbackDelegate = Delegate.CreateDelegate(delegateType, managedMethod); |
|
|
|
var handle = GCHandle.Alloc(managedFallbackDelegate); |
|
|
|
var result = |
|
BeginCompilerCommand(BurstCompilerOptions.CompilerCommandILPPCompilation) |
|
.With(ilppMethod).And() |
|
.With(ManagedResolverFunctionPointer).And() |
|
.With(GCHandle.ToIntPtr(handle)) |
|
.SendToCompiler(); |
|
|
|
return new IntPtr(Convert.ToInt64(result, 16)).ToPointer(); |
|
#else |
|
return ilppMethod.ToPointer(); |
|
#endif |
|
} |
|
|
|
/// <summary> |
|
/// DO NOT USE - deprecated. |
|
/// </summary> |
|
/// <param name="handle">A runtime method handle.</param> |
|
/// <returns>Nothing.</returns> |
|
[Obsolete("This method will be removed in a future version of Burst")] |
|
public static unsafe void* CompileUnsafeStaticMethod(RuntimeMethodHandle handle) |
|
{ |
|
throw new NotImplementedException(); |
|
} |
|
|
|
/// <summary> |
|
/// Compile the following delegate into a function pointer with burst, invokable from a Burst Job or from regular C#. |
|
/// </summary> |
|
/// <typeparam name="T">Type of the delegate of the function pointer</typeparam> |
|
/// <param name="delegateMethod">The delegate to compile</param> |
|
/// <returns>A function pointer invokable from a Burst Job or from regular C#</returns> |
|
public static unsafe FunctionPointer<T> CompileFunctionPointer<T>(T delegateMethod) where T : class |
|
{ |
|
VerifyDelegateIsNotMulticast<T>(delegateMethod); |
|
VerifyDelegateHasCorrectUnmanagedFunctionPointerAttribute<T>(delegateMethod); |
|
// We have added support for runtime CompileDelegate in 2018.2+ |
|
void* function = Compile(delegateMethod, true); |
|
return new FunctionPointer<T>(new IntPtr(function)); |
|
} |
|
|
|
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] |
|
internal class StaticTypeReinitAttribute : Attribute |
|
{ |
|
public readonly Type reinitType; |
|
|
|
public StaticTypeReinitAttribute(Type toReinit) |
|
{ |
|
reinitType = toReinit; |
|
} |
|
} |
|
|
|
private static unsafe void* Compile(object delegateObj, bool isFunctionPointer) |
|
{ |
|
if (!(delegateObj is Delegate)) throw new ArgumentException("object instance must be a System.Delegate", nameof(delegateObj)); |
|
var delegateMethod = (Delegate)delegateObj; |
|
return Compile(delegateMethod, delegateMethod.Method, isFunctionPointer, false); |
|
} |
|
|
|
private static unsafe void* Compile(object delegateObj, MethodInfo methodInfo, bool isFunctionPointer, bool isILPostProcessing) |
|
{ |
|
if (delegateObj == null) throw new ArgumentNullException(nameof(delegateObj)); |
|
|
|
if (delegateObj.GetType().IsGenericType) |
|
{ |
|
throw new InvalidOperationException($"The delegate type `{delegateObj.GetType()}` must be a non-generic type"); |
|
} |
|
if (!methodInfo.IsStatic) |
|
{ |
|
throw new InvalidOperationException($"The method `{methodInfo}` must be static. Instance methods are not supported"); |
|
} |
|
if (methodInfo.IsGenericMethod) |
|
{ |
|
throw new InvalidOperationException($"The method `{methodInfo}` must be a non-generic method"); |
|
} |
|
|
|
#if ENABLE_IL2CPP |
|
if (isFunctionPointer && !isILPostProcessing && |
|
methodInfo.GetCustomAttributes().All(s => s.GetType().Name != "MonoPInvokeCallbackAttribute")) |
|
{ |
|
UnityEngine.Debug.Log($"The method `{methodInfo}` must have `MonoPInvokeCallback` attribute to be compatible with IL2CPP!"); |
|
} |
|
#endif |
|
|
|
void* function; |
|
|
|
#if BURST_INTERNAL |
|
// Internally in Burst tests, we callback the C# method instead |
|
function = (void*)InternalCompiler(delegateObj); |
|
#else |
|
|
|
Delegate managedFallbackDelegateMethod = null; |
|
|
|
if (!isILPostProcessing) |
|
{ |
|
managedFallbackDelegateMethod = delegateObj as Delegate; |
|
} |
|
|
|
var delegateMethod = delegateObj as Delegate; |
|
|
|
#if UNITY_EDITOR |
|
string defaultOptions; |
|
|
|
// In case Burst is disabled entirely from the command line |
|
if (BurstCompilerOptions.ForceDisableBurstCompilation) |
|
{ |
|
if (isILPostProcessing) |
|
{ |
|
return null; |
|
} |
|
else |
|
{ |
|
GCHandle.Alloc(managedFallbackDelegateMethod); |
|
function = (void*)Marshal.GetFunctionPointerForDelegate(managedFallbackDelegateMethod); |
|
return function; |
|
} |
|
} |
|
|
|
if (isILPostProcessing) |
|
{ |
|
defaultOptions = "--" + BurstCompilerOptions.OptionJitIsForFunctionPointer + "\n"; |
|
} |
|
else if (isFunctionPointer) |
|
{ |
|
defaultOptions = "--" + BurstCompilerOptions.OptionJitIsForFunctionPointer + "\n"; |
|
// Make sure that the delegate will never be collected |
|
var delHandle = GCHandle.Alloc(managedFallbackDelegateMethod); |
|
defaultOptions += "--" + BurstCompilerOptions.OptionJitManagedDelegateHandle + "0x" + ManagedResolverFunctionPointer + "|" + "0x" + GCHandle.ToIntPtr(delHandle).ToInt64().ToString("X16"); |
|
} |
|
else |
|
{ |
|
defaultOptions = "--" + BurstCompilerOptions.OptionJitEnableSynchronousCompilation; |
|
} |
|
|
|
string extraOptions; |
|
// The attribute is directly on the method, so we recover the underlying method here |
|
if (Options.TryGetOptions(methodInfo, true, out extraOptions, isForILPostProcessing: isILPostProcessing)) |
|
{ |
|
if (!string.IsNullOrWhiteSpace(extraOptions)) |
|
{ |
|
defaultOptions += "\n" + extraOptions; |
|
} |
|
|
|
var delegateMethodId = Unity.Burst.LowLevel.BurstCompilerService.CompileAsyncDelegateMethod(delegateObj, defaultOptions); |
|
function = Unity.Burst.LowLevel.BurstCompilerService.GetAsyncCompiledAsyncDelegateMethod(delegateMethodId); |
|
} |
|
#else |
|
// The attribute is directly on the method, so we recover the underlying method here |
|
if (BurstCompilerOptions.HasBurstCompileAttribute(methodInfo)) |
|
{ |
|
if (Options.EnableBurstCompilation && BurstCompilerHelper.IsBurstGenerated) |
|
{ |
|
var delegateMethodId = Unity.Burst.LowLevel.BurstCompilerService.CompileAsyncDelegateMethod(delegateObj, string.Empty); |
|
function = Unity.Burst.LowLevel.BurstCompilerService.GetAsyncCompiledAsyncDelegateMethod(delegateMethodId); |
|
} |
|
else |
|
{ |
|
// If this is for direct-call, and we're in a player, with Burst disabled, then we should return null, |
|
// since we don't actually have a managedFallbackDelegateMethod at this point. |
|
if (isILPostProcessing) |
|
{ |
|
return null; |
|
} |
|
|
|
// Make sure that the delegate will never be collected |
|
GCHandle.Alloc(managedFallbackDelegateMethod); |
|
// If we are in a standalone player, and burst is disabled and we are actually |
|
// trying to load a function pointer, in that case we need to support it |
|
// so we are then going to use the managed function directly |
|
// NOTE: When running under IL2CPP, this could lead to a `System.NotSupportedException : To marshal a managed method, please add an attribute named 'MonoPInvokeCallback' to the method definition.` |
|
// so in that case, the method needs to have `MonoPInvokeCallback` |
|
// but that's a requirement for IL2CPP, not an issue with burst |
|
function = (void*)Marshal.GetFunctionPointerForDelegate(managedFallbackDelegateMethod); |
|
} |
|
} |
|
#endif |
|
else |
|
{ |
|
throw new InvalidOperationException($"Burst cannot compile the function pointer `{methodInfo}` because the `[BurstCompile]` attribute is missing"); |
|
} |
|
#endif |
|
// Should not happen but in that case, we are still trying to generated an error |
|
// It can be null if we are trying to compile a function in a standalone player |
|
// and the function was not compiled. In that case, we need to output an error |
|
if (function == null) |
|
{ |
|
throw new InvalidOperationException($"Burst failed to compile the function pointer `{methodInfo}`"); |
|
} |
|
|
|
// When burst compilation is disabled, we are still returning a valid stub function pointer (the a pointer to the managed function) |
|
// so that CompileFunctionPointer actually returns a delegate in all cases |
|
return function; |
|
} |
|
|
|
/// <summary> |
|
/// Lets the compiler service know we are shutting down, called by the event on OnDomainUnload, if EditorApplication.quitting was called |
|
/// </summary> |
|
internal static void Shutdown() |
|
{ |
|
#if UNITY_EDITOR |
|
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandShutdown); |
|
#endif |
|
} |
|
|
|
#if UNITY_EDITOR |
|
internal static void SetDefaultOptions() |
|
{ |
|
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandSetDefaultOptions, Options.GetOptions(true)); |
|
} |
|
#endif |
|
|
|
#if UNITY_EDITOR |
|
// We need this to be queried each domain reload in a static constructor so that it is called on the main thread only! |
|
internal static readonly bool IsScriptDebugInfoEnabled = UnityEditor.Compilation.CompilationPipeline.IsScriptDebugInfoEnabled(); |
|
|
|
private sealed class DomainReloadStateSingleton : UnityEditor.ScriptableSingleton<DomainReloadStateSingleton> |
|
{ |
|
public bool AlreadyLoaded = false; |
|
public bool IsScriptDebugInfoEnabled = false; |
|
} |
|
|
|
internal static void DomainReload() |
|
{ |
|
const string parameterSeparator = "***"; |
|
const string assemblySeparator = "```"; |
|
|
|
var isScriptDebugInfoEnabled = IsScriptDebugInfoEnabled; |
|
|
|
var cmdBuilder = |
|
BeginCompilerCommand(BurstCompilerOptions.CompilerCommandDomainReload) |
|
.With(ProgressCallbackFunctionPointer) |
|
.With(parameterSeparator) |
|
.With(EagerCompileLogCallbackFunctionPointer) |
|
.With(parameterSeparator) |
|
.With(isScriptDebugInfoEnabled ? "Debug" : "Release") |
|
.With(parameterSeparator); |
|
|
|
// We need to send the list of assemblies if |
|
// (a) we have never done that before in this Editor instance, or |
|
// (b) we have done it before, but now the scripting code optimization mode has changed |
|
// from Debug to Release or vice-versa. |
|
// This is because these are the two cases in which CompilerClient will be |
|
// destroyed and recreated. |
|
if (!DomainReloadStateSingleton.instance.AlreadyLoaded || |
|
DomainReloadStateSingleton.instance.IsScriptDebugInfoEnabled != isScriptDebugInfoEnabled) |
|
{ |
|
// Gather list of assemblies to compile (only actually used at Editor startup) |
|
var assemblyNames = UnityEditor.Compilation.CompilationPipeline |
|
.GetAssemblies(UnityEditor.Compilation.AssembliesType.Editor) |
|
.Select(x => x.name); |
|
|
|
foreach (var assemblyName in assemblyNames) |
|
{ |
|
cmdBuilder.With(assemblyName) |
|
.With(assemblySeparator); |
|
} |
|
|
|
DomainReloadStateSingleton.instance.AlreadyLoaded = true; |
|
DomainReloadStateSingleton.instance.IsScriptDebugInfoEnabled = IsScriptDebugInfoEnabled; |
|
} |
|
|
|
cmdBuilder.SendToCompiler(); |
|
} |
|
|
|
internal static string VersionNotify(string version) |
|
{ |
|
return SendCommandToCompiler(BurstCompilerOptions.CompilerCommandVersionNotification, version); |
|
} |
|
#endif |
|
|
|
/// <summary> |
|
/// Cancel any compilation being processed by the JIT Compiler in the background. |
|
/// </summary> |
|
internal static void Cancel() |
|
{ |
|
#if UNITY_EDITOR |
|
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandCancel); |
|
#endif |
|
} |
|
|
|
/// <summary> |
|
/// Check if there is any job pending related to the last compilation ID. |
|
/// </summary> |
|
internal static bool IsCurrentCompilationDone() |
|
{ |
|
#if UNITY_EDITOR |
|
return SendCommandToCompiler(BurstCompilerOptions.CompilerCommandIsCurrentCompilationDone) == "True"; |
|
#else |
|
return true; |
|
#endif |
|
} |
|
|
|
internal static void Enable() |
|
{ |
|
#if UNITY_EDITOR |
|
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandEnableCompiler); |
|
#endif |
|
} |
|
|
|
internal static void Disable() |
|
{ |
|
#if UNITY_EDITOR |
|
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandDisableCompiler); |
|
#endif |
|
} |
|
|
|
internal static bool IsHostEditorArm() |
|
{ |
|
#if UNITY_EDITOR |
|
return SendCommandToCompiler(BurstCompilerOptions.CompilerCommandIsArmTestEnv)=="true"; |
|
#else |
|
return false; |
|
#endif |
|
} |
|
|
|
internal static void TriggerUnsafeStaticMethodRecompilation() |
|
{ |
|
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) |
|
{ |
|
var reinitAttributes = asm.GetCustomAttributes().Where( |
|
x => x.GetType().FullName == "Unity.Burst.BurstCompiler+StaticTypeReinitAttribute" |
|
); |
|
foreach (var attribute in reinitAttributes) |
|
{ |
|
var ourAttribute = attribute as StaticTypeReinitAttribute; |
|
var type = ourAttribute.reinitType; |
|
var method = type.GetMethod("Constructor",BindingFlags.Static|BindingFlags.Public); |
|
method.Invoke(null, new object[] { }); |
|
} |
|
} |
|
} |
|
|
|
internal static void TriggerRecompilation() |
|
{ |
|
#if UNITY_EDITOR |
|
SetDefaultOptions(); |
|
|
|
// This is done separately from CompilerCommandTriggerRecompilation below, |
|
// because CompilerCommandTriggerRecompilation will cause all jobs to re-request |
|
// their function pointers from Burst, and we need to have actually triggered |
|
// compilation by that point. |
|
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandTriggerSetupRecompilation); |
|
|
|
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandTriggerRecompilation, Options.GetOptions(true)); |
|
#endif |
|
} |
|
|
|
internal static void UnloadAdditionalLibraries() |
|
{ |
|
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandUnloadBurstNatives); |
|
} |
|
|
|
internal static void InitialiseDebuggerHooks() |
|
{ |
|
if (IsApiAvailable("BurstManagedDebuggerPluginV1")) |
|
{ |
|
SendCommandToCompiler(SendCommandToCompiler(BurstCompilerOptions.CompilerCommandRequestInitialiseDebuggerCommmand)); |
|
} |
|
} |
|
|
|
internal static bool IsApiAvailable(string apiName) |
|
{ |
|
return SendCommandToCompiler(BurstCompilerOptions.CompilerCommandIsNativeApiAvailable, apiName) == "True"; |
|
} |
|
|
|
internal static int RequestSetProtocolVersion(int version) |
|
{ |
|
// Ask editor for the maximum version of the protocol we support, then inform the rest of the systems the negotiated version |
|
var editorVersion = SendCommandToCompiler(BurstCompilerOptions.CompilerCommandRequestSetProtocolVersionEditor, $"{version}"); |
|
if (string.IsNullOrEmpty(editorVersion) || !int.TryParse(editorVersion, out var result)) |
|
{ |
|
result=0; |
|
} |
|
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandSetProtocolVersionBurst, $"{result}"); |
|
return result; |
|
} |
|
|
|
|
|
|
|
#if UNITY_EDITOR |
|
private unsafe delegate void LogCallbackDelegate(void* userData, int logType, byte* message, byte* fileName, int lineNumber); |
|
|
|
private static unsafe void EagerCompileLogCallback(void* userData, int logType, byte* message, byte* fileName, int lineNumber) |
|
{ |
|
if (EagerCompilationLoggingEnabled) |
|
{ |
|
BurstRuntime.Log(message, logType, fileName, lineNumber); |
|
} |
|
} |
|
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|
private delegate IntPtr ManagedFnPtrResolverDelegate(IntPtr handleVal); |
|
|
|
private static IntPtr ManagedResolverFunction(IntPtr handleVal) |
|
{ |
|
var delegateObj = GCHandle.FromIntPtr(handleVal).Target; |
|
var fnptr = Marshal.GetFunctionPointerForDelegate(delegateObj); |
|
return fnptr; |
|
} |
|
|
|
internal static bool EagerCompilationLoggingEnabled = false; |
|
|
|
private static readonly string EagerCompileLogCallbackFunctionPointer; |
|
private static readonly string ManagedResolverFunctionPointer; |
|
#endif |
|
|
|
internal static void NotifyCompilationStarted() |
|
{ |
|
#if UNITY_EDITOR |
|
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandNotifyCompilationStarted); |
|
#endif |
|
} |
|
|
|
internal static void NotifyAssemblyCompilationNotRequired(string assemblyName) |
|
{ |
|
#if UNITY_EDITOR |
|
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandNotifyAssemblyCompilationNotRequired, assemblyName); |
|
#endif |
|
} |
|
|
|
internal static void NotifyAssemblyCompilationFinished(string assemblyName) |
|
{ |
|
#if UNITY_EDITOR |
|
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandNotifyAssemblyCompilationFinished, assemblyName); |
|
#endif |
|
} |
|
|
|
internal static void NotifyCompilationFinished() |
|
{ |
|
#if UNITY_EDITOR |
|
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandNotifyCompilationFinished); |
|
#endif |
|
} |
|
|
|
internal static string AotCompilation(string[] assemblyFolders, string[] assemblyRoots, string options) |
|
{ |
|
var result = "failed"; |
|
#if UNITY_EDITOR |
|
result = SendCommandToCompiler( |
|
BurstCompilerOptions.CompilerCommandAotCompilation, |
|
BurstCompilerOptions.SerialiseCompilationOptionsSafe(assemblyRoots, assemblyFolders, options)); |
|
#endif |
|
return result; |
|
} |
|
|
|
#if UNITY_EDITOR |
|
private static readonly string ProgressCallbackFunctionPointer; |
|
|
|
#if UNITY_2020_1_OR_NEWER |
|
private delegate void ProgressCallbackDelegate(int current, int total); |
|
|
|
private static void ProgressCallback(int current, int total) |
|
{ |
|
OnProgress?.Invoke(current, total); |
|
} |
|
|
|
internal static event Action<int, int> OnProgress; |
|
#endif |
|
#endif |
|
|
|
internal static void SetProfilerCallbacks() |
|
{ |
|
#if UNITY_EDITOR && UNITY_2020_1_OR_NEWER |
|
BeginCompilerCommand(BurstCompilerOptions.CompilerCommandSetProfileCallbacks) |
|
.With(ProfileBeginCallbackFunctionPointer).And(';') |
|
.With(ProfileEndCallbackFunctionPointer) |
|
.SendToCompiler(); |
|
#endif |
|
} |
|
|
|
#if UNITY_EDITOR && UNITY_2020_1_OR_NEWER |
|
internal delegate void ProfileBeginCallbackDelegate(string markerName, string metadataName, string metadataValue); |
|
internal delegate void ProfileEndCallbackDelegate(string markerName); |
|
|
|
private static readonly string ProfileBeginCallbackFunctionPointer; |
|
private static readonly string ProfileEndCallbackFunctionPointer; |
|
|
|
private static void ProfileBeginCallback(string markerName, string metadataName, string metadataValue) => OnProfileBegin?.Invoke(markerName, metadataName, metadataValue); |
|
private static void ProfileEndCallback(string markerName) => OnProfileEnd?.Invoke(markerName); |
|
|
|
internal static event ProfileBeginCallbackDelegate OnProfileBegin; |
|
internal static event ProfileEndCallbackDelegate OnProfileEnd; |
|
#endif |
|
|
|
|
|
private static string SendRawCommandToCompiler(string command) |
|
{ |
|
var results = Unity.Burst.LowLevel.BurstCompilerService.GetDisassembly(DummyMethodInfo, command); |
|
if (!string.IsNullOrEmpty(results)) |
|
return results.TrimStart('\n'); |
|
return ""; |
|
} |
|
|
|
private static string SendCommandToCompiler(string commandName, string commandArgs = null) |
|
{ |
|
if (commandName == null) throw new ArgumentNullException(nameof(commandName)); |
|
|
|
if (commandArgs == null) |
|
{ |
|
// If there are no arguments then there's no reason to go through the builder |
|
return SendRawCommandToCompiler(commandName); |
|
} |
|
|
|
// Otherwise use the builder for building the final command |
|
return BeginCompilerCommand(commandName) |
|
.With(commandArgs) |
|
.SendToCompiler(); |
|
} |
|
|
|
private static readonly MethodInfo DummyMethodInfo = typeof(BurstCompiler).GetMethod(nameof(DummyMethod), BindingFlags.Static | BindingFlags.NonPublic); |
|
|
|
/// <summary> |
|
/// Dummy empty method for being able to send a command to the compiler |
|
/// </summary> |
|
private static void DummyMethod() { } |
|
|
|
#if !UNITY_EDITOR && !BURST_INTERNAL |
|
/// <summary> |
|
/// Internal class to detect at standalone player time if AOT settings were enabling burst. |
|
/// </summary> |
|
[BurstCompile] |
|
internal static class BurstCompilerHelper |
|
{ |
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|
private delegate bool IsBurstEnabledDelegate(); |
|
private static readonly IsBurstEnabledDelegate IsBurstEnabledImpl = new IsBurstEnabledDelegate(IsBurstEnabled); |
|
|
|
[BurstCompile] |
|
[AOT.MonoPInvokeCallback(typeof(IsBurstEnabledDelegate))] |
|
private static bool IsBurstEnabled() |
|
{ |
|
bool result = true; |
|
DiscardedMethod(ref result); |
|
return result; |
|
} |
|
|
|
[BurstDiscard] |
|
private static void DiscardedMethod(ref bool value) |
|
{ |
|
value = false; |
|
} |
|
|
|
private static unsafe bool IsCompiledByBurst(Delegate del) |
|
{ |
|
var delegateMethodId = Unity.Burst.LowLevel.BurstCompilerService.CompileAsyncDelegateMethod(del, string.Empty); |
|
// We don't try to run the method, having a pointer is already enough to tell us that burst was active for AOT settings |
|
return Unity.Burst.LowLevel.BurstCompilerService.GetAsyncCompiledAsyncDelegateMethod(delegateMethodId) != (void*)0; |
|
} |
|
|
|
/// <summary> |
|
/// Gets a boolean indicating whether burst was enabled for standalone player, used only at runtime. |
|
/// </summary> |
|
public static readonly bool IsBurstGenerated = IsCompiledByBurst(IsBurstEnabledImpl); |
|
} |
|
#endif // !UNITY_EDITOR && !BURST_INTERNAL |
|
|
|
/// <summary> |
|
/// Fake delegate class to make BurstCompilerService.CompileAsyncDelegateMethod happy |
|
/// so that it can access the underlying static method via the property get_Method. |
|
/// So this class is not a delegate. |
|
/// </summary> |
|
private class FakeDelegate |
|
{ |
|
public FakeDelegate(MethodInfo method) |
|
{ |
|
Method = method; |
|
} |
|
|
|
[Preserve] |
|
public MethodInfo Method { get; } |
|
} |
|
|
|
#else // UNITY_DOTSPLAYER || NET_DOTS |
|
|
|
/// <summary> |
|
/// Compile the following delegate into a function pointer with burst, invokable from a Burst Job or from regular C#. |
|
/// </summary> |
|
/// <typeparam name="T">Type of the delegate of the function pointer</typeparam> |
|
/// <param name="delegateMethod">The delegate to compile</param> |
|
/// <returns>A function pointer invokable from a Burst Job or from regular C#</returns> |
|
public static unsafe FunctionPointer<T> CompileFunctionPointer<T>(T delegateMethod) where T : System.Delegate |
|
{ |
|
// Make sure that the delegate will never be collected |
|
GCHandle.Alloc(delegateMethod); |
|
return new FunctionPointer<T>(Marshal.GetFunctionPointerForDelegate(delegateMethod)); |
|
} |
|
|
|
internal static bool IsApiAvailable(string apiName) |
|
{ |
|
return false; |
|
} |
|
#endif |
|
} |
|
}
|
|
|