Mirror of Svelto.ECS because we're a fan of it
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

261 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. var group = dic.GetOrAdd(
  135. groupID, () => new FasterDictionary<ComponentID, ITypeSafeDictionary>());
  136. foreach (var componentBuilder in entityComponentsToBuild)
  137. {
  138. var safeDictionary = group.GetOrAdd(componentBuilder.getComponentID, () => componentBuilder.CreateDictionary(numberOfEntities));
  139. componentBuilder.Preallocate(safeDictionary, numberOfEntities);
  140. }
  141. }
  142. PreallocateDictionaries(currentComponentsToAddPerGroup);
  143. PreallocateDictionaries(lastComponentsToAddPerGroup);
  144. _currentNumberEntitiesCreatedPerGroup.GetOrAdd(groupID);
  145. _lastNumberEntitiesCreatedPerGroup.GetOrAdd(groupID);
  146. }
  147. internal void Swap()
  148. {
  149. Swap(ref currentComponentsToAddPerGroup, ref lastComponentsToAddPerGroup);
  150. Swap(ref _currentNumberEntitiesCreatedPerGroup, ref _lastNumberEntitiesCreatedPerGroup);
  151. }
  152. static void Swap<T>(ref T item1, ref T item2)
  153. {
  154. (item2, item1) = (item1, item2);
  155. }
  156. public OtherComponentsToAddPerGroupEnumerator GetEnumerator()
  157. {
  158. return new OtherComponentsToAddPerGroupEnumerator(lastComponentsToAddPerGroup
  159. , _lastNumberEntitiesCreatedPerGroup);
  160. }
  161. //Before I tried for the third time to use a SparseSet instead of FasterDictionary, remember that
  162. //while group indices are sequential, they may not be used in a sequential order. Sparseset needs
  163. //entities to be created sequentially (the index cannot be managed externally)
  164. internal FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, ITypeSafeDictionary>>
  165. currentComponentsToAddPerGroup;
  166. FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, ITypeSafeDictionary>>
  167. lastComponentsToAddPerGroup;
  168. /// <summary>
  169. /// To avoid extra allocation, I don't clear the groups, so I need an extra data structure
  170. /// to keep count of the number of entities built this frame. At the moment the actual number
  171. /// of entities built is not used
  172. /// </summary>
  173. FasterDictionary<ExclusiveGroupStruct, uint> _currentNumberEntitiesCreatedPerGroup;
  174. FasterDictionary<ExclusiveGroupStruct, uint> _lastNumberEntitiesCreatedPerGroup;
  175. //uint _totalEntitiesToAdd;
  176. }
  177. }
  178. struct OtherComponentsToAddPerGroupEnumerator
  179. {
  180. public OtherComponentsToAddPerGroupEnumerator
  181. (FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, ITypeSafeDictionary>>
  182. lastComponentsToAddPerGroup
  183. , FasterDictionary<ExclusiveGroupStruct, uint> otherNumberEntitiesCreatedPerGroup)
  184. {
  185. _lastComponentsToAddPerGroup = lastComponentsToAddPerGroup;
  186. _lastNumberEntitiesCreatedPerGroup = otherNumberEntitiesCreatedPerGroup.GetEnumerator();
  187. Current = default;
  188. }
  189. public bool MoveNext()
  190. {
  191. while (_lastNumberEntitiesCreatedPerGroup.MoveNext())
  192. {
  193. var current = _lastNumberEntitiesCreatedPerGroup.Current;
  194. if (current.value > 0) //there are entities in this group
  195. {
  196. var value = _lastComponentsToAddPerGroup[current.key];
  197. Current = new GroupInfo()
  198. {
  199. group = current.key
  200. , components = value
  201. };
  202. return true;
  203. }
  204. }
  205. return false;
  206. }
  207. public GroupInfo Current { get; private set; }
  208. //cannot be read only as they will be modified by MoveNext
  209. readonly FasterDictionary<ExclusiveGroupStruct, FasterDictionary<ComponentID, ITypeSafeDictionary>>
  210. _lastComponentsToAddPerGroup;
  211. SveltoDictionaryKeyValueEnumerator<ExclusiveGroupStruct, uint,
  212. ManagedStrategy<SveltoDictionaryNode<ExclusiveGroupStruct>>, ManagedStrategy<uint>,
  213. ManagedStrategy<int>>
  214. _lastNumberEntitiesCreatedPerGroup;
  215. }
  216. struct GroupInfo
  217. {
  218. public ExclusiveGroupStruct group;
  219. public FasterDictionary<ComponentID, ITypeSafeDictionary> components;
  220. }
  221. }