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.

350 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((uint)entityID.groupID != 0
  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((uint) 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((uint) 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((uint) 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, (uint) 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, (uint) toGroupId, this);
  201. //call all the MoveTo callbacks
  202. dictionaryOfEntities.Value.ExecuteEnginesAddOrSwapCallbacks(_reactiveEnginesSwap
  203. , dictionaryOfEntities.Value, new ExclusiveGroupStruct(fromIdGroupId)
  204. , new ExclusiveGroupStruct(toGroupId), profiler);
  205. //todo: if it's unmanaged, I can use fastclear
  206. groupOfEntitiesToCopyAndClear.Clear();
  207. }
  208. }
  209. }
  210. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  211. FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetDBGroup(ExclusiveGroupStruct fromIdGroupId)
  212. {
  213. if (_groupEntityComponentsDB.TryGetValue(fromIdGroupId
  214. , out FasterDictionary<RefWrapperType, ITypeSafeDictionary>
  215. fromGroup) == false)
  216. throw new ECSException("Group doesn't exist: ".FastConcat((uint) fromIdGroupId));
  217. return fromGroup;
  218. }
  219. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  220. FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetOrCreateDBGroup
  221. (ExclusiveGroupStruct toGroupId)
  222. {
  223. return _groupEntityComponentsDB.GetOrCreate(
  224. toGroupId, () => new FasterDictionary<RefWrapperType, ITypeSafeDictionary>());
  225. }
  226. ITypeSafeDictionary GetOrCreateTypeSafeDictionary
  227. (ExclusiveGroupStruct groupId, FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup
  228. , RefWrapperType type, ITypeSafeDictionary fromTypeSafeDictionary)
  229. {
  230. //be sure that the TypeSafeDictionary for the entity Type exists
  231. if (toGroup.TryGetValue(type, out ITypeSafeDictionary toEntitiesDictionary) == false)
  232. {
  233. toEntitiesDictionary = fromTypeSafeDictionary.Create();
  234. toGroup.Add(type, toEntitiesDictionary);
  235. }
  236. //update GroupsPerEntity
  237. if (_groupsPerEntity.TryGetValue(type, out var groupedGroup) == false)
  238. groupedGroup = _groupsPerEntity[type] =
  239. new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();
  240. groupedGroup[groupId] = toEntitiesDictionary;
  241. return toEntitiesDictionary;
  242. }
  243. static ITypeSafeDictionary GetTypeSafeDictionary
  244. (uint groupID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> @group, RefWrapperType refWrapper)
  245. {
  246. if (@group.TryGetValue(refWrapper, out ITypeSafeDictionary fromTypeSafeDictionary) == false)
  247. {
  248. throw new ECSException("no group found: ".FastConcat(groupID));
  249. }
  250. return fromTypeSafeDictionary;
  251. }
  252. void RemoveEntitiesFromGroup(ExclusiveGroupStruct groupID, in PlatformProfiler profiler)
  253. {
  254. _entityLocator.RemoveAllGroupReferenceLocators(groupID);
  255. if (_groupEntityComponentsDB.TryGetValue(groupID, out var dictionariesOfEntities))
  256. {
  257. foreach (var dictionaryOfEntities in dictionariesOfEntities)
  258. {
  259. dictionaryOfEntities.Value.ExecuteEnginesRemoveCallbacks(_reactiveEnginesAddRemove, profiler
  260. , new ExclusiveGroupStruct(groupID));
  261. dictionaryOfEntities.Value.FastClear();
  262. var groupsOfEntityType = _groupsPerEntity[dictionaryOfEntities.Key];
  263. groupsOfEntityType[groupID].FastClear();
  264. }
  265. }
  266. }
  267. //one datastructure rule them all:
  268. //split by group
  269. //split by type per group. It's possible to get all the entities of a give type T per group thanks
  270. //to the FasterDictionary capabilities OR it's possible to get a specific entityComponent indexed by
  271. //ID. This ID doesn't need to be the EGID, it can be just the entityID
  272. //for each group id, save a dictionary indexed by entity type of entities indexed by id
  273. // group EntityComponentType entityID, EntityComponent
  274. internal readonly FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>
  275. _groupEntityComponentsDB;
  276. //for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are
  277. //found indexed by group id. TypeSafeDictionary are never created, they instead point to the ones hold
  278. //by _groupEntityComponentsDB
  279. // <EntityComponentType <groupID <entityID, EntityComponent>>>
  280. internal readonly FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>>
  281. _groupsPerEntity;
  282. //The filters stored for each component and group
  283. internal readonly FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>>
  284. _groupFilters;
  285. readonly EntitiesDB _entitiesDB;
  286. EntitiesDB IUnitTestingInterface.entitiesForTesting => _entitiesDB;
  287. }
  288. public interface IUnitTestingInterface
  289. {
  290. EntitiesDB entitiesForTesting { get; }
  291. }
  292. }