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.

517 lines
27KB

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