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.

534 lines
28KB

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