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.

515 lines
26KB

  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using Svelto.Common;
  4. using Svelto.DataStructures;
  5. using Svelto.ECS.DataStructures;
  6. using Svelto.ECS.Internal;
  7. namespace Svelto.ECS
  8. {
  9. public partial class EnginesRoot
  10. {
  11. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  12. void SingleSubmission(PlatformProfiler profiler)
  13. {
  14. ClearDebugChecks(); //this must be done first as I need the carry the last states after the submission
  15. _entitiesOperations.ExecuteRemoveAndSwappingOperations(_swapEntities, _removeEntities, _removeGroup,
  16. _swapGroup, this);
  17. AddEntities(profiler);
  18. }
  19. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  20. static void RemoveGroup(ExclusiveGroupStruct groupID, EnginesRoot enginesRoot)
  21. {
  22. using (var sampler = new PlatformProfiler("remove whole group"))
  23. {
  24. enginesRoot.RemoveEntitiesFromGroup(groupID, sampler);
  25. }
  26. }
  27. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  28. static void SwapGroup(ExclusiveGroupStruct fromGroupID, ExclusiveGroupStruct toGroupID, EnginesRoot enginesRoot)
  29. {
  30. using (var sampler = new PlatformProfiler("swap whole group"))
  31. {
  32. enginesRoot.SwapEntitiesBetweenGroups(fromGroupID, toGroupID, sampler);
  33. }
  34. }
  35. static void RemoveEntities(
  36. FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, FasterList<(uint, string)>>>
  37. removeOperations, FasterList<EGID> entitiesRemoved, EnginesRoot enginesRoot)
  38. {
  39. using (var sampler = new PlatformProfiler("remove Entities"))
  40. {
  41. using (sampler.Sample("Remove Entity References"))
  42. {
  43. var count = entitiesRemoved.count;
  44. for (int i = 0; i < count; i++)
  45. {
  46. enginesRoot._entityLocator.RemoveEntityReference(entitiesRemoved[i]);
  47. }
  48. }
  49. using (sampler.Sample("Execute remove callbacks and remove entities"))
  50. {
  51. foreach (var entitiesToRemove in removeOperations)
  52. {
  53. ExclusiveGroupStruct group = entitiesToRemove.key;
  54. var fromGroupDictionary = enginesRoot.GetDBGroup(group);
  55. foreach (var groupedEntitiesToRemove in entitiesToRemove.value)
  56. {
  57. var componentType = groupedEntitiesToRemove.key;
  58. ITypeSafeDictionary fromComponentsDictionary = fromGroupDictionary[componentType];
  59. FasterList<(uint, string)> infosToProcess = groupedEntitiesToRemove.value;
  60. fromComponentsDictionary.ExecuteEnginesRemoveCallbacks(infosToProcess,
  61. enginesRoot._reactiveEnginesRemove, group, in sampler);
  62. }
  63. }
  64. }
  65. using (sampler.Sample("Remove Entities"))
  66. {
  67. foreach (var entitiesToRemove in removeOperations)
  68. {
  69. ExclusiveGroupStruct fromGroup = entitiesToRemove.key;
  70. var fromGroupDictionary = enginesRoot.GetDBGroup(fromGroup);
  71. foreach (var groupedEntitiesToRemove in entitiesToRemove.value)
  72. {
  73. RefWrapperType componentType = groupedEntitiesToRemove.key;
  74. ITypeSafeDictionary fromComponentsDictionary = fromGroupDictionary[componentType];
  75. FasterList<(uint, string)> entityIDsToRemove = groupedEntitiesToRemove.value;
  76. enginesRoot.RemoveEntityFromPersistentFilters(entityIDsToRemove, fromGroup,
  77. componentType, fromComponentsDictionary);
  78. fromComponentsDictionary.RemoveEntitiesFromDictionary(entityIDsToRemove);
  79. //store new count after the entities are removed, plus the number of entities removed
  80. enginesRoot._cachedRangeOfSubmittedIndices.Add(((uint, uint))(
  81. fromComponentsDictionary.count,
  82. fromComponentsDictionary.count + entityIDsToRemove.count));
  83. }
  84. }
  85. }
  86. var rangeEnumerator = enginesRoot._cachedRangeOfSubmittedIndices.GetEnumerator();
  87. using (sampler.Sample("Execute remove Callbacks Fast"))
  88. {
  89. foreach (var entitiesToRemove in removeOperations)
  90. {
  91. ExclusiveGroupStruct group = entitiesToRemove.key;
  92. var fromGroupDictionary = enginesRoot.GetDBGroup(group);
  93. foreach (var groupedEntitiesToRemove in entitiesToRemove.value)
  94. {
  95. rangeEnumerator.MoveNext();
  96. var componentType = groupedEntitiesToRemove.key;
  97. ITypeSafeDictionary fromComponentsDictionary = fromGroupDictionary[componentType];
  98. //get all the engines linked to TValue
  99. if (!enginesRoot._reactiveEnginesRemoveEx.TryGetValue(new RefWrapperType(componentType),
  100. out var entityComponentsEngines))
  101. continue;
  102. fromComponentsDictionary.ExecuteEnginesRemoveCallbacksFast(entityComponentsEngines,
  103. group, rangeEnumerator.Current, in sampler);
  104. }
  105. }
  106. }
  107. }
  108. }
  109. static void SwapEntities(
  110. FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType,
  111. FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>> swapEntitiesOperations,
  112. FasterList<(EGID, EGID)> entitiesIDSwaps, EnginesRoot enginesRoot)
  113. {
  114. using (var sampler = new PlatformProfiler("Swap entities between groups"))
  115. {
  116. using (sampler.Sample("Update Entity References"))
  117. {
  118. var count = entitiesIDSwaps.count;
  119. for (int i = 0; i < count; i++)
  120. {
  121. var (fromEntityGid, toEntityGid) = entitiesIDSwaps[i];
  122. enginesRoot._entityLocator.UpdateEntityReference(fromEntityGid, toEntityGid);
  123. }
  124. }
  125. using (sampler.Sample("Swap Entities"))
  126. {
  127. enginesRoot._cachedRangeOfSubmittedIndices.FastClear();
  128. //Entities to swap are organised in order to minimise the amount of dictionary lookups.
  129. //swapEntitiesOperations iterations happen in the following order:
  130. //for each fromGroup, get all the entities to swap for each component type.
  131. //then get the list of IDs for each ToGroup.
  132. //now swap the set of FromGroup -> ToGroup entities per ID.
  133. foreach (var entitiesToSwap in swapEntitiesOperations)
  134. {
  135. ExclusiveGroupStruct fromGroup = entitiesToSwap.key;
  136. var fromGroupDictionary = enginesRoot.GetDBGroup(fromGroup);
  137. //iterate all the fromgroups
  138. foreach (var groupedEntitiesToSwap in entitiesToSwap.value)
  139. {
  140. var componentType = groupedEntitiesToSwap.key;
  141. ITypeSafeDictionary fromComponentsDictionary = fromGroupDictionary[componentType];
  142. //get the subset of togroups that come from from group
  143. foreach (var entitiesInfoToSwap in groupedEntitiesToSwap.value)
  144. {
  145. ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key;
  146. ITypeSafeDictionary toComponentsDictionary =
  147. enginesRoot.GetOrAddTypeSafeDictionary(toGroup,
  148. enginesRoot.GetOrAddDBGroup(toGroup), componentType, fromComponentsDictionary);
  149. DBC.ECS.Check.Assert(toComponentsDictionary != null,
  150. "something went wrong with the creation of dictionaries");
  151. //this list represents the set of entities that come from fromGroup and need
  152. //to be swapped to toGroup. Most of the times will be 1 of few units.
  153. FasterList<(uint, uint, string)> fromEntityToEntityIDs = entitiesInfoToSwap.value;
  154. int fromDictionaryCountBeforeSubmission = -1;
  155. if (enginesRoot._indicesOfPersistentFiltersUsedByThisComponent.TryGetValue(
  156. new NativeRefWrapperType(componentType),
  157. out NativeDynamicArrayCast<int> listOfFilters))
  158. {
  159. enginesRoot._cachedIndicesToSwapBeforeSubmissionForFilters.FastClear();
  160. fromDictionaryCountBeforeSubmission = fromComponentsDictionary.count - 1;
  161. //add the index of the entities in the component array for each entityID
  162. //BEFORE the submission, as after that the ID will be different
  163. foreach (var (fromEntityID, _, _) in fromEntityToEntityIDs)
  164. enginesRoot._cachedIndicesToSwapBeforeSubmissionForFilters.Add(fromEntityID,
  165. fromComponentsDictionary.GetIndex(fromEntityID));
  166. }
  167. //ensure that to dictionary has enough room to store the new entities`
  168. toComponentsDictionary.EnsureCapacity((uint)(toComponentsDictionary.count +
  169. (uint)fromEntityToEntityIDs.count));
  170. //fortunately swap means that entities are added at the end of each destination
  171. //dictionary list, so we can just iterate the list using the indices ranges added in the
  172. //_cachedIndices
  173. enginesRoot._cachedRangeOfSubmittedIndices.Add(((uint, uint))(
  174. toComponentsDictionary.count,
  175. toComponentsDictionary.count + fromEntityToEntityIDs.count));
  176. fromComponentsDictionary.SwapEntitiesBetweenDictionaries(fromEntityToEntityIDs,
  177. fromGroup, toGroup, toComponentsDictionary);
  178. if (fromDictionaryCountBeforeSubmission != -1) //this if skips the swap if there are no filters linked to the component
  179. enginesRoot.SwapEntityBetweenPersistentFilters(fromEntityToEntityIDs,
  180. enginesRoot._cachedIndicesToSwapBeforeSubmissionForFilters,
  181. toComponentsDictionary, fromGroup, toGroup,
  182. (uint)fromDictionaryCountBeforeSubmission, listOfFilters);
  183. }
  184. }
  185. }
  186. }
  187. using (sampler.Sample("Execute Swap Callbacks"))
  188. {
  189. foreach (var entitiesToSwap in swapEntitiesOperations)
  190. {
  191. ExclusiveGroupStruct fromGroup = entitiesToSwap.key;
  192. foreach (var groupedEntitiesToSwap in entitiesToSwap.value)
  193. {
  194. var componentType = groupedEntitiesToSwap.key;
  195. //get all the engines linked to TValue
  196. if (!enginesRoot._reactiveEnginesSwap.TryGetValue(new RefWrapperType(componentType),
  197. out var entityComponentsEngines))
  198. continue;
  199. foreach (var entitiesInfoToSwap in groupedEntitiesToSwap.value)
  200. {
  201. ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key;
  202. ITypeSafeDictionary toComponentsDictionary = GetTypeSafeDictionary(toGroup,
  203. enginesRoot.GetDBGroup(toGroup), componentType);
  204. var infosToProcess = entitiesInfoToSwap.value;
  205. toComponentsDictionary.ExecuteEnginesSwapCallbacks(infosToProcess,
  206. entityComponentsEngines, fromGroup, toGroup, in sampler);
  207. }
  208. }
  209. }
  210. }
  211. var rangeEnumerator = enginesRoot._cachedRangeOfSubmittedIndices.GetEnumerator();
  212. using (sampler.Sample("Execute Swap Callbacks Fast"))
  213. {
  214. foreach (var entitiesToSwap in swapEntitiesOperations)
  215. {
  216. ExclusiveGroupStruct fromGroup = entitiesToSwap.key;
  217. foreach (var groupedEntitiesToSwap in entitiesToSwap.value)
  218. {
  219. var componentType = groupedEntitiesToSwap.key;
  220. foreach (var entitiesInfoToSwap in groupedEntitiesToSwap.value)
  221. {
  222. rangeEnumerator.MoveNext();
  223. //get all the engines linked to TValue
  224. if (!enginesRoot._reactiveEnginesSwapEx.TryGetValue(new RefWrapperType(componentType),
  225. out var entityComponentsEngines))
  226. continue;
  227. ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key;
  228. ITypeSafeDictionary toComponentsDictionary = GetTypeSafeDictionary(toGroup,
  229. enginesRoot.GetDBGroup(toGroup), componentType);
  230. toComponentsDictionary.ExecuteEnginesSwapCallbacksFast(entityComponentsEngines,
  231. fromGroup, toGroup, rangeEnumerator.Current, in sampler);
  232. }
  233. }
  234. }
  235. }
  236. }
  237. }
  238. void AddEntities(PlatformProfiler sampler)
  239. {
  240. //current buffer becomes other, and other becomes current
  241. _groupedEntityToAdd.Swap();
  242. //I need to iterate the previous current, which is now other
  243. if (_groupedEntityToAdd.AnyPreviousEntityCreated())
  244. {
  245. _cachedRangeOfSubmittedIndices.FastClear();
  246. using (sampler.Sample("Add operations"))
  247. {
  248. try
  249. {
  250. using (sampler.Sample("Add entities to database"))
  251. {
  252. //each group is indexed by entity view type. for each type there is a dictionary indexed
  253. //by entityID
  254. foreach (var groupToSubmit in _groupedEntityToAdd)
  255. {
  256. var groupID = groupToSubmit.@group;
  257. var groupDB = GetOrAddDBGroup(groupID);
  258. //add the entityComponents in the group
  259. foreach (var entityComponentsToSubmit in groupToSubmit.components)
  260. {
  261. var type = entityComponentsToSubmit.key;
  262. var fromDictionary = entityComponentsToSubmit.value;
  263. var wrapper = new RefWrapperType(type);
  264. var toDictionary =
  265. GetOrAddTypeSafeDictionary(groupID, groupDB, wrapper, fromDictionary);
  266. //all the new entities are added at the end of each dictionary list, so we can
  267. //just iterate the list using the indices ranges added in the _cachedIndices
  268. _cachedRangeOfSubmittedIndices.Add(((uint, uint))(toDictionary.count,
  269. toDictionary.count + fromDictionary.count));
  270. //Fill the DB with the entity components generated this frame.
  271. fromDictionary.AddEntitiesToDictionary(toDictionary, groupID, entityLocator);
  272. }
  273. }
  274. }
  275. //then submit everything in the engines, so that the DB is up to date with all the entity components
  276. //created by the entity built
  277. var enumerator = _cachedRangeOfSubmittedIndices.GetEnumerator();
  278. using (sampler.Sample("Add entities to engines fast"))
  279. {
  280. foreach (GroupInfo groupToSubmit in _groupedEntityToAdd)
  281. {
  282. var groupID = groupToSubmit.@group;
  283. var groupDB = GetDBGroup(groupID);
  284. foreach (var entityComponentsToSubmit in groupToSubmit.components)
  285. {
  286. var type = entityComponentsToSubmit.key;
  287. var wrapper = new RefWrapperType(type);
  288. var toDictionary = GetTypeSafeDictionary(groupID, groupDB, wrapper);
  289. enumerator.MoveNext();
  290. toDictionary.ExecuteEnginesAddEntityCallbacksFast(_reactiveEnginesAddEx, groupID,
  291. enumerator.Current, in sampler);
  292. }
  293. }
  294. }
  295. //then submit everything in the engines, so that the DB is up to date with all the entity components
  296. //created by the entity built
  297. using (sampler.Sample("Add entities to engines"))
  298. {
  299. foreach (GroupInfo groupToSubmit in _groupedEntityToAdd)
  300. {
  301. var groupID = groupToSubmit.@group;
  302. var groupDB = GetDBGroup(groupID);
  303. //This loop iterates again all the entity components that have been just submitted to call
  304. //the Add Callbacks on them. Note that I am iterating the transient buffer of the just
  305. //added components, but calling the callback on the entities just added in the real buffer
  306. //Note: it's OK to add new entities while this happens because of the double buffer
  307. //design of the transient buffer of added entities.
  308. foreach (var entityComponentsToSubmit in groupToSubmit.components)
  309. {
  310. var type = entityComponentsToSubmit.key;
  311. var fromDictionary = entityComponentsToSubmit.value;
  312. //this contains the total number of components ever submitted in the DB
  313. ITypeSafeDictionary toDictionary = GetTypeSafeDictionary(groupID, groupDB, type);
  314. fromDictionary.ExecuteEnginesAddCallbacks(_reactiveEnginesAdd, toDictionary,
  315. groupID, in sampler);
  316. }
  317. }
  318. }
  319. }
  320. finally
  321. {
  322. using (sampler.Sample("clear double buffering"))
  323. {
  324. //other can be cleared now, but let's avoid deleting the dictionary every time
  325. _groupedEntityToAdd.ClearLastAddOperations();
  326. }
  327. }
  328. }
  329. }
  330. }
  331. bool HasMadeNewStructuralChangesInThisIteration()
  332. {
  333. return _groupedEntityToAdd.AnyEntityCreated() || _entitiesOperations.AnyOperationQueued();
  334. }
  335. void RemoveEntitiesFromGroup(ExclusiveGroupStruct groupID, in PlatformProfiler profiler)
  336. {
  337. _entityLocator.RemoveAllGroupReferenceLocators(groupID);
  338. if (_groupEntityComponentsDB.TryGetValue(groupID, out var dictionariesOfEntities))
  339. {
  340. foreach (var dictionaryOfEntities in dictionariesOfEntities)
  341. {
  342. //RemoveEX happens inside
  343. dictionaryOfEntities.value.ExecuteEnginesRemoveCallbacks_Group(_reactiveEnginesRemove,
  344. _reactiveEnginesRemoveEx, groupID, profiler);
  345. }
  346. foreach (var dictionaryOfEntities in dictionariesOfEntities)
  347. {
  348. dictionaryOfEntities.value.Clear();
  349. _groupsPerEntity[dictionaryOfEntities.key][groupID].Clear();
  350. }
  351. }
  352. }
  353. void SwapEntitiesBetweenGroups(ExclusiveGroupStruct fromGroupId, ExclusiveGroupStruct toGroupId,
  354. PlatformProfiler platformProfiler)
  355. {
  356. FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup = GetDBGroup(fromGroupId);
  357. FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup = GetOrAddDBGroup(toGroupId);
  358. _entityLocator.UpdateAllGroupReferenceLocators(fromGroupId, toGroupId);
  359. //remove entities from dictionaries
  360. foreach (var dictionaryOfEntities in fromGroup)
  361. {
  362. RefWrapperType refWrapperType = dictionaryOfEntities.key;
  363. ITypeSafeDictionary fromDictionary = dictionaryOfEntities.value;
  364. ITypeSafeDictionary toDictionary =
  365. GetOrAddTypeSafeDictionary(toGroupId, toGroup, refWrapperType, fromDictionary);
  366. fromDictionary.AddEntitiesToDictionary(toDictionary, toGroupId, this.entityLocator);
  367. }
  368. //Call all the callbacks
  369. foreach (var dictionaryOfEntities in fromGroup)
  370. {
  371. RefWrapperType refWrapperType = dictionaryOfEntities.key;
  372. ITypeSafeDictionary fromDictionary = dictionaryOfEntities.value;
  373. ITypeSafeDictionary toDictionary = GetTypeSafeDictionary(toGroupId, toGroup, refWrapperType);
  374. //SwapEX happens inside
  375. fromDictionary.ExecuteEnginesSwapCallbacks_Group(_reactiveEnginesSwap,
  376. _reactiveEnginesSwapEx, toDictionary, fromGroupId, toGroupId, platformProfiler);
  377. }
  378. //remove entities from dictionaries
  379. foreach (var dictionaryOfEntities in fromGroup)
  380. {
  381. dictionaryOfEntities.value.Clear();
  382. _groupsPerEntity[dictionaryOfEntities.key][fromGroupId].Clear();
  383. }
  384. }
  385. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  386. ITypeSafeDictionary GetOrAddTypeSafeDictionary(ExclusiveGroupStruct groupId,
  387. FasterDictionary<RefWrapperType, ITypeSafeDictionary> groupPerComponentType, RefWrapperType type,
  388. ITypeSafeDictionary fromDictionary)
  389. {
  390. //be sure that the TypeSafeDictionary for the entity Type exists
  391. if (groupPerComponentType.TryGetValue(type, out ITypeSafeDictionary toEntitiesDictionary) == false)
  392. {
  393. toEntitiesDictionary = fromDictionary.Create();
  394. groupPerComponentType.Add(type, toEntitiesDictionary);
  395. }
  396. {
  397. //update GroupsPerEntity
  398. if (_groupsPerEntity.TryGetValue(type, out var groupedGroup) == false)
  399. groupedGroup = _groupsPerEntity[type] =
  400. new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();
  401. groupedGroup[groupId] = toEntitiesDictionary;
  402. }
  403. return toEntitiesDictionary;
  404. }
  405. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  406. static ITypeSafeDictionary GetTypeSafeDictionary(ExclusiveGroupStruct groupID,
  407. FasterDictionary<RefWrapperType, ITypeSafeDictionary> @group, RefWrapperType refWrapper)
  408. {
  409. if (@group.TryGetValue(refWrapper, out ITypeSafeDictionary fromTypeSafeDictionary) == false)
  410. {
  411. throw new ECSException("no group found: ".FastConcat(groupID.ToName()));
  412. }
  413. return fromTypeSafeDictionary;
  414. }
  415. readonly DoubleBufferedEntitiesToAdd _groupedEntityToAdd;
  416. readonly EntitiesOperations _entitiesOperations;
  417. readonly FasterList<(uint, uint)> _cachedRangeOfSubmittedIndices;
  418. readonly FasterDictionary<uint, uint> _cachedIndicesToSwapBeforeSubmissionForFilters;
  419. static readonly
  420. Action<FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType,
  421. FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>>, FasterList<(EGID, EGID)>
  422. ,
  423. EnginesRoot> _swapEntities;
  424. static readonly Action<
  425. FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, FasterList<(uint, string)>>>,
  426. FasterList<EGID>, EnginesRoot> _removeEntities;
  427. static readonly Action<ExclusiveGroupStruct, EnginesRoot> _removeGroup;
  428. static readonly Action<ExclusiveGroupStruct, ExclusiveGroupStruct, EnginesRoot> _swapGroup;
  429. }
  430. }