Mirror of Svelto.ECS because we're a fan of it
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

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