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.

442 lines
22KB

  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using System.Threading;
  4. using Svelto.Common;
  5. using Svelto.DataStructures;
  6. using Svelto.DataStructures.Native;
  7. using Svelto.ECS.Internal;
  8. namespace Svelto.ECS
  9. {
  10. //this cannot be inside EntitiesDB otherwise it will cause hashing of reference in Burst
  11. public class Internal_FilterHelper
  12. {
  13. //since the user can choose their own filterID, in order to avoid collisions between
  14. //filters of the same type, the FilterContext is provided. The type is identified through
  15. //ComponentTypeID<T>
  16. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  17. internal static CombinedFilterComponentID CombineFilterIDWithComponentID<T>(CombinedFilterID combinedFilterID)
  18. where T : struct, _IInternalEntityComponent
  19. {
  20. return combinedFilterID.CombineComponent<T>();
  21. }
  22. }
  23. public partial class EntitiesDB
  24. {
  25. public SveltoFilters GetFilters()
  26. {
  27. return new SveltoFilters(
  28. _enginesRoot._persistentEntityFilters,
  29. _enginesRoot._indicesOfPersistentFiltersUsedByThisComponent,
  30. _enginesRoot._transientEntityFilters,
  31. _enginesRoot._indicesOfTransientFiltersUsedByThisComponent);
  32. }
  33. /// <summary>
  34. /// this whole structure is usable inside DOTS JOBS and BURST
  35. /// </summary>
  36. public readonly struct SveltoFilters
  37. {
  38. static readonly SharedStaticWrapper<int, Internal_FilterHelper> uniqueContextID;
  39. #if UNITY_BURST
  40. [Unity.Burst.BurstDiscard]
  41. //SharedStatic values must be initialized from not burstified code
  42. #endif
  43. public static FilterContextID GetNewContextID()
  44. {
  45. return new FilterContextID((ushort)Interlocked.Increment(ref uniqueContextID.Data));
  46. }
  47. internal SveltoFilters(SharedSveltoDictionaryNative<CombinedFilterComponentID, EntityFilterCollection> persistentEntityFilters,
  48. SharedSveltoDictionaryNative<ComponentID, NativeDynamicArrayCast<int>> indicesOfPersistentFiltersUsedByThisComponent,
  49. SharedSveltoDictionaryNative<CombinedFilterComponentID, EntityFilterCollection> transientEntityFilters,
  50. SharedSveltoDictionaryNative<ComponentID, NativeDynamicArrayCast<int>> indicesOfTransientFiltersUsedByThisComponent)
  51. {
  52. _persistentEntityFilters = persistentEntityFilters;
  53. _indicesOfPersistentFiltersUsedByThisComponent = indicesOfPersistentFiltersUsedByThisComponent;
  54. _transientEntityFilters = transientEntityFilters;
  55. _indicesOfTransientFiltersUsedByThisComponent = indicesOfTransientFiltersUsedByThisComponent;
  56. }
  57. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  58. public ref EntityFilterCollection GetOrCreatePersistentFilter<T>(int filterID, FilterContextID filterContextId)
  59. where T : struct, _IInternalEntityComponent
  60. {
  61. return ref GetOrCreatePersistentFilter<T>(new CombinedFilterID(filterID, filterContextId));
  62. }
  63. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  64. public ref EntityFilterCollection GetOrCreatePersistentFilter<T>(CombinedFilterID filterID) where T : struct, _IInternalEntityComponent
  65. {
  66. var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID<T>(filterID);
  67. if (_persistentEntityFilters.TryFindIndex(componentAndFilterID, out var index) == true)
  68. return ref _persistentEntityFilters.GetDirectValueByRef(index);
  69. _persistentEntityFilters.Add(componentAndFilterID, new EntityFilterCollection(filterID));
  70. var lastIndex = _persistentEntityFilters.count - 1;
  71. var componentId = ComponentTypeID<T>.id;
  72. if (_indicesOfPersistentFiltersUsedByThisComponent.TryFindIndex(componentId, out var getIndex) == false)
  73. {
  74. var newArray = new NativeDynamicArrayCast<int>(1, Allocator.Persistent);
  75. newArray.Add(lastIndex);
  76. _indicesOfPersistentFiltersUsedByThisComponent.Add(componentId, newArray);
  77. }
  78. else
  79. {
  80. ref var array = ref _indicesOfPersistentFiltersUsedByThisComponent.GetDirectValueByRef(getIndex);
  81. array.Add(lastIndex);
  82. }
  83. return ref _persistentEntityFilters.GetDirectValueByRef((uint)lastIndex);
  84. }
  85. /// <summary>
  86. /// Create a persistent filter. Persistent filters are not deleted after each submission,
  87. /// however they have a maintenance cost that must be taken into account and will affect
  88. /// entities submission performance.
  89. /// </summary>
  90. /// <typeparam name="T"></typeparam>
  91. /// <returns></returns>
  92. #if UNITY_BURST && UNITY_COLLECTIONS
  93. [Unity.Burst.BurstDiscard] //not burst compatible because of ComponentTypeID<T>.id and GetOrAdd callback;
  94. #endif
  95. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  96. public ref EntityFilterCollection CreatePersistentFilter<T>(CombinedFilterID filterID)
  97. where T : struct, _IInternalEntityComponent
  98. {
  99. var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID<T>(filterID);
  100. if (_persistentEntityFilters.TryFindIndex(componentAndFilterID, out var index) == true)
  101. throw new ECSException("filter already exists");
  102. var filterCollection = new EntityFilterCollection(filterID);
  103. _persistentEntityFilters.Add(componentAndFilterID, filterCollection);
  104. var lastIndex = _persistentEntityFilters.count - 1;
  105. _indicesOfPersistentFiltersUsedByThisComponent.GetOrAdd(ComponentTypeID<T>.id, _builder).Add(lastIndex);
  106. return ref _persistentEntityFilters.GetDirectValueByRef((uint)lastIndex);
  107. }
  108. static NativeDynamicArrayCast<int> Builder()
  109. {
  110. return new NativeDynamicArrayCast<int>(1, Allocator.Persistent);
  111. }
  112. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  113. public ref EntityFilterCollection GetPersistentFilter<T>(int filterID, FilterContextID filterContextId)
  114. where T : struct, _IInternalEntityComponent
  115. {
  116. return ref GetPersistentFilter<T>(new CombinedFilterID(filterID, filterContextId));
  117. }
  118. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  119. public ref EntityFilterCollection GetPersistentFilter<T>(CombinedFilterID filterID)
  120. where T : struct, _IInternalEntityComponent
  121. {
  122. var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID<T>(filterID);
  123. if (_persistentEntityFilters.TryFindIndex(componentAndFilterID, out var index) == true)
  124. return ref _persistentEntityFilters.GetDirectValueByRef(index);
  125. throw new ECSException("filter not found");
  126. }
  127. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  128. public bool TryGetPersistentFilter<T>(CombinedFilterID combinedFilterID,
  129. out EntityFilterCollection entityCollection)
  130. where T : struct, _IInternalEntityComponent
  131. {
  132. var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID<T>(combinedFilterID);
  133. if (_persistentEntityFilters.TryFindIndex(componentAndFilterID, out var index) == true)
  134. {
  135. entityCollection = _persistentEntityFilters.GetDirectValueByRef(index);
  136. return true;
  137. }
  138. entityCollection = default;
  139. return false;
  140. }
  141. /// <summary>
  142. /// Svelto.ECS tracks the filters linked to each
  143. /// component. This allows to iterate over all the filters of a given filter context linked to a component.
  144. /// </summary>
  145. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  146. public EntityFilterCollectionsEnumerator GetPersistentFilters<T>()
  147. where T : struct, _IInternalEntityComponent
  148. {
  149. if (_indicesOfPersistentFiltersUsedByThisComponent.TryFindIndex(ComponentTypeID<T>.id, out var index) == true)
  150. return new EntityFilterCollectionsEnumerator(
  151. _indicesOfPersistentFiltersUsedByThisComponent.GetDirectValueByRef(index),
  152. _persistentEntityFilters);
  153. throw new ECSException($"no filters associated with the type {TypeCache<T>.name}");
  154. }
  155. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  156. public EntityFilterCollectionsWithContextEnumerator GetPersistentFilters<T>(FilterContextID filterContextId)
  157. where T : struct, _IInternalEntityComponent
  158. {
  159. if (_indicesOfPersistentFiltersUsedByThisComponent.TryFindIndex(ComponentTypeID<T>.id, out var index) == true)
  160. return new EntityFilterCollectionsWithContextEnumerator(
  161. _indicesOfPersistentFiltersUsedByThisComponent.GetDirectValueByRef(index),
  162. _persistentEntityFilters, filterContextId);
  163. throw new ECSException($"no filters associated with the type {TypeCache<T>.name}");
  164. }
  165. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  166. public bool TryGetPersistentFilters<T>(FilterContextID filterContextId,
  167. out EntityFilterCollectionsWithContextEnumerator enumerator)
  168. where T : struct, _IInternalEntityComponent
  169. {
  170. if (_indicesOfPersistentFiltersUsedByThisComponent.TryFindIndex(ComponentTypeID<T>.id, out var index) == true)
  171. {
  172. ref var filterIndices = ref _indicesOfPersistentFiltersUsedByThisComponent.GetDirectValueByRef(index);
  173. enumerator = new EntityFilterCollectionsWithContextEnumerator(filterIndices, _persistentEntityFilters, filterContextId);
  174. return true;
  175. }
  176. enumerator = default;
  177. return false;
  178. }
  179. /// <summary>
  180. /// Creates a transient filter. Transient filters are deleted after each submission
  181. /// transient filters are identified by filterID and Context and can be linked to several groups.
  182. /// So for each group there can be as many as necessary transient filters with different ID and contextID
  183. /// </summary>
  184. /// <typeparam name="T"></typeparam>
  185. /// <returns></returns>
  186. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  187. public ref EntityFilterCollection GetOrCreateTransientFilter<T>(CombinedFilterID combinedFilterID, bool trackFilter = false)
  188. where T : struct, _IInternalEntityComponent
  189. {
  190. var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID<T>(combinedFilterID);
  191. if (_transientEntityFilters.TryFindIndex(componentAndFilterID, out var index))
  192. return ref _transientEntityFilters.GetDirectValueByRef(index);
  193. return ref InternalCreateTransientFilter<T>(combinedFilterID, componentAndFilterID, trackFilter);
  194. }
  195. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  196. public ref EntityFilterCollection GetOrCreateTransientFilter<T>(int filterID, FilterContextID filterContextId)
  197. where T : struct, _IInternalEntityComponent
  198. {
  199. return ref GetOrCreateTransientFilter<T>(new CombinedFilterID(filterID, filterContextId));
  200. }
  201. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  202. public ref EntityFilterCollection CreateTransientFilter<T>(CombinedFilterID combinedFilterID, bool trackFilter = false)
  203. where T : struct, _IInternalEntityComponent
  204. {
  205. CombinedFilterComponentID componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID<T>(combinedFilterID);
  206. #if DEBUG && !PROFILE_SVELTO
  207. if (_transientEntityFilters.TryFindIndex(componentAndFilterID, out _))
  208. throw new ECSException($"filter already exists {TypeCache<T>.name}");
  209. #endif
  210. return ref InternalCreateTransientFilter<T>(combinedFilterID, componentAndFilterID, trackFilter);
  211. }
  212. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  213. public bool TryGetTransientFilter<T>(CombinedFilterID filterID, out EntityFilterCollection entityCollection)
  214. where T : struct, _IInternalEntityComponent
  215. {
  216. var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID<T>(filterID);
  217. if (_transientEntityFilters.TryFindIndex(componentAndFilterID, out var index))
  218. {
  219. entityCollection = _transientEntityFilters.GetDirectValueByRef(index);
  220. return true;
  221. }
  222. entityCollection = default;
  223. return false;
  224. }
  225. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  226. public ref EntityFilterCollection GetTransientFilter<T>(CombinedFilterID filterID)
  227. where T : struct, _IInternalEntityComponent
  228. {
  229. var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID<T>(filterID);
  230. if (_transientEntityFilters.TryFindIndex(componentAndFilterID, out var index))
  231. {
  232. return ref _transientEntityFilters.GetDirectValueByRef(index);
  233. }
  234. throw new ECSException($"no filters associated with the type {TypeCache<T>.name}");
  235. }
  236. /// <summary>
  237. /// Svelto.ECS tracks the filters linked to each
  238. /// component. This allows to iterate over all the filters of a given filter context linked to a component.
  239. /// </summary>
  240. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  241. public EntityFilterCollectionsEnumerator GetTransientFilters<T>()
  242. where T : struct, _IInternalEntityComponent
  243. {
  244. if (_indicesOfTransientFiltersUsedByThisComponent.TryFindIndex(ComponentTypeID<T>.id, out var index) == true)
  245. return new EntityFilterCollectionsEnumerator(
  246. _indicesOfTransientFiltersUsedByThisComponent.GetDirectValueByRef(index),
  247. _transientEntityFilters);
  248. throw new ECSException($"no filters associated with the type {TypeCache<T>.name}");
  249. }
  250. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  251. public EntityFilterCollectionsWithContextEnumerator GetTransientFilters<T>(FilterContextID filterContextId)
  252. where T : struct, _IInternalEntityComponent
  253. {
  254. if (_indicesOfTransientFiltersUsedByThisComponent.TryFindIndex(ComponentTypeID<T>.id, out var index) == true)
  255. return new EntityFilterCollectionsWithContextEnumerator(
  256. _indicesOfTransientFiltersUsedByThisComponent.GetDirectValueByRef(index),
  257. _transientEntityFilters, filterContextId);
  258. throw new ECSException($"no filters associated with the type {TypeCache<T>.name}");
  259. }
  260. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  261. public bool TryGetTransientFilters<T>(FilterContextID filterContextId, out EntityFilterCollectionsWithContextEnumerator enumerator)
  262. where T : struct, _IInternalEntityComponent
  263. {
  264. if (_indicesOfTransientFiltersUsedByThisComponent.TryFindIndex(ComponentTypeID<T>.id, out var index) == true)
  265. {
  266. enumerator = new EntityFilterCollectionsWithContextEnumerator(
  267. _indicesOfTransientFiltersUsedByThisComponent.GetDirectValueByRef(index),
  268. _transientEntityFilters, filterContextId);
  269. return true;
  270. }
  271. enumerator = default;
  272. return false;
  273. }
  274. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  275. ref EntityFilterCollection InternalCreateTransientFilter<T>(CombinedFilterID filterID, CombinedFilterComponentID componentAndFilterID,
  276. bool trackFilter)
  277. where T : struct, _IInternalEntityComponent
  278. {
  279. var filterCollection = new EntityFilterCollection(filterID);
  280. _transientEntityFilters.Add(componentAndFilterID, filterCollection);
  281. if (trackFilter)
  282. {
  283. var typeRef = ComponentTypeID<T>.id;
  284. var lastIndex = _transientEntityFilters.count - 1;
  285. _indicesOfTransientFiltersUsedByThisComponent.GetOrAdd(ComponentTypeID<T>.id, _builder).Add(lastIndex);
  286. }
  287. return ref _transientEntityFilters.GetDirectValueByRef((uint)(_transientEntityFilters.count - 1));
  288. }
  289. public struct EntityFilterCollectionsEnumerator
  290. {
  291. internal EntityFilterCollectionsEnumerator(NativeDynamicArrayCast<int> getDirectValueByRef,
  292. SharedSveltoDictionaryNative<CombinedFilterComponentID, EntityFilterCollection> sharedSveltoDictionaryNative): this()
  293. {
  294. _getDirectValueByRef = getDirectValueByRef;
  295. _sharedSveltoDictionaryNative = sharedSveltoDictionaryNative;
  296. }
  297. public EntityFilterCollectionsEnumerator GetEnumerator()
  298. {
  299. return this;
  300. }
  301. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  302. public bool MoveNext()
  303. {
  304. if (_currentIndex < _getDirectValueByRef.count)
  305. {
  306. _currentIndex++;
  307. return true;
  308. }
  309. return false;
  310. }
  311. public ref EntityFilterCollection Current
  312. {
  313. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  314. get => ref _sharedSveltoDictionaryNative.GetDirectValueByRef((uint)_currentIndex - 1);
  315. }
  316. readonly NativeDynamicArrayCast<int> _getDirectValueByRef;
  317. readonly SharedSveltoDictionaryNative<CombinedFilterComponentID, EntityFilterCollection> _sharedSveltoDictionaryNative;
  318. int _currentIndex;
  319. }
  320. /// <summary>
  321. /// TODO: ABSOLUTELY UNIT TEST THIS AS THE CODE WAS WRONG!!!
  322. /// </summary>
  323. public struct EntityFilterCollectionsWithContextEnumerator
  324. {
  325. internal EntityFilterCollectionsWithContextEnumerator(NativeDynamicArrayCast<int> filterIndices,
  326. SharedSveltoDictionaryNative<CombinedFilterComponentID, EntityFilterCollection> sharedSveltoDictionaryNative,
  327. FilterContextID filterContextId): this()
  328. {
  329. _filterIndices = filterIndices;
  330. _sharedSveltoDictionaryNative = sharedSveltoDictionaryNative;
  331. _filterContextId = filterContextId;
  332. }
  333. public EntityFilterCollectionsWithContextEnumerator GetEnumerator()
  334. {
  335. return this;
  336. }
  337. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  338. public bool MoveNext()
  339. {
  340. while (_currentIndex++ < _filterIndices.count &&
  341. _sharedSveltoDictionaryNative.GetDirectValueByRef((uint)_filterIndices[(uint)_currentIndex - 1]).combinedFilterID
  342. .contextID.id != _filterContextId.id);
  343. if (_currentIndex - 1 < _filterIndices.count)
  344. return true;
  345. return false;
  346. }
  347. public ref EntityFilterCollection Current
  348. {
  349. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  350. get => ref _sharedSveltoDictionaryNative.GetDirectValueByRef((uint)_filterIndices[(uint)_currentIndex - 1]);
  351. }
  352. readonly NativeDynamicArrayCast<int> _filterIndices;
  353. readonly SharedSveltoDictionaryNative<CombinedFilterComponentID, EntityFilterCollection> _sharedSveltoDictionaryNative;
  354. readonly FilterContextID _filterContextId;
  355. int _currentIndex;
  356. }
  357. readonly SharedSveltoDictionaryNative<CombinedFilterComponentID, EntityFilterCollection> _persistentEntityFilters;
  358. readonly SharedSveltoDictionaryNative<ComponentID, NativeDynamicArrayCast<int>>
  359. _indicesOfPersistentFiltersUsedByThisComponent;
  360. readonly SharedSveltoDictionaryNative<CombinedFilterComponentID, EntityFilterCollection> _transientEntityFilters;
  361. readonly SharedSveltoDictionaryNative<ComponentID, NativeDynamicArrayCast<int>>
  362. _indicesOfTransientFiltersUsedByThisComponent;
  363. static readonly Func<NativeDynamicArrayCast<int>> _builder = Builder;
  364. }
  365. }
  366. }