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.

265 lines
12KB

  1. using System;
  2. using Svelto.DataStructures;
  3. using Svelto.ECS.Internal;
  4. namespace Svelto.ECS
  5. {
  6. public partial class EnginesRoot
  7. {
  8. internal class DoubleBufferedEntitiesToAdd
  9. {
  10. //while caching is good to avoid over creating dictionaries that may be reused, the side effect
  11. //is that I have to iterate every time up to 100 dictionaries during the flushing of the build entities
  12. //even if there are 0 entities inside.
  13. const int MAX_NUMBER_OF_GROUPS_TO_CACHE = 100;
  14. const int MAX_NUMBER_OF_TYPES_PER_GROUP_TO_CACHE = 100;
  15. public DoubleBufferedEntitiesToAdd()
  16. {
  17. var entitiesCreatedPerGroupA = new FasterDictionary<ExclusiveGroupStruct, uint>();
  18. var entitiesCreatedPerGroupB = new FasterDictionary<ExclusiveGroupStruct, uint>();
  19. var entityComponentsToAddBufferA =
  20. new FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, ITypeSafeDictionary>>();
  21. var entityComponentsToAddBufferB =
  22. new FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, ITypeSafeDictionary>>();
  23. _currentNumberEntitiesCreatedPerGroup = entitiesCreatedPerGroupA;
  24. _lastNumberEntitiesCreatedPerGroup = entitiesCreatedPerGroupB;
  25. currentComponentsToAddPerGroup = entityComponentsToAddBufferA;
  26. lastComponentsToAddPerGroup = entityComponentsToAddBufferB;
  27. }
  28. public void ClearLastAddOperations()
  29. {
  30. var numberOfGroupsAddedSoFar = lastComponentsToAddPerGroup.count;
  31. var componentDictionariesPerType = lastComponentsToAddPerGroup.unsafeValues;
  32. //TODO: rewrite the caching logic with the new RecycleOrAdd dictionary functionality
  33. //I still do not want to cache too many groups
  34. //If we didn't create too many groups, we keep them alive, so we avoid the cost of creating new dictionaries
  35. //during future submissions, otherwise we clean up everything
  36. if (numberOfGroupsAddedSoFar > MAX_NUMBER_OF_GROUPS_TO_CACHE)
  37. {
  38. for (var i = 0; i < numberOfGroupsAddedSoFar; ++i)
  39. {
  40. var componentTypesCount = componentDictionariesPerType[i].count;
  41. var componentTypesDictionary = componentDictionariesPerType[i].unsafeValues;
  42. {
  43. for (var j = 0; j < componentTypesCount; ++j)
  44. //dictionaries of components may be native so they need to be disposed
  45. //before the references are GCed
  46. componentTypesDictionary[j].Dispose();
  47. }
  48. }
  49. //reset the number of entities created so far
  50. _lastNumberEntitiesCreatedPerGroup.Clear();
  51. lastComponentsToAddPerGroup.Clear();
  52. return;
  53. }
  54. for (var i = 0; i < numberOfGroupsAddedSoFar; ++i)
  55. {
  56. var componentTypesCount = componentDictionariesPerType[i].count;
  57. ITypeSafeDictionary[] componentTypesDictionary = componentDictionariesPerType[i].unsafeValues;
  58. for (var j = 0; j < componentTypesCount; ++j)
  59. //clear the dictionary of entities created so far (it won't allocate though)
  60. componentTypesDictionary[j].Clear();
  61. //if we didn't create too many component for this group, I reuse the component arrays
  62. if (componentTypesCount <= MAX_NUMBER_OF_TYPES_PER_GROUP_TO_CACHE)
  63. {
  64. for (var j = 0; j < componentTypesCount; ++j)
  65. componentTypesDictionary[j].Clear();
  66. }
  67. else
  68. {
  69. //here I have to dispose, because I am actually clearing the reference of the dictionary
  70. //with the next line.
  71. for (var j = 0; j < componentTypesCount; ++j)
  72. componentTypesDictionary[j].Dispose();
  73. componentDictionariesPerType[i].Clear();
  74. }
  75. }
  76. //reset the number of entities created so far
  77. _lastNumberEntitiesCreatedPerGroup.Clear();
  78. // _totalEntitiesToAdd = 0;
  79. }
  80. public void Dispose()
  81. {
  82. {
  83. var otherValuesArray = lastComponentsToAddPerGroup.unsafeValues;
  84. for (var i = 0; i < lastComponentsToAddPerGroup.count; ++i)
  85. {
  86. int safeDictionariesCount = otherValuesArray[i].count;
  87. ITypeSafeDictionary[] safeDictionaries = otherValuesArray[i].unsafeValues;
  88. //do not remove the dictionaries of entities per type created so far, they will be reused
  89. for (var j = 0; j < safeDictionariesCount; ++j)
  90. //clear the dictionary of entities create do far (it won't allocate though)
  91. safeDictionaries[j].Dispose();
  92. }
  93. }
  94. {
  95. var currentValuesArray = currentComponentsToAddPerGroup.unsafeValues;
  96. for (var i = 0; i < currentComponentsToAddPerGroup.count; ++i)
  97. {
  98. int safeDictionariesCount = currentValuesArray[i].count;
  99. ITypeSafeDictionary[] safeDictionaries = currentValuesArray[i].unsafeValues;
  100. //do not remove the dictionaries of entities per type created so far, they will be reused
  101. for (var j = 0; j < safeDictionariesCount; ++j)
  102. //clear the dictionary of entities create do far (it won't allocate though)
  103. safeDictionaries[j].Dispose();
  104. }
  105. }
  106. _currentNumberEntitiesCreatedPerGroup = null;
  107. _lastNumberEntitiesCreatedPerGroup = null;
  108. lastComponentsToAddPerGroup = null;
  109. currentComponentsToAddPerGroup = null;
  110. }
  111. internal bool AnyEntityCreated()
  112. {
  113. return _currentNumberEntitiesCreatedPerGroup.count > 0;
  114. }
  115. internal bool AnyPreviousEntityCreated()
  116. {
  117. return _lastNumberEntitiesCreatedPerGroup.count > 0;
  118. }
  119. internal void IncrementEntityCount(ExclusiveGroupStruct groupID)
  120. {
  121. _currentNumberEntitiesCreatedPerGroup.GetOrAdd(groupID)++;
  122. // _totalEntitiesToAdd++;
  123. }
  124. // public uint NumberOfEntitiesToAdd()
  125. // {
  126. // return _totalEntitiesToAdd;
  127. // }
  128. internal void Preallocate
  129. (ExclusiveGroupStruct groupID, uint numberOfEntities, IComponentBuilder[] entityComponentsToBuild)
  130. {
  131. void PreallocateDictionaries
  132. (FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, ITypeSafeDictionary>> dic)
  133. {
  134. //get the set of entities in the group ID
  135. var group = dic.GetOrAdd(
  136. groupID, () => new FasterDictionary<ComponentID, ITypeSafeDictionary>());
  137. //for each component of the entities in the group
  138. foreach (var componentBuilder in entityComponentsToBuild)
  139. {
  140. //get the dictionary of entities for the component type
  141. var components = group.GetOrAdd(componentBuilder.getComponentID, () => componentBuilder.CreateDictionary(numberOfEntities));
  142. componentBuilder.Preallocate(components, numberOfEntities);
  143. }
  144. }
  145. PreallocateDictionaries(currentComponentsToAddPerGroup);
  146. PreallocateDictionaries(lastComponentsToAddPerGroup);
  147. _currentNumberEntitiesCreatedPerGroup.GetOrAdd(groupID);
  148. _lastNumberEntitiesCreatedPerGroup.GetOrAdd(groupID);
  149. }
  150. internal void Swap()
  151. {
  152. Swap(ref currentComponentsToAddPerGroup, ref lastComponentsToAddPerGroup);
  153. Swap(ref _currentNumberEntitiesCreatedPerGroup, ref _lastNumberEntitiesCreatedPerGroup);
  154. }
  155. static void Swap<T>(ref T item1, ref T item2)
  156. {
  157. (item2, item1) = (item1, item2);
  158. }
  159. public OtherComponentsToAddPerGroupEnumerator GetEnumerator()
  160. {
  161. return new OtherComponentsToAddPerGroupEnumerator(lastComponentsToAddPerGroup
  162. , _lastNumberEntitiesCreatedPerGroup);
  163. }
  164. //Before I tried for the third time to use a SparseSet instead of FasterDictionary, remember that
  165. //while group indices are sequential, they may not be used in a sequential order. Sparseset needs
  166. //entities to be created sequentially (the index cannot be managed externally)
  167. internal FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, ITypeSafeDictionary>>
  168. currentComponentsToAddPerGroup;
  169. FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, ITypeSafeDictionary>>
  170. lastComponentsToAddPerGroup;
  171. /// <summary>
  172. /// To avoid extra allocation, I don't clear the groups, so I need an extra data structure
  173. /// to keep count of the number of entities built this frame. At the moment the actual number
  174. /// of entities built is not used
  175. /// </summary>
  176. FasterDictionary<ExclusiveGroupStruct, uint> _currentNumberEntitiesCreatedPerGroup;
  177. FasterDictionary<ExclusiveGroupStruct, uint> _lastNumberEntitiesCreatedPerGroup;
  178. //uint _totalEntitiesToAdd;
  179. }
  180. }
  181. struct OtherComponentsToAddPerGroupEnumerator
  182. {
  183. public OtherComponentsToAddPerGroupEnumerator
  184. (FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, ITypeSafeDictionary>>
  185. lastComponentsToAddPerGroup
  186. , FasterDictionary<ExclusiveGroupStruct, uint> otherNumberEntitiesCreatedPerGroup)
  187. {
  188. _lastComponentsToAddPerGroup = lastComponentsToAddPerGroup;
  189. _lastNumberEntitiesCreatedPerGroup = otherNumberEntitiesCreatedPerGroup.GetEnumerator();
  190. Current = default;
  191. }
  192. public bool MoveNext()
  193. {
  194. while (_lastNumberEntitiesCreatedPerGroup.MoveNext())
  195. {
  196. var current = _lastNumberEntitiesCreatedPerGroup.Current;
  197. if (current.value > 0) //there are entities in this group
  198. {
  199. var value = _lastComponentsToAddPerGroup[current.key];
  200. Current = new GroupInfo()
  201. {
  202. group = current.key
  203. , components = value
  204. };
  205. return true;
  206. }
  207. }
  208. return false;
  209. }
  210. public GroupInfo Current { get; private set; }
  211. //cannot be read only as they will be modified by MoveNext
  212. readonly FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, ITypeSafeDictionary>>
  213. _lastComponentsToAddPerGroup;
  214. SveltoDictionaryKeyValueEnumerator<ExclusiveGroupStruct, uint,
  215. ManagedStrategy<SveltoDictionaryNode<ExclusiveGroupStruct>>, ManagedStrategy<uint>,
  216. ManagedStrategy<int>>
  217. _lastNumberEntitiesCreatedPerGroup;
  218. }
  219. struct GroupInfo
  220. {
  221. public ExclusiveGroupStruct group;
  222. public FasterDictionary<ComponentID, ITypeSafeDictionary> components;
  223. }
  224. }