#if UNITY_2019_3_OR_NEWER using System; using System.Collections.Generic; using System.Diagnostics; #if BURST_UNITY_MOCK using System.Runtime.CompilerServices; #endif using Unity.Collections.LowLevel.Unsafe; namespace Unity.Burst { /// /// A structure that allows to share mutable static data between C# and HPC#. /// /// Type of the data to share (must not contain any reference types) public readonly unsafe struct SharedStatic where T : struct { private readonly void* _buffer; private SharedStatic(void* buffer) { _buffer = buffer; CheckIf_T_IsUnmanagedOrThrow(); // We will remove this once we have full support for unmanaged constraints with C# 8.0 } /// /// Get a writable reference to the shared data. /// public ref T Data { get { #if UNITY_DOTSPLAYER return ref UnsafeUtility.AsRef(_buffer); #else return ref Unsafe.AsRef(_buffer); #endif } } /// /// Get a direct unsafe pointer to the shared data. /// public void* UnsafeDataPointer { get { return _buffer; } } /// /// Creates a shared static data for the specified context (usable from both C# and HPC#) /// /// A type class that uniquely identifies the this shared data. /// Optional alignment /// A shared static for the specified context public static SharedStatic GetOrCreate(uint alignment = 0) { return new SharedStatic(SharedStatic.GetOrCreateSharedStaticInternal( BurstRuntime.GetHashCode64(), 0, (uint)UnsafeUtility.SizeOf(), alignment == 0 ? 4 : alignment)); } /// /// Creates a shared static data for the specified context and sub-context (usable from both C# and HPC#) /// /// A type class that uniquely identifies the this shared data. /// A type class that uniquely identifies this shared data within a sub-context of the primary context /// Optional alignment /// A shared static for the specified context public static SharedStatic GetOrCreate(uint alignment = 0) { return new SharedStatic(SharedStatic.GetOrCreateSharedStaticInternal( BurstRuntime.GetHashCode64(), BurstRuntime.GetHashCode64(), (uint)UnsafeUtility.SizeOf(), alignment == 0 ? 4 : alignment)); } #if !NET_DOTS /// /// Creates a shared static data for the specified context (reflection based, only usable from C#, but not from HPC#) /// /// A type class that uniquely identifies the this shared data /// Optional alignment /// A shared static for the specified context public static SharedStatic GetOrCreate(Type contextType, uint alignment = 0) { return new SharedStatic(SharedStatic.GetOrCreateSharedStaticInternal( BurstRuntime.GetHashCode64(contextType), 0, (uint)UnsafeUtility.SizeOf(), alignment == 0 ? 4 : alignment)); } /// /// Creates a shared static data for the specified context and sub-context (usable from both C# and HPC#) /// /// A type class that uniquely identifies the this shared data /// A type class that uniquely identifies this shared data within a sub-context of the primary context /// Optional alignment /// A shared static for the specified context public static SharedStatic GetOrCreate(Type contextType, Type subContextType, uint alignment = 0) { return new SharedStatic(SharedStatic.GetOrCreateSharedStaticInternal( BurstRuntime.GetHashCode64(contextType), BurstRuntime.GetHashCode64(subContextType), (uint)UnsafeUtility.SizeOf(), alignment == 0 ? (uint)4 : alignment)); } #endif [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] private static void CheckIf_T_IsUnmanagedOrThrow() { if (!UnsafeUtility.IsUnmanaged()) throw new InvalidOperationException($"The type {typeof(T)} used in SharedStatic<{typeof(T)}> must be unmanaged (contain no managed types)."); } } internal static class SharedStatic { #if !NET_DOTS private static readonly Dictionary HashToType = new Dictionary(); public static unsafe void* GetOrCreateSharedStaticInternal(Type typeContext, Type subTypeContext, uint sizeOf, uint alignment) { return GetOrCreateSharedStaticInternal(GetSafeHashCode64(typeContext), GetSafeHashCode64(subTypeContext), sizeOf, alignment); } private static long GetSafeHashCode64(Type type) { var hash = BurstRuntime.GetHashCode64(type); lock (HashToType) { Type existingType; if (HashToType.TryGetValue(hash, out existingType)) { if (existingType != type) { var message = $"The type `{type}` has a hash conflict with `{existingType}`"; #if !BURST_UNITY_MOCK && !UNITY_DOTSPLAYER UnityEngine.Debug.LogError(message); #endif throw new InvalidOperationException(message); } } else { HashToType.Add(hash, type); } } return hash; } #endif [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] private static void CheckSizeOf(uint sizeOf) { if (sizeOf == 0) throw new ArgumentException("sizeOf must be > 0", nameof(sizeOf)); } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] private static unsafe void CheckResult(void* result) { if (result == null) throw new InvalidOperationException("Unable to create a SharedStatic for this key. This is most likely due to the size of the struct inside of the SharedStatic having changed or the same key being reused for differently sized values. To fix this the editor needs to be restarted."); } // Prevent GetOrCreateSharedMemory from being stripped, by preventing GetOrCreateSharedStaticInteranl fromm being stripped. internal class PreserveAttribute : System.Attribute {} [Preserve] public static unsafe void* GetOrCreateSharedStaticInternal(long getHashCode64, long getSubHashCode64, uint sizeOf, uint alignment) { CheckSizeOf(sizeOf); var hash128 = new UnityEngine.Hash128((ulong)getHashCode64, (ulong)getSubHashCode64); var result = Unity.Burst.LowLevel.BurstCompilerService.GetOrCreateSharedMemory(ref hash128, sizeOf, alignment); CheckResult(result); return result; } } } #endif