using System; using System.Runtime.InteropServices; using System.Text; namespace Unity.Burst { /// <summary> /// Provides helper intrinsics that can be used at runtime. /// </summary> #if BURST_COMPILER_SHARED internal static class BurstRuntimeInternal #else public static class BurstRuntime #endif { /// <summary> /// Gets a 32-bits hashcode from a type computed for the <see cref="System.Type.AssemblyQualifiedName"/> /// </summary> /// <typeparam name="T">The type to compute the hash from</typeparam> /// <returns>The 32-bit hashcode.</returns> public static int GetHashCode32<T>() { #if !UNITY_DOTSRUNTIME_IL2CPP return HashCode32<T>.Value; #else // DOTS Runtime IL2CPP Builds do not use C#'s lazy static initialization order (it uses a C like order, aka random) // As such we cannot rely on static init for caching types since any static constructor calling this function // may return uninitialized/default-initialized memory return HashStringWithFNV1A32(typeof(T).AssemblyQualifiedName); #endif } /// <summary> /// Gets a 32-bits hashcode from a type computed for the <see cref="System.Type.AssemblyQualifiedName"/> /// This method cannot be used from a burst job. /// </summary> /// <param name="type">The type to compute the hash from</param> /// <returns>The 32-bit hashcode.</returns> public static int GetHashCode32(Type type) { return HashStringWithFNV1A32(type.AssemblyQualifiedName); } /// <summary> /// Gets a 64-bits hashcode from a type computed for the <see cref="System.Type.AssemblyQualifiedName"/> /// </summary> /// <typeparam name="T">The type to compute the hash from</typeparam> /// <returns>The 64-bit hashcode.</returns> public static long GetHashCode64<T>() { #if !UNITY_DOTSRUNTIME_IL2CPP return HashCode64<T>.Value; #else // DOTS Runtime IL2CPP Builds do not use C#'s lazy static initialization order (it uses a C like order, aka random) // As such we cannot rely on static init for caching types since any static constructor calling this function // may return uninitialized/default-initialized memory return HashStringWithFNV1A64(typeof(T).AssemblyQualifiedName); #endif } /// <summary> /// Gets a 64-bits hashcode from a type computed for the <see cref="System.Type.AssemblyQualifiedName"/>. /// This method cannot be used from a burst job. /// </summary> /// <param name="type">Type to calculate a hash for</param> /// <returns>The 64-bit hashcode.</returns> public static long GetHashCode64(Type type) { return HashStringWithFNV1A64(type.AssemblyQualifiedName); } // method internal as it is used by the compiler directly internal static int HashStringWithFNV1A32(string text) { // Using http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-1a // with basis and prime: const uint offsetBasis = 2166136261; const uint prime = 16777619; uint result = offsetBasis; foreach (var c in text) { result = prime * (result ^ (byte)(c & 255)); result = prime * (result ^ (byte)(c >> 8)); } return (int)result; } // method internal as it is used by the compiler directly internal static long HashStringWithFNV1A64(string text) { // Using http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-1a // with basis and prime: const ulong offsetBasis = 14695981039346656037; const ulong prime = 1099511628211; ulong result = offsetBasis; foreach (var c in text) { result = prime * (result ^ (byte)(c & 255)); result = prime * (result ^ (byte)(c >> 8)); } return (long)result; } private struct HashCode32<T> { public static readonly int Value = HashStringWithFNV1A32(typeof(T).AssemblyQualifiedName); } private struct HashCode64<T> { public static readonly long Value = HashStringWithFNV1A64(typeof(T).AssemblyQualifiedName); } #if !BURST_COMPILER_SHARED /// <summary> /// Allows for loading additional Burst native libraries /// Important: Designed for Play mode / Desktop Standalone Players ONLY /// In Editor, any libraries that have been loaded will be unloaded on exit of playmode /// Only supported from 2020.1 and later. You can use BurstCompiler.IsLoadAdditionalLibrarySupported() to confirm it is available. /// </summary> /// <param name="pathToLibBurstGenerated">Absolute filesystem location of bursted library to load</param> /// <returns>true if the library was loaded successfully</returns> public static bool LoadAdditionalLibrary(string pathToLibBurstGenerated) { if (BurstCompiler.IsLoadAdditionalLibrarySupported()) { return LoadAdditionalLibraryInternal(pathToLibBurstGenerated); } return false; } internal static bool LoadAdditionalLibraryInternal(string pathToLibBurstGenerated) { #if !UNITY_DOTSRUNTIME return (bool)typeof(Unity.Burst.LowLevel.BurstCompilerService).GetMethod("LoadBurstLibrary").Invoke(null, new object[] { pathToLibBurstGenerated }); #else return false; #endif } #if UNITY_2020_1_OR_NEWER internal static void Initialize() { } // Prevent BurstCompilerService.Log from being stripped, introduce PreserveAttribute to avoid //requiring a unityengine using directive, il2cpp will see the attribute and know to not strip //the Log method and its BurstCompilerService.Log dependency internal class PreserveAttribute : System.Attribute {} [Preserve] internal static unsafe void Log(byte* message, int logType, byte* fileName, int lineNumber) { Unity.Burst.LowLevel.BurstCompilerService.Log((byte*) 0, (Unity.Burst.LowLevel.BurstCompilerService.BurstLogType)logType, message, fileName, lineNumber); } #elif UNITY_2019_4_OR_NEWER // Because we can't back-port the new API BurstCompilerService.Log introduced in 2020.1 // we are still trying to allow to log on earlier version of Unity by going back to managed // code when we are using Debug.Log. It is not great in terms of performance but it should not // be a matter when debugging. internal static unsafe void Log(byte* message, int logType, byte* fileName, int lineNumber) { var fp = LogHelper.Instance.Data; // If we have a domain reload, the function pointer will be cleared, so we can't call it. if (fp.IsCreated) { fp.Invoke(message, logType, fileName, lineNumber); } } private unsafe delegate void NativeLogDelegate(byte* message, int logType, byte* filename, int lineNumber); private static readonly unsafe NativeLogDelegate ManagedNativeLog = ManagedNativeLogImpl; [AOT.MonoPInvokeCallback(typeof(NativeLogDelegate))] private static unsafe void ManagedNativeLogImpl(byte* message, int logType, byte* filename, int lineNumber) { if (message == null) return; int byteCount = 0; while (message[byteCount] != 0) byteCount++; var managedText = Encoding.UTF8.GetString(message, byteCount); switch (logType) { case 1: UnityEngine.Debug.LogWarning(managedText); break; case 2: UnityEngine.Debug.LogError(managedText); break; default: UnityEngine.Debug.Log(managedText); break; } } private class LogHelper { public static readonly SharedStatic<FunctionPointer<NativeLogDelegate>> Instance = SharedStatic<FunctionPointer<NativeLogDelegate>>.GetOrCreate<LogHelper>(); } static BurstRuntime() { LogHelper.Instance.Data = new FunctionPointer<NativeLogDelegate>(Marshal.GetFunctionPointerForDelegate(ManagedNativeLog)); } [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.AfterAssembliesLoaded)] internal static void Initialize() { } #else internal static void Initialize() { } internal static unsafe void Log(byte* message, int logType, byte* fileName, int lineNumber) { } #endif #endif // !BURST_COMPILER_SHARED /// <summary> /// Return a pointer to read-only memory consisting of the literal UTF-8 bytes of a string constant. /// </summary> /// <param name="str">A string which must a string literal</param> /// <param name="byteCount">Receives the number of UTF-8 encoded bytes the constant contains (excluding null terminator)</param> /// <returns>A pointer to constant data representing the UTF-8 encoded bytes of the string literal, terminated with a null terminator</returns> public unsafe static byte* GetUTF8LiteralPointer(string str, out int byteCount) { throw new NotImplementedException("This function only works from Burst"); } } }