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.

EnginesRoot.Submission.cs 28KB

Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
4 years ago
Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
4 years ago
Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
4 years ago
Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using Svelto.Common;
  4. using Svelto.DataStructures;
  5. using Svelto.ECS.Internal;
  6. namespace Svelto.ECS
  7. {
  8. public partial class EnginesRoot
  9. {
  10. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  11. void SingleSubmission(PlatformProfiler profiler)
  12. {
  13. //clear the data checks before the submission. We want to allow structural changes inside the callbacks
  14. ClearChecksForMultipleOperationsOnTheSameEgid();
  15. _entitiesOperations.ExecuteRemoveAndSwappingOperations(
  16. _swapEntities,
  17. _removeEntities,
  18. _removeGroup,
  19. _swapGroup,
  20. this);
  21. AddEntities(profiler);
  22. //clear the data checks after the submission, so if structural changes happened inside the callback, the debug structure is reset for the next frame operations
  23. ClearChecksForMultipleOperationsOnTheSameEgid();
  24. }
  25. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  26. static void RemoveGroup(ExclusiveGroupStruct groupID, EnginesRoot enginesRoot)
  27. {
  28. using (var sampler = new PlatformProfiler("remove whole group"))
  29. {
  30. enginesRoot.RemoveEntitiesFromGroup(groupID, sampler);
  31. }
  32. }
  33. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  34. static void SwapGroup(ExclusiveGroupStruct fromGroupID, ExclusiveGroupStruct toGroupID, EnginesRoot enginesRoot)
  35. {
  36. using (var sampler = new PlatformProfiler("swap whole group"))
  37. {
  38. enginesRoot.SwapEntitiesBetweenGroups(fromGroupID, toGroupID, sampler);
  39. }
  40. }
  41. static void RemoveEntities(
  42. FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, FasterList<(uint, string)>>>
  43. removeOperations, FasterList<EGID> entitiesRemoved, EnginesRoot enginesRoot)
  44. {
  45. using (var sampler = new PlatformProfiler("remove Entities"))
  46. {
  47. using (sampler.Sample("Remove Entity References"))
  48. {
  49. var count = entitiesRemoved.count;
  50. for (int i = 0; i < count; i++)
  51. {
  52. enginesRoot._entityLocator.RemoveEntityReference(entitiesRemoved[i]);
  53. }
  54. }
  55. using (sampler.Sample("Execute remove callbacks and remove entities"))
  56. {
  57. foreach (var entitiesToRemove in removeOperations)
  58. {
  59. ExclusiveGroupStruct group = entitiesToRemove.key;
  60. var fromGroupDictionary = enginesRoot.GetDBGroup(group);
  61. foreach (var groupedEntitiesToRemove in entitiesToRemove.value)
  62. {
  63. var componentType = groupedEntitiesToRemove.key;
  64. ITypeSafeDictionary fromComponentsDictionary = fromGroupDictionary[componentType];
  65. FasterList<(uint, string)> infosToProcess = groupedEntitiesToRemove.value;
  66. fromComponentsDictionary.ExecuteEnginesRemoveCallbacks(
  67. infosToProcess,
  68. enginesRoot._reactiveEnginesRemove,
  69. group,
  70. in sampler);
  71. }
  72. }
  73. }
  74. using (sampler.Sample("Remove Entities"))
  75. {
  76. enginesRoot._cachedRangeOfSubmittedIndices.Clear();
  77. foreach (var entitiesToRemove in removeOperations)
  78. {
  79. ExclusiveGroupStruct fromGroup = entitiesToRemove.key;
  80. var fromGroupDictionary = enginesRoot.GetDBGroup(fromGroup);
  81. foreach (var groupedEntitiesToRemove in entitiesToRemove.value)
  82. {
  83. RefWrapperType componentType = groupedEntitiesToRemove.key;
  84. ITypeSafeDictionary fromComponentsDictionary = fromGroupDictionary[componentType];
  85. FasterList<(uint, string)> entityIDsToRemove = groupedEntitiesToRemove.value;
  86. enginesRoot._transientEntityIDsLeftAndAffectedByRemoval.Clear();
  87. fromComponentsDictionary.RemoveEntitiesFromDictionary(
  88. entityIDsToRemove,
  89. enginesRoot._transientEntityIDsLeftAndAffectedByRemoval);
  90. //important: remove from the filter must happen after remove from the dictionary
  91. //as we need to read the new indices linked to entities after the removal
  92. enginesRoot.RemoveEntitiesFromPersistentFilters(
  93. entityIDsToRemove,
  94. fromGroup,
  95. componentType,
  96. fromComponentsDictionary,
  97. enginesRoot._transientEntityIDsLeftAndAffectedByRemoval);
  98. //store new database count after the entities are removed from the datatabase, plus the number of entities removed
  99. enginesRoot._cachedRangeOfSubmittedIndices.Add(
  100. ((uint, uint))(fromComponentsDictionary.count,
  101. fromComponentsDictionary.count + entityIDsToRemove.count));
  102. }
  103. }
  104. }
  105. var rangeEnumerator = enginesRoot._cachedRangeOfSubmittedIndices.GetEnumerator();
  106. //Note, very important: This is exploiting a trick of the removal operation (RemoveEntitiesFromDictionary)
  107. //You may wonder: how can the remove callbacks iterate entities that have been just removed
  108. //from the database? This works just because during a remove, entities are put at the end of the
  109. //array and not actually removed. The entities are not iterated anymore in future just because
  110. //the count of the array decreases. This means that at the end of the array, after the remove
  111. //operations, we will find the collection of entities just removed. The remove callbacks are
  112. //going to iterate the array from the new count to the new count + the number of entities removed
  113. using (sampler.Sample("Execute remove Callbacks Fast"))
  114. {
  115. foreach (var entitiesToRemove in removeOperations)
  116. {
  117. ExclusiveGroupStruct group = entitiesToRemove.key;
  118. var fromGroupDictionary = enginesRoot.GetDBGroup(group);
  119. foreach (var groupedEntitiesToRemove in entitiesToRemove.value)
  120. {
  121. rangeEnumerator.MoveNext();
  122. var componentType = groupedEntitiesToRemove.key;
  123. ITypeSafeDictionary fromComponentsDictionary = fromGroupDictionary[componentType];
  124. //get all the engines linked to TValue
  125. if (!enginesRoot._reactiveEnginesRemoveEx.TryGetValue(
  126. componentType,
  127. out var entityComponentsEngines))
  128. continue;
  129. fromComponentsDictionary.ExecuteEnginesRemoveCallbacksFast(
  130. entityComponentsEngines,
  131. group,
  132. rangeEnumerator.Current,
  133. in sampler);
  134. }
  135. }
  136. }
  137. }
  138. }
  139. static void SwapEntities(FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType,
  140. FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>> swapEntitiesOperations,
  141. FasterList<(EGID, EGID)> entitiesIDSwaps, EnginesRoot enginesRoot)
  142. {
  143. using (var sampler = new PlatformProfiler("Swap entities between groups"))
  144. {
  145. using (sampler.Sample("Update Entity References"))
  146. {
  147. var count = entitiesIDSwaps.count;
  148. for (int i = 0; i < count; i++)
  149. {
  150. var (fromEntityGid, toEntityGid) = entitiesIDSwaps[i];
  151. enginesRoot._entityLocator.UpdateEntityReference(fromEntityGid, toEntityGid);
  152. }
  153. }
  154. using (sampler.Sample("Swap Entities"))
  155. {
  156. enginesRoot._cachedRangeOfSubmittedIndices.Clear();
  157. //Entities to swap are organised in order to minimise the amount of dictionary lookups.
  158. //swapEntitiesOperations iterations happen in the following order:
  159. //for each fromGroup, get all the entities to swap for each component type.
  160. //then get the list of IDs for each ToGroup.
  161. //now swap the set of FromGroup -> ToGroup entities per ID.
  162. foreach (var entitiesToSwap in swapEntitiesOperations)
  163. {
  164. ExclusiveGroupStruct fromGroup = entitiesToSwap.key;
  165. var fromGroupDictionary = enginesRoot.GetDBGroup(fromGroup);
  166. //iterate all the fromgroups
  167. foreach (var groupedEntitiesToSwap in entitiesToSwap.value)
  168. {
  169. var componentType = groupedEntitiesToSwap.key;
  170. ITypeSafeDictionary fromComponentsDictionaryDB = fromGroupDictionary[componentType];
  171. //get the subset of togroups that come from from group
  172. foreach (var entitiesInfoToSwap in groupedEntitiesToSwap.value)
  173. {
  174. ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key;
  175. ITypeSafeDictionary toComponentsDictionaryDB = enginesRoot.GetOrAddTypeSafeDictionary(
  176. toGroup,
  177. enginesRoot.GetOrAddDBGroup(toGroup),
  178. componentType,
  179. fromComponentsDictionaryDB);
  180. DBC.ECS.Check.Assert(
  181. toComponentsDictionaryDB != null,
  182. "something went wrong with the creation of dictionaries");
  183. //this list represents the set of entities that come from fromGroup and need
  184. //to be swapped to toGroup. Most of the times will be 1 of few units.
  185. FasterList<(uint, uint, string)> fromEntityToEntityIDs = entitiesInfoToSwap.value;
  186. //ensure that to dictionary has enough room to store the new entities
  187. toComponentsDictionaryDB.EnsureCapacity(
  188. (uint)(toComponentsDictionaryDB.count + (uint)fromEntityToEntityIDs.count));
  189. //fortunately swap means that entities are added at the end of each destination
  190. //dictionary list, so we can just iterate the list using the indices ranges added in the
  191. //_cachedIndices
  192. enginesRoot._cachedRangeOfSubmittedIndices.Add(
  193. ((uint, uint))(toComponentsDictionaryDB.count,
  194. toComponentsDictionaryDB.count + fromEntityToEntityIDs.count));
  195. enginesRoot._transientEntityIDsLeftAndAffectedByRemoval.Clear();
  196. fromComponentsDictionaryDB.SwapEntitiesBetweenDictionaries(
  197. fromEntityToEntityIDs,
  198. fromGroup,
  199. toGroup,
  200. toComponentsDictionaryDB,
  201. enginesRoot._transientEntityIDsLeftAndAffectedByRemoval);
  202. //important: this must happen after the entities are swapped in the database
  203. enginesRoot.SwapEntityBetweenPersistentFilters(
  204. fromEntityToEntityIDs,
  205. fromComponentsDictionaryDB,
  206. toComponentsDictionaryDB,
  207. fromGroup,
  208. toGroup,
  209. componentType,
  210. enginesRoot._transientEntityIDsLeftAndAffectedByRemoval);
  211. }
  212. }
  213. }
  214. }
  215. using (sampler.Sample("Execute Swap Callbacks"))
  216. {
  217. foreach (var entitiesToSwap in swapEntitiesOperations)
  218. {
  219. ExclusiveGroupStruct fromGroup = entitiesToSwap.key;
  220. foreach (var groupedEntitiesToSwap in entitiesToSwap.value)
  221. {
  222. var componentType = groupedEntitiesToSwap.key;
  223. //get all the engines linked to TValue
  224. if (!enginesRoot._reactiveEnginesSwap.TryGetValue(
  225. new RefWrapperType(componentType),
  226. out var entityComponentsEngines))
  227. continue;
  228. foreach (var entitiesInfoToSwap in groupedEntitiesToSwap.value)
  229. {
  230. ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key;
  231. ITypeSafeDictionary toComponentsDictionary = GetTypeSafeDictionary(
  232. toGroup,
  233. enginesRoot.GetDBGroup(toGroup),
  234. componentType);
  235. var infosToProcess = entitiesInfoToSwap.value;
  236. toComponentsDictionary.ExecuteEnginesSwapCallbacks(
  237. infosToProcess,
  238. entityComponentsEngines,
  239. fromGroup,
  240. toGroup,
  241. in sampler);
  242. }
  243. }
  244. }
  245. }
  246. var rangeEnumerator = enginesRoot._cachedRangeOfSubmittedIndices.GetEnumerator();
  247. using (sampler.Sample("Execute Swap Callbacks Fast"))
  248. {
  249. foreach (var entitiesToSwap in swapEntitiesOperations)
  250. {
  251. ExclusiveGroupStruct fromGroup = entitiesToSwap.key;
  252. foreach (var groupedEntitiesToSwap in entitiesToSwap.value)
  253. {
  254. var componentType = groupedEntitiesToSwap.key;
  255. foreach (var entitiesInfoToSwap in groupedEntitiesToSwap.value)
  256. {
  257. rangeEnumerator.MoveNext();
  258. //get all the engines linked to TValue
  259. if (!enginesRoot._reactiveEnginesSwapEx.TryGetValue(
  260. new RefWrapperType(componentType),
  261. out var entityComponentsEngines))
  262. continue;
  263. ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key;
  264. ITypeSafeDictionary toComponentsDictionary = GetTypeSafeDictionary(
  265. toGroup,
  266. enginesRoot.GetDBGroup(toGroup),
  267. componentType);
  268. toComponentsDictionary.ExecuteEnginesSwapCallbacksFast(
  269. entityComponentsEngines,
  270. fromGroup,
  271. toGroup,
  272. rangeEnumerator.Current,
  273. in sampler);
  274. }
  275. }
  276. }
  277. }
  278. }
  279. }
  280. void AddEntities(PlatformProfiler sampler)
  281. {
  282. //current buffer becomes other, and other becomes current
  283. _groupedEntityToAdd.Swap();
  284. //I need to iterate the previous current, which is now other
  285. if (_groupedEntityToAdd.AnyPreviousEntityCreated())
  286. {
  287. _cachedRangeOfSubmittedIndices.Clear();
  288. using (sampler.Sample("Add operations"))
  289. {
  290. try
  291. {
  292. using (sampler.Sample("Add entities to database"))
  293. {
  294. //each group is indexed by entity view type. for each type there is a dictionary indexed
  295. //by entityID
  296. foreach (var groupToSubmit in _groupedEntityToAdd)
  297. {
  298. var groupID = groupToSubmit.@group;
  299. var groupDB = GetOrAddDBGroup(groupID);
  300. //add the entityComponents in the group
  301. foreach (var entityComponentsToSubmit in groupToSubmit.components)
  302. {
  303. var type = entityComponentsToSubmit.key;
  304. var fromDictionary = entityComponentsToSubmit.value;
  305. var wrapper = new RefWrapperType(type);
  306. var toDictionary = GetOrAddTypeSafeDictionary(groupID, groupDB, wrapper, fromDictionary);
  307. //all the new entities are added at the end of each dictionary list, so we can
  308. //just iterate the list using the indices ranges added in the _cachedIndices
  309. _cachedRangeOfSubmittedIndices.Add(
  310. ((uint, uint))(toDictionary.count, toDictionary.count + fromDictionary.count));
  311. //Fill the DB with the entity components generated this frame.
  312. fromDictionary.AddEntitiesToDictionary(toDictionary, groupID
  313. #if SLOW_SVELTO_SUBMISSION
  314. , entityLocator
  315. #endif
  316. );
  317. }
  318. }
  319. }
  320. //then submit everything in the engines, so that the DB is up to date with all the entity components
  321. //created by the entity built
  322. var enumerator = _cachedRangeOfSubmittedIndices.GetEnumerator();
  323. using (sampler.Sample("Add entities to engines fast"))
  324. {
  325. foreach (GroupInfo groupToSubmit in _groupedEntityToAdd)
  326. {
  327. var groupID = groupToSubmit.@group;
  328. var groupDB = GetDBGroup(groupID);
  329. foreach (var entityComponentsToSubmit in groupToSubmit.components)
  330. {
  331. var type = entityComponentsToSubmit.key;
  332. var wrapper = new RefWrapperType(type);
  333. var toDictionary = GetTypeSafeDictionary(groupID, groupDB, wrapper);
  334. enumerator.MoveNext();
  335. toDictionary.ExecuteEnginesAddEntityCallbacksFast(_reactiveEnginesAddEx, groupID, enumerator.Current, in sampler);
  336. }
  337. }
  338. }
  339. //then submit everything in the engines, so that the DB is up to date with all the entity components
  340. //created by the entity built
  341. using (sampler.Sample("Add entities to engines"))
  342. {
  343. foreach (GroupInfo groupToSubmit in _groupedEntityToAdd)
  344. {
  345. var groupID = groupToSubmit.@group;
  346. var groupDB = GetDBGroup(groupID);
  347. //This loop iterates again all the entity components that have been just submitted to call
  348. //the Add Callbacks on them. Note that I am iterating the transient buffer of the just
  349. //added components, but calling the callback on the entities just added in the real buffer
  350. //Note: it's OK to add new entities while this happens because of the double buffer
  351. //design of the transient buffer of added entities.
  352. foreach (var entityComponentsToSubmit in groupToSubmit.components)
  353. {
  354. var type = entityComponentsToSubmit.key;
  355. var fromDictionary = entityComponentsToSubmit.value;
  356. //this contains the total number of components ever submitted in the DB
  357. ITypeSafeDictionary toDictionary = GetTypeSafeDictionary(groupID, groupDB, type);
  358. fromDictionary.ExecuteEnginesAddCallbacks(
  359. _reactiveEnginesAdd, toDictionary, groupID, in sampler);
  360. }
  361. }
  362. }
  363. }
  364. finally
  365. {
  366. using (sampler.Sample("clear double buffering"))
  367. {
  368. //other can be cleared now, but let's avoid deleting the dictionary every time
  369. _groupedEntityToAdd.ClearLastAddOperations();
  370. }
  371. }
  372. }
  373. }
  374. }
  375. bool HasMadeNewStructuralChangesInThisIteration()
  376. {
  377. return _groupedEntityToAdd.AnyEntityCreated() || _entitiesOperations.AnyOperationQueued();
  378. }
  379. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  380. void RemoveEntitiesFromGroup(ExclusiveGroupStruct groupID, in PlatformProfiler profiler)
  381. {
  382. _entityLocator.RemoveAllGroupReferenceLocators(groupID);
  383. if (_groupEntityComponentsDB.TryGetValue(groupID, out var dictionariesOfEntities))
  384. {
  385. foreach (var dictionaryOfEntities in dictionariesOfEntities)
  386. {
  387. //RemoveEX happens inside
  388. dictionaryOfEntities.value.ExecuteEnginesRemoveCallbacks_Group(
  389. _reactiveEnginesRemove, _reactiveEnginesRemoveEx, groupID, profiler);
  390. }
  391. foreach (var dictionaryOfEntities in dictionariesOfEntities)
  392. {
  393. dictionaryOfEntities.value.Clear();
  394. _groupsPerEntity[dictionaryOfEntities.key][groupID].Clear();
  395. }
  396. }
  397. }
  398. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  399. void SwapEntitiesBetweenGroups(ExclusiveGroupStruct fromGroupId, ExclusiveGroupStruct toGroupId, PlatformProfiler platformProfiler)
  400. {
  401. FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup = GetDBGroup(fromGroupId);
  402. FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup = GetOrAddDBGroup(toGroupId);
  403. _entityLocator.UpdateAllGroupReferenceLocators(fromGroupId, toGroupId);
  404. //remove entities from dictionaries
  405. foreach (var dictionaryOfEntities in fromGroup)
  406. {
  407. RefWrapperType refWrapperType = dictionaryOfEntities.key;
  408. ITypeSafeDictionary fromDictionary = dictionaryOfEntities.value;
  409. ITypeSafeDictionary toDictionary = GetOrAddTypeSafeDictionary(toGroupId, toGroup, refWrapperType, fromDictionary);
  410. fromDictionary.AddEntitiesToDictionary(toDictionary, toGroupId
  411. #if SLOW_SVELTO_SUBMISSION
  412. , entityLocator
  413. #endif
  414. );
  415. }
  416. //Call all the callbacks
  417. foreach (var dictionaryOfEntities in fromGroup)
  418. {
  419. RefWrapperType refWrapperType = dictionaryOfEntities.key;
  420. ITypeSafeDictionary fromDictionary = dictionaryOfEntities.value;
  421. ITypeSafeDictionary toDictionary = GetTypeSafeDictionary(toGroupId, toGroup, refWrapperType);
  422. //SwapEX happens inside
  423. fromDictionary.ExecuteEnginesSwapCallbacks_Group(_reactiveEnginesSwap, _reactiveEnginesSwapEx, toDictionary,
  424. fromGroupId, toGroupId, platformProfiler);
  425. }
  426. //remove entities from dictionaries
  427. foreach (var dictionaryOfEntities in fromGroup)
  428. {
  429. dictionaryOfEntities.value.Clear();
  430. _groupsPerEntity[dictionaryOfEntities.key][fromGroupId].Clear();
  431. }
  432. }
  433. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  434. ITypeSafeDictionary GetOrAddTypeSafeDictionary(ExclusiveGroupStruct groupId,
  435. FasterDictionary<RefWrapperType, ITypeSafeDictionary> groupPerComponentType, RefWrapperType type,
  436. ITypeSafeDictionary fromDictionary)
  437. {
  438. //be sure that the TypeSafeDictionary for the entity Type exists
  439. if (groupPerComponentType.TryGetValue(type, out ITypeSafeDictionary toEntitiesDictionary) == false)
  440. {
  441. toEntitiesDictionary = fromDictionary.Create();
  442. groupPerComponentType.Add(type, toEntitiesDictionary);
  443. }
  444. {
  445. //update GroupsPerEntity
  446. if (_groupsPerEntity.TryGetValue(type, out var groupedGroup) == false)
  447. groupedGroup = _groupsPerEntity[type] =
  448. new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();
  449. groupedGroup[groupId] = toEntitiesDictionary;
  450. }
  451. return toEntitiesDictionary;
  452. }
  453. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  454. static ITypeSafeDictionary GetTypeSafeDictionary(ExclusiveGroupStruct groupID,
  455. FasterDictionary<RefWrapperType, ITypeSafeDictionary> @group, RefWrapperType refWrapper)
  456. {
  457. if (@group.TryGetValue(refWrapper, out ITypeSafeDictionary fromTypeSafeDictionary) == false)
  458. {
  459. throw new ECSException("no group found: ".FastConcat(groupID.ToName()));
  460. }
  461. return fromTypeSafeDictionary;
  462. }
  463. readonly DoubleBufferedEntitiesToAdd _groupedEntityToAdd;
  464. readonly EntitiesOperations _entitiesOperations;
  465. //transient caches>>>>>>>>>>>>>>>>>>>>>
  466. readonly FasterList<(uint, uint)> _cachedRangeOfSubmittedIndices;
  467. readonly FasterDictionary<uint, int> _transientEntityIDsLeftWithoutDuplicates;
  468. readonly FasterList<uint> _transientEntityIDsLeftAndAffectedByRemoval;
  469. //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  470. static readonly
  471. Action<FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType,
  472. FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>>, FasterList<(EGID, EGID)>
  473. , EnginesRoot> _swapEntities;
  474. static readonly Action<
  475. FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, FasterList<(uint, string)>>>,
  476. FasterList<EGID>, EnginesRoot> _removeEntities;
  477. static readonly Action<ExclusiveGroupStruct, EnginesRoot> _removeGroup;
  478. static readonly Action<ExclusiveGroupStruct, ExclusiveGroupStruct, EnginesRoot> _swapGroup;
  479. }
  480. }