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.

349 lines
17KB

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