Mirror of Svelto.ECS because we're a fan of it
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

526 lignes
27KB

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