#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.Burst.LowLevel; using Unity.Collections.LowLevel.Unsafe; using UnityEngine; 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. It is likely that the same key was used to allocate a shared memory with a smaller size while the new size requested is bigger"); } public static unsafe void* GetOrCreateSharedStaticInternal(long getHashCode64, long getSubHashCode64, uint sizeOf, uint alignment) { CheckSizeOf(sizeOf); var hash128 = new Hash128((ulong)getHashCode64, (ulong)getSubHashCode64); var result = BurstCompilerService.GetOrCreateSharedMemory(ref hash128, sizeOf, alignment); CheckResult(result); return result; } } } #endif