Mirror of Svelto.ECS because we're a fan of it
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.

285 lines
9.1KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Reflection;
  5. using System.Runtime.CompilerServices;
  6. using System.Threading;
  7. using DBC.ECS;
  8. using Svelto.Common;
  9. using Svelto.DataStructures;
  10. using Svelto.ECS.Hybrid;
  11. using Svelto.ECS.Internal;
  12. using Svelto.Utilities;
  13. namespace Svelto.ECS
  14. {
  15. struct ComponentBuilderComparer : IEqualityComparer<IComponentBuilder>
  16. {
  17. public bool Equals(IComponentBuilder x, IComponentBuilder y)
  18. {
  19. return x.GetEntityComponentType() == y.GetEntityComponentType();
  20. }
  21. public int GetHashCode(IComponentBuilder obj)
  22. {
  23. return obj.GetEntityComponentType().GetHashCode();
  24. }
  25. }
  26. public static class BurstCompatibleCounter
  27. {
  28. public static int counter;
  29. }
  30. static public class ComponentTypeMap
  31. {
  32. static readonly FasterDictionary<RefWrapper<Type>, ComponentID> _componentTypeMap = new FasterDictionary<RefWrapper<Type>, ComponentID>();
  33. static readonly FasterDictionary<ComponentID, Type> _reverseComponentTypeMap = new FasterDictionary<ComponentID, Type>();
  34. public static void Add(Type type, ComponentID idData)
  35. {
  36. _componentTypeMap.Add(type, idData);
  37. _reverseComponentTypeMap.Add(idData, type);
  38. }
  39. public static ComponentID FetchID(Type type)
  40. {
  41. return _componentTypeMap[type];
  42. }
  43. public static Type FetchType(ComponentID id)
  44. {
  45. return _reverseComponentTypeMap[id];
  46. }
  47. }
  48. public class ComponentTypeID<T> where T : struct, _IInternalEntityComponent
  49. {
  50. static readonly SharedStaticWrapper<ComponentID, ComponentTypeID<T>> _id;
  51. public static ComponentID id
  52. {
  53. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  54. get => _id.Data;
  55. }
  56. //todo: any reason to not do this? If I don't, I cannot Create filters in ready functions and
  57. //I have to remove the CreateFilter method
  58. static ComponentTypeID()
  59. {
  60. Init();
  61. }
  62. #if UNITY_BURST
  63. [Unity.Burst.BurstDiscard]
  64. //SharedStatic values must be initialized from not burstified code
  65. #endif
  66. static void Init()
  67. {
  68. _id.Data = Interlocked.Increment(ref BurstCompatibleCounter.counter);
  69. ComponentTypeMap.Add(typeof(T), id);
  70. }
  71. }
  72. sealed class ComponentIDDebugProxy
  73. {
  74. public ComponentIDDebugProxy(ComponentID id)
  75. {
  76. this._id = id;
  77. }
  78. public Type type => ComponentTypeMap.FetchType(_id);
  79. readonly ComponentID _id;
  80. }
  81. [DebuggerTypeProxy(typeof(ComponentIDDebugProxy))]
  82. public struct ComponentID: IEquatable<ComponentID>
  83. {
  84. public static implicit operator int(ComponentID id)
  85. {
  86. return id._id;
  87. }
  88. public static implicit operator uint(ComponentID id)
  89. {
  90. return (uint)id._id;
  91. }
  92. public static implicit operator ComponentID(int id)
  93. {
  94. return new ComponentID() {_id = id};
  95. }
  96. public bool Equals(ComponentID other)
  97. {
  98. return _id == other._id;
  99. }
  100. public override int GetHashCode()
  101. {
  102. return _id;
  103. }
  104. int _id;
  105. }
  106. public class ComponentBuilder<T> : IComponentBuilder where T : struct, _IInternalEntityComponent
  107. {
  108. internal static readonly Type ENTITY_COMPONENT_TYPE;
  109. internal static readonly bool IS_ENTITY_VIEW_COMPONENT;
  110. static readonly string ENTITY_COMPONENT_NAME;
  111. static readonly bool IS_UNMANAGED;
  112. #if SLOW_SVELTO_SUBMISSION
  113. public static readonly bool HAS_EGID;
  114. public static readonly bool HAS_REFERENCE;
  115. #endif
  116. static ComponentBuilder()
  117. {
  118. ENTITY_COMPONENT_TYPE = typeof(T);
  119. IS_ENTITY_VIEW_COMPONENT = typeof(IEntityViewComponent).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  120. #if SLOW_SVELTO_SUBMISSION
  121. HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  122. HAS_REFERENCE = typeof(INeedEntityReference).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
  123. SetEGIDWithoutBoxing<T>.Warmup();
  124. #endif
  125. ENTITY_COMPONENT_NAME = ENTITY_COMPONENT_TYPE.ToString();
  126. IS_UNMANAGED = TypeCache<T>.isUnmanaged; //attention this is important as it serves as warm up for Type<T>
  127. #if UNITY_NATIVE
  128. if (IS_UNMANAGED)
  129. EntityComponentIDMap.Register<T>(new Filler<T>());
  130. #endif
  131. ComponentBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT);
  132. if (IS_ENTITY_VIEW_COMPONENT)
  133. {
  134. EntityViewComponentCache.InitCache();
  135. }
  136. else
  137. {
  138. if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_INFO_COMPONENT &&
  139. TypeCache<T>.isUnmanaged == false)
  140. throw new Exception(
  141. $"Entity Component check failed, unexpected struct type (must be unmanaged) {ENTITY_COMPONENT_TYPE}");
  142. }
  143. }
  144. public ComponentBuilder()
  145. {
  146. _initializer = default;
  147. }
  148. public ComponentBuilder(in T initializer) : this()
  149. {
  150. _initializer = initializer;
  151. }
  152. public bool isUnmanaged => IS_UNMANAGED;
  153. public ComponentID getComponentID => ComponentTypeID<T>.id;
  154. static readonly ThreadLocal<EntityViewComponentCache> _localCache = new ThreadLocal<EntityViewComponentCache>(() => new EntityViewComponentCache());
  155. public void BuildEntityAndAddToList(ITypeSafeDictionary dictionary, EGID egid, IEnumerable<object> implementors)
  156. {
  157. var castedDic = dictionary as ITypeSafeDictionary<T>;
  158. if (IS_ENTITY_VIEW_COMPONENT)
  159. {
  160. T entityComponent = default;
  161. Check.Require(castedDic.ContainsKey(egid.entityID) == false,
  162. $"building an entity with already used entity id! id: '{(ulong)egid}', {ENTITY_COMPONENT_NAME}");
  163. this.SetEntityViewComponentImplementors(ref entityComponent, implementors, _localCache.Value);
  164. castedDic.Add(egid.entityID, entityComponent);
  165. }
  166. else
  167. {
  168. Check.Require(!castedDic.ContainsKey(egid.entityID),
  169. $"building an entity with already used entity id! id: '{egid.entityID}'");
  170. castedDic.Add(egid.entityID, _initializer);
  171. }
  172. }
  173. void IComponentBuilder.Preallocate(ITypeSafeDictionary dictionary, uint size)
  174. {
  175. Preallocate(dictionary, size);
  176. }
  177. public ITypeSafeDictionary CreateDictionary(uint size)
  178. {
  179. return TypeSafeDictionaryFactory<T>.Create(size);
  180. }
  181. public Type GetEntityComponentType()
  182. {
  183. return ENTITY_COMPONENT_TYPE;
  184. }
  185. public override int GetHashCode()
  186. {
  187. return _initializer.GetHashCode();
  188. }
  189. static void Preallocate(ITypeSafeDictionary dictionary, uint size)
  190. {
  191. dictionary.EnsureCapacity(size);
  192. }
  193. readonly T _initializer;
  194. internal class EntityViewComponentCache
  195. {
  196. internal readonly FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>> cachedFields;
  197. internal readonly Dictionary<Type, Type[]> cachedTypes;
  198. //this is just a local static cache that is cleared after every use
  199. #if DEBUG && !PROFILE_SVELTO
  200. internal readonly Dictionary<Type, ECSTuple<object, int>> implementorsByType;
  201. #else
  202. internal readonly Dictionary<Type, object> implementorsByType;
  203. #endif
  204. internal EntityViewComponentCache()
  205. {
  206. cachedFields = new FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>>();
  207. var type = typeof(T);
  208. var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
  209. for (var i = fields.Length - 1; i >= 0; --i)
  210. {
  211. var field = fields[i];
  212. if (field.FieldType.IsInterface == true)
  213. {
  214. var setter = FastInvoke<T>.MakeSetter(field);
  215. //for each interface, cache the setter for this type
  216. cachedFields.Add(new KeyValuePair<Type, FastInvokeActionCast<T>>(field.FieldType, setter));
  217. }
  218. }
  219. #if DEBUG && !PROFILE_SVELTO
  220. if (fields.Length == 0)
  221. Console.LogWarning($"No fields found in component {type}. Are you declaring only properties?");
  222. #endif
  223. cachedTypes = new Dictionary<Type, Type[]>();
  224. #if DEBUG && !PROFILE_SVELTO
  225. implementorsByType = new Dictionary<Type, ECSTuple<object, int>>();
  226. #else
  227. implementorsByType = new Dictionary<Type, object>();
  228. #endif
  229. }
  230. internal static void InitCache()
  231. {
  232. }
  233. }
  234. }
  235. }