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.

331 lines
15KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.CompilerServices;
  4. using DBC.ECS;
  5. using Svelto.Common;
  6. using Svelto.DataStructures;
  7. using Svelto.ECS.Internal;
  8. namespace Svelto.ECS
  9. {
  10. public partial class EnginesRoot : IDisposable, IUnitTestingInterface
  11. {
  12. ///--------------------------------------------
  13. ///
  14. public IEntityStreamConsumerFactory GenerateConsumerFactory()
  15. {
  16. return new GenericEntityStreamConsumerFactory(this);
  17. }
  18. public IEntityFactory GenerateEntityFactory()
  19. {
  20. return new GenericEntityFactory(this);
  21. }
  22. public IEntityFunctions GenerateEntityFunctions()
  23. {
  24. return new GenericEntityFunctions(this);
  25. }
  26. ///--------------------------------------------
  27. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  28. EntityInitializer BuildEntity
  29. (EGID entityID, IComponentBuilder[] componentsToBuild, Type descriptorType,
  30. IEnumerable<object> implementors = null)
  31. {
  32. CheckAddEntityID(entityID, descriptorType);
  33. Check.Require(entityID.groupID != 0, "invalid group detected, are you using new ExclusiveGroupStruct() instead of new ExclusiveGroup()?");
  34. var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd, componentsToBuild
  35. , implementors, descriptorType);
  36. return new EntityInitializer(entityID, dic);
  37. }
  38. ///--------------------------------------------
  39. void Preallocate<T>(ExclusiveGroupStruct groupID, uint size) where T : IEntityDescriptor, new()
  40. {
  41. using (var profiler = new PlatformProfiler("Preallocate"))
  42. {
  43. var entityComponentsToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild;
  44. var numberOfEntityComponents = entityComponentsToBuild.Length;
  45. FasterDictionary<RefWrapperType, ITypeSafeDictionary> group = GetOrCreateGroup(groupID, profiler);
  46. for (var index = 0; index < numberOfEntityComponents; index++)
  47. {
  48. var entityComponentBuilder = entityComponentsToBuild[index];
  49. var entityComponentType = entityComponentBuilder.GetEntityComponentType();
  50. var refWrapper = new RefWrapperType(entityComponentType);
  51. if (group.TryGetValue(refWrapper, out var dbList) == false)
  52. group[refWrapper] = entityComponentBuilder.Preallocate(ref dbList, size);
  53. else
  54. dbList.SetCapacity(size);
  55. if (_groupsPerEntity.TryGetValue(refWrapper, out var groupedGroup) == false)
  56. groupedGroup = _groupsPerEntity[refWrapper] = new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();
  57. groupedGroup[groupID] = dbList;
  58. }
  59. }
  60. }
  61. ///--------------------------------------------
  62. ///
  63. void MoveEntityFromAndToEngines(IComponentBuilder[] componentBuilders, EGID fromEntityGID, EGID? toEntityGID)
  64. {
  65. using (var sampler = new PlatformProfiler("Move Entity From Engines"))
  66. {
  67. var fromGroup = GetGroup(fromEntityGID.groupID);
  68. //Check if there is an EntityInfo linked to this entity, if so it's a DynamicEntityDescriptor!
  69. if (fromGroup.TryGetValue(new RefWrapperType(ComponentBuilderUtilities.ENTITY_INFO_COMPONENT)
  70. , out var entityInfoDic)
  71. && (entityInfoDic as ITypeSafeDictionary<EntityInfoComponent>).TryGetValue(
  72. fromEntityGID.entityID, out var entityInfo))
  73. SwapOrRemoveEntityComponents(fromEntityGID, toEntityGID, entityInfo.componentsToBuild, fromGroup
  74. , sampler);
  75. //otherwise it's a normal static entity descriptor
  76. else
  77. SwapOrRemoveEntityComponents(fromEntityGID, toEntityGID, componentBuilders, fromGroup, sampler);
  78. }
  79. }
  80. void SwapOrRemoveEntityComponents(EGID fromEntityGID, EGID? toEntityGID, IComponentBuilder[] entitiesToMove
  81. , FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup, in PlatformProfiler sampler)
  82. {
  83. using (sampler.Sample("MoveEntityComponents"))
  84. {
  85. var length = entitiesToMove.Length;
  86. FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup = null;
  87. //Swap is not like adding a new entity. While adding new entities happen at the end of submission
  88. //Adding an entity to a group due to a swap of groups happens now.
  89. if (toEntityGID != null)
  90. {
  91. var toGroupID = toEntityGID.Value.groupID;
  92. toGroup = GetOrCreateGroup(toGroupID, sampler);
  93. //Add all the entities to the dictionary
  94. for (var i = 0; i < length; i++)
  95. CopyEntityToDictionary(fromEntityGID, toEntityGID.Value, fromGroup, toGroup
  96. , entitiesToMove[i].GetEntityComponentType(), sampler);
  97. }
  98. //call all the callbacks
  99. for (var i = 0; i < length; i++)
  100. ExecuteEnginesSwapOrRemoveCallbacks(fromEntityGID, toEntityGID, fromGroup, toGroup
  101. , entitiesToMove[i].GetEntityComponentType(), sampler);
  102. //then remove all the entities from the dictionary
  103. for (var i = 0; i < length; i++)
  104. RemoveEntityFromDictionary(fromEntityGID, fromGroup, entitiesToMove[i].GetEntityComponentType(),
  105. sampler);
  106. }
  107. }
  108. void CopyEntityToDictionary
  109. (EGID entityGID, EGID toEntityGID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup
  110. , FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup, Type entityComponentType,
  111. in PlatformProfiler sampler)
  112. {
  113. using (sampler.Sample("CopyEntityToDictionary"))
  114. {
  115. var wrapper = new RefWrapperType(entityComponentType);
  116. ITypeSafeDictionary fromTypeSafeDictionary =
  117. GetTypeSafeDictionary(entityGID.groupID, fromGroup, wrapper);
  118. #if DEBUG && !PROFILE_SVELTO
  119. if (fromTypeSafeDictionary.Has(entityGID.entityID) == false)
  120. {
  121. throw new EntityNotFoundException(entityGID, entityComponentType);
  122. }
  123. #endif
  124. ITypeSafeDictionary toEntitiesDictionary =
  125. GetOrCreateTypeSafeDictionary(toEntityGID.groupID, toGroup, wrapper, fromTypeSafeDictionary);
  126. fromTypeSafeDictionary.AddEntityToDictionary(entityGID, toEntityGID, toEntitiesDictionary);
  127. }
  128. }
  129. void ExecuteEnginesSwapOrRemoveCallbacks
  130. (EGID entityGID, EGID? toEntityGID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup
  131. , FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup, Type entityComponentType
  132. , in PlatformProfiler profiler)
  133. {
  134. using (profiler.Sample("MoveEntityComponentFromAndToEngines"))
  135. {
  136. //add all the entities
  137. var refWrapper = new RefWrapperType(entityComponentType);
  138. var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper);
  139. ITypeSafeDictionary toEntitiesDictionary = null;
  140. if (toGroup != null)
  141. toEntitiesDictionary = toGroup[refWrapper]; //this is guaranteed to exist by AddEntityToDictionary
  142. #if DEBUG && !PROFILE_SVELTO
  143. if (fromTypeSafeDictionary.Has(entityGID.entityID) == false)
  144. throw new EntityNotFoundException(entityGID, entityComponentType);
  145. #endif
  146. fromTypeSafeDictionary.ExecuteEnginesSwapOrRemoveCallbacks(entityGID, toEntityGID, toEntitiesDictionary
  147. , toEntityGID == null ? _reactiveEnginesAddRemove : _reactiveEnginesSwap, in profiler);
  148. }
  149. }
  150. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  151. void RemoveEntityFromDictionary
  152. (EGID entityGID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup, Type entityComponentType
  153. , in PlatformProfiler sampler)
  154. {
  155. using (sampler.Sample("RemoveEntityFromDictionary"))
  156. {
  157. var refWrapper = new RefWrapperType(entityComponentType);
  158. var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper);
  159. fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID);
  160. }
  161. }
  162. /// <summary>
  163. /// Swap all the entities from one group to another
  164. ///
  165. /// TODO: write unit test that also tests that this calls MoveTo callbacks and not Add or Remove.
  166. /// also that the passing EGID is the same of a component with EGID
  167. /// </summary>
  168. /// <param name="fromIdGroupId"></param>
  169. /// <param name="toGroupId"></param>
  170. /// <param name="profiler"></param>
  171. void SwapEntitiesBetweenGroups(ExclusiveGroupStruct fromIdGroupId, ExclusiveGroupStruct toGroupId, in PlatformProfiler profiler)
  172. {
  173. using (profiler.Sample("SwapEntitiesBetweenGroups"))
  174. {
  175. FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup = GetGroup(fromIdGroupId);
  176. FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup = GetOrCreateGroup(toGroupId, profiler);
  177. foreach (var dictionaryOfEntities in fromGroup)
  178. {
  179. ITypeSafeDictionary toEntitiesDictionary =
  180. GetOrCreateTypeSafeDictionary(toGroupId, toGroup, dictionaryOfEntities.Key
  181. , dictionaryOfEntities.Value);
  182. var groupsOfEntityType = _groupsPerEntity[dictionaryOfEntities.Key];
  183. var groupOfEntitiesToCopyAndClear = groupsOfEntityType[fromIdGroupId];
  184. toEntitiesDictionary.AddEntitiesFromDictionary(groupOfEntitiesToCopyAndClear, toGroupId);
  185. //call all the MoveTo callbacks
  186. dictionaryOfEntities.Value.ExecuteEnginesAddOrSwapCallbacks(_reactiveEnginesSwap
  187. , dictionaryOfEntities.Value, new ExclusiveGroupStruct(fromIdGroupId), new ExclusiveGroupStruct(toGroupId), profiler);
  188. //todo: if it's unmanaged, I can use fastclear
  189. groupOfEntitiesToCopyAndClear.Clear();
  190. }
  191. }
  192. }
  193. FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetGroup(ExclusiveGroupStruct fromIdGroupId)
  194. {
  195. if (_groupEntityComponentsDB.TryGetValue(fromIdGroupId
  196. , out FasterDictionary<RefWrapperType, ITypeSafeDictionary>
  197. fromGroup) == false)
  198. throw new ECSException("Group doesn't exist: ".FastConcat(fromIdGroupId));
  199. return fromGroup;
  200. }
  201. FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetOrCreateGroup(ExclusiveGroupStruct toGroupId,
  202. in PlatformProfiler profiler)
  203. {
  204. using (profiler.Sample("GetOrCreateGroup"))
  205. {
  206. if (_groupEntityComponentsDB.TryGetValue(
  207. toGroupId, out FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup) == false)
  208. toGroup = _groupEntityComponentsDB[toGroupId] =
  209. new FasterDictionary<RefWrapperType, ITypeSafeDictionary>();
  210. return toGroup;
  211. }
  212. }
  213. ITypeSafeDictionary GetOrCreateTypeSafeDictionary
  214. (ExclusiveGroupStruct groupId, FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup, RefWrapperType type
  215. , ITypeSafeDictionary fromTypeSafeDictionary)
  216. {
  217. //be sure that the TypeSafeDictionary for the entity Type exists
  218. if (toGroup.TryGetValue(type, out ITypeSafeDictionary toEntitiesDictionary) == false)
  219. {
  220. toEntitiesDictionary = fromTypeSafeDictionary.Create();
  221. toGroup.Add(type, toEntitiesDictionary);
  222. }
  223. //update GroupsPerEntity
  224. if (_groupsPerEntity.TryGetValue(type, out var groupedGroup) == false)
  225. groupedGroup = _groupsPerEntity[type] = new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();
  226. groupedGroup[groupId] = toEntitiesDictionary;
  227. return toEntitiesDictionary;
  228. }
  229. static ITypeSafeDictionary GetTypeSafeDictionary
  230. (uint groupID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> @group, RefWrapperType refWrapper)
  231. {
  232. if (@group.TryGetValue(refWrapper, out ITypeSafeDictionary fromTypeSafeDictionary) == false)
  233. {
  234. throw new ECSException("no group found: ".FastConcat(groupID));
  235. }
  236. return fromTypeSafeDictionary;
  237. }
  238. void RemoveEntitiesFromGroup(ExclusiveGroupStruct groupID, in PlatformProfiler profiler)
  239. {
  240. if (_groupEntityComponentsDB.TryGetValue(groupID, out var dictionariesOfEntities))
  241. {
  242. foreach (var dictionaryOfEntities in dictionariesOfEntities)
  243. {
  244. dictionaryOfEntities.Value.ExecuteEnginesRemoveCallbacks(_reactiveEnginesAddRemove, profiler
  245. , new ExclusiveGroupStruct(groupID));
  246. dictionaryOfEntities.Value.FastClear();
  247. var groupsOfEntityType =
  248. _groupsPerEntity[dictionaryOfEntities.Key];
  249. groupsOfEntityType[groupID].FastClear();
  250. }
  251. }
  252. }
  253. //one datastructure rule them all:
  254. //split by group
  255. //split by type per group. It's possible to get all the entities of a give type T per group thanks
  256. //to the FasterDictionary capabilities OR it's possible to get a specific entityComponent indexed by
  257. //ID. This ID doesn't need to be the EGID, it can be just the entityID
  258. //for each group id, save a dictionary indexed by entity type of entities indexed by id
  259. // group EntityComponentType entityID, EntityComponent
  260. internal readonly FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>
  261. _groupEntityComponentsDB;
  262. //for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are
  263. //found indexed by group id. TypeSafeDictionary are never created, they instead point to the ones hold
  264. //by _groupEntityComponentsDB
  265. // <EntityComponentType <groupID <entityID, EntityComponent>>>
  266. internal readonly FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>>
  267. _groupsPerEntity;
  268. //The filters stored for each component and group
  269. internal readonly FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>>
  270. _groupFilters;
  271. readonly EntitiesDB _entitiesDB;
  272. EntitiesDB IUnitTestingInterface.entitiesForTesting => _entitiesDB;
  273. }
  274. public interface IUnitTestingInterface
  275. {
  276. EntitiesDB entitiesForTesting { get; }
  277. }
  278. }