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.

305 lines
13KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.CompilerServices;
  4. using Svelto.Common;
  5. using Svelto.DataStructures;
  6. using Svelto.ECS.Internal;
  7. namespace Svelto.ECS
  8. {
  9. public partial class EnginesRoot : IDisposable
  10. {
  11. /// <summary>
  12. /// Dispose an EngineRoot once not used anymore, so that all the
  13. /// engines are notified with the entities removed.
  14. /// It's a clean up process.
  15. /// </summary>
  16. public void Dispose()
  17. {
  18. using (var profiler = new PlatformProfiler("Final Dispose"))
  19. {
  20. foreach (var groups in _groupEntityViewsDB)
  21. {
  22. foreach (var entityList in groups.Value)
  23. {
  24. entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove,
  25. profiler, new ExclusiveGroup.ExclusiveGroupStruct(groups.Key));
  26. }
  27. }
  28. _groupEntityViewsDB.Clear();
  29. _groupsPerEntity.Clear();
  30. foreach (var engine in _disposableEngines)
  31. engine.Dispose();
  32. _disposableEngines.Clear();
  33. _enginesSet.Clear();
  34. _enginesTypeSet.Clear();
  35. _reactiveEnginesSwap.Clear();
  36. _reactiveEnginesAddRemove.Clear();
  37. _entitiesOperations.Clear();
  38. _transientEntitiesOperations.Clear();
  39. _scheduler.Dispose();
  40. #if DEBUG && !PROFILER
  41. _idCheckers.Clear();
  42. #endif
  43. _groupedEntityToAdd = null;
  44. _entitiesStream.Dispose();
  45. }
  46. GC.SuppressFinalize(this);
  47. }
  48. ~EnginesRoot()
  49. {
  50. Console.LogWarning("Engines Root has been garbage collected, don't forget to call Dispose()!");
  51. Dispose();
  52. }
  53. ///--------------------------------------------
  54. ///
  55. public IEntityStreamConsumerFactory GenerateConsumerFactory()
  56. {
  57. return new GenericEntityStreamConsumerFactory(this);
  58. }
  59. public IEntityFactory GenerateEntityFactory()
  60. {
  61. return new GenericEntityFactory(this);
  62. }
  63. public IEntityFunctions GenerateEntityFunctions()
  64. {
  65. return new GenericEntityFunctions(this);
  66. }
  67. ///--------------------------------------------
  68. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  69. EntityStructInitializer BuildEntity(EGID entityID, IEntityBuilder[] entitiesToBuild,
  70. IEnumerable<object> implementors = null)
  71. {
  72. CheckAddEntityID(entityID);
  73. var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd,
  74. entitiesToBuild, implementors);
  75. return new EntityStructInitializer(entityID, dic);
  76. }
  77. ///--------------------------------------------
  78. void Preallocate<T>(uint groupID, uint size) where T : IEntityDescriptor, new()
  79. {
  80. var entityViewsToBuild = EntityDescriptorTemplate<T>.descriptor.entitiesToBuild;
  81. var numberOfEntityViews = entityViewsToBuild.Length;
  82. //reserve space in the database
  83. if (_groupEntityViewsDB.TryGetValue(groupID, out var group) == false)
  84. group = _groupEntityViewsDB[groupID] = new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>();
  85. for (var index = 0; index < numberOfEntityViews; index++)
  86. {
  87. var entityViewBuilder = entityViewsToBuild[index];
  88. var entityViewType = entityViewBuilder.GetEntityType();
  89. var refWrapper = new RefWrapper<Type>(entityViewType);
  90. if (group.TryGetValue(refWrapper, out var dbList) == false)
  91. group[refWrapper] = entityViewBuilder.Preallocate(ref dbList, size);
  92. else
  93. dbList.SetCapacity(size);
  94. if (_groupsPerEntity.TryGetValue(refWrapper, out var groupedGroup) == false)
  95. groupedGroup = _groupsPerEntity[refWrapper] =
  96. new FasterDictionary<uint, ITypeSafeDictionary>();
  97. groupedGroup[groupID] = dbList;
  98. }
  99. }
  100. ///--------------------------------------------
  101. ///
  102. void MoveEntityFromAndToEngines(IEntityBuilder[] entityBuilders, EGID fromEntityGID, EGID? toEntityGID)
  103. {
  104. using (var sampler = new PlatformProfiler("Move Entity From Engines"))
  105. {
  106. //for each entity view generated by the entity descriptor
  107. if (_groupEntityViewsDB.TryGetValue(fromEntityGID.groupID, out var fromGroup) == false)
  108. throw new ECSException("from group not found eid: ".FastConcat(fromEntityGID.entityID)
  109. .FastConcat(" group: ").FastConcat(fromEntityGID.groupID));
  110. //Check if there is an EntityInfoView linked to this entity, if so it's a DynamicEntityDescriptor!
  111. if (fromGroup.TryGetValue(new RefWrapper<Type>(EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW),
  112. out var entityInfoViewDic) &&
  113. (entityInfoViewDic as TypeSafeDictionary<EntityStructInfoView>).TryGetValue(
  114. fromEntityGID.entityID, out var entityInfoView))
  115. MoveEntities(fromEntityGID, toEntityGID, entityInfoView.entitiesToBuild, fromGroup, sampler);
  116. //otherwise it's a normal static entity descriptor
  117. else
  118. MoveEntities(fromEntityGID, toEntityGID, entityBuilders, fromGroup, sampler);
  119. }
  120. }
  121. void MoveEntities(EGID fromEntityGID, EGID? toEntityGID, IEntityBuilder[] entitiesToMove,
  122. FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, PlatformProfiler sampler)
  123. {
  124. FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = null;
  125. if (toEntityGID != null)
  126. {
  127. var toGroupID = toEntityGID.Value.groupID;
  128. if (_groupEntityViewsDB.TryGetValue(toGroupID, out toGroup) == false)
  129. toGroup = _groupEntityViewsDB[toGroupID] = new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>();
  130. //Add all the entities to the dictionary
  131. for (var i = 0; i < entitiesToMove.Length; i++)
  132. CopyEntityToDictionary(fromEntityGID, toEntityGID.Value, fromGroup, toGroup,
  133. entitiesToMove[i].GetEntityType());
  134. }
  135. //call all the callbacks
  136. for (var i = 0; i < entitiesToMove.Length; i++)
  137. MoveEntityViewFromAndToEngines(fromEntityGID, toEntityGID, fromGroup, toGroup,
  138. entitiesToMove[i].GetEntityType(), sampler);
  139. //then remove all the entities from the dictionary
  140. for (var i = 0; i < entitiesToMove.Length; i++)
  141. RemoveEntityFromDictionary(fromEntityGID, fromGroup, entitiesToMove[i].GetEntityType(), sampler);
  142. }
  143. void CopyEntityToDictionary(EGID entityGID, EGID toEntityGID,
  144. FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup,
  145. FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, Type entityViewType)
  146. {
  147. var wrapper = new RefWrapper<Type>(entityViewType);
  148. if (fromGroup.TryGetValue(wrapper, out var fromTypeSafeDictionary) == false)
  149. {
  150. throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID)
  151. .FastConcat(" group: ").FastConcat(entityGID.groupID));
  152. }
  153. #if DEBUG && !PROFILER
  154. if (fromTypeSafeDictionary.Has(entityGID.entityID) == false)
  155. {
  156. throw new EntityNotFoundException(entityGID, entityViewType);
  157. }
  158. #endif
  159. if (toGroup.TryGetValue(wrapper, out var toEntitiesDictionary) == false)
  160. {
  161. toEntitiesDictionary = fromTypeSafeDictionary.Create();
  162. toGroup.Add(wrapper, toEntitiesDictionary);
  163. }
  164. //todo: this must be unit tested properly
  165. if (_groupsPerEntity.TryGetValue(wrapper, out var groupedGroup) == false)
  166. groupedGroup = _groupsPerEntity[wrapper] =
  167. new FasterDictionary<uint, ITypeSafeDictionary>();
  168. groupedGroup[toEntityGID.groupID] = toEntitiesDictionary;
  169. fromTypeSafeDictionary.AddEntityToDictionary(entityGID, toEntityGID, toEntitiesDictionary);
  170. }
  171. void MoveEntityViewFromAndToEngines(EGID entityGID, EGID? toEntityGID,
  172. FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup,
  173. FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, Type entityViewType,
  174. in PlatformProfiler profiler)
  175. {
  176. //add all the entities
  177. var refWrapper = new RefWrapper<Type>(entityViewType);
  178. if (fromGroup.TryGetValue(refWrapper, out var fromTypeSafeDictionary) == false)
  179. {
  180. throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID)
  181. .FastConcat(" group: ").FastConcat(entityGID.groupID));
  182. }
  183. ITypeSafeDictionary toEntitiesDictionary = null;
  184. if (toGroup != null)
  185. toEntitiesDictionary = toGroup[refWrapper]; //this is guaranteed to exist by AddEntityToDictionary
  186. #if DEBUG && !PROFILER
  187. if (fromTypeSafeDictionary.Has(entityGID.entityID) == false)
  188. throw new EntityNotFoundException(entityGID, entityViewType);
  189. #endif
  190. fromTypeSafeDictionary.MoveEntityFromEngines(entityGID, toEntityGID,
  191. toEntitiesDictionary, toEntityGID == null ? _reactiveEnginesAddRemove : _reactiveEnginesSwap,
  192. in profiler);
  193. }
  194. void RemoveEntityFromDictionary(EGID entityGID,
  195. FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, Type entityViewType,
  196. in PlatformProfiler profiler)
  197. {
  198. var refWrapper = new RefWrapper<Type>(entityViewType);
  199. if (fromGroup.TryGetValue(refWrapper, out var fromTypeSafeDictionary) == false)
  200. {
  201. throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID)
  202. .FastConcat(" group: ").FastConcat(entityGID.groupID));
  203. }
  204. fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID, profiler);
  205. if (fromTypeSafeDictionary.Count == 0) //clean up
  206. {
  207. //todo: this must be unit tested properly
  208. _groupsPerEntity[refWrapper].Remove(entityGID.groupID);
  209. //I don't remove the group if empty on purpose, in case it needs to be reused
  210. }
  211. }
  212. /// <summary>
  213. /// Todo: I should keep the group, but I need to mark the group as deleted for the Exist function to work
  214. /// </summary>
  215. /// <param name="groupID"></param>
  216. /// <param name="profiler"></param>
  217. void RemoveGroupAndEntitiesFromDB(uint groupID, in PlatformProfiler profiler)
  218. {
  219. var dictionariesOfEntities = _groupEntityViewsDB[groupID];
  220. foreach (var dictionaryOfEntities in dictionariesOfEntities)
  221. {
  222. dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler,
  223. new ExclusiveGroup.ExclusiveGroupStruct(groupID));
  224. var groupedGroupOfEntities = _groupsPerEntity[dictionaryOfEntities.Key];
  225. groupedGroupOfEntities.Remove(groupID);
  226. }
  227. //careful, in this case I assume you really don't want to use this group anymore
  228. //so I remove it from the database
  229. _groupEntityViewsDB.Remove(groupID);
  230. }
  231. internal Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityStruct
  232. {
  233. return _entitiesStream.GenerateConsumer<T>(name, capacity);
  234. }
  235. public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity) where T : unmanaged,
  236. IEntityStruct
  237. {
  238. return _entitiesStream.GenerateConsumer<T>(group, name, capacity);
  239. }
  240. //one datastructure rule them all:
  241. //split by group
  242. //split by type per group. It's possible to get all the entities of a give type T per group thanks
  243. //to the FasterDictionary capabilities OR it's possible to get a specific entityView indexed by
  244. //ID. This ID doesn't need to be the EGID, it can be just the entityID
  245. //for each group id, save a dictionary indexed by entity type of entities indexed by id
  246. //ITypeSafeDictionary = Key = entityID, Value = EntityStruct
  247. readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> _groupEntityViewsDB;
  248. //for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are
  249. //found indexed by group id
  250. //EntityViewType //groupID //entityID, EntityStruct
  251. readonly FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> _groupsPerEntity;
  252. readonly EntitiesDB _entitiesDB;
  253. readonly EntitiesStream _entitiesStream;
  254. }
  255. }