#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