Mirror of Svelto.ECS because we're a fan of it
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

567 行
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. //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. }