using System; using System.Runtime.CompilerServices; using System.Threading; using Svelto.Common; using Svelto.DataStructures; using Svelto.DataStructures.Native; using Svelto.ECS.Internal; namespace Svelto.ECS { //this cannot be inside EntitiesDB otherwise it will cause hashing of reference in Burst public class Internal_FilterHelper { //since the user can choose their own filterID, in order to avoid collisions between //filters of the same type, the FilterContext is provided. The type is identified through //ComponentTypeID [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static CombinedFilterComponentID CombineFilterIDWithComponentID(CombinedFilterID combinedFilterID) where T : struct, _IInternalEntityComponent { return combinedFilterID.CombineComponent(); } } public partial class EntitiesDB { public SveltoFilters GetFilters() { return new SveltoFilters( _enginesRoot._persistentEntityFilters, _enginesRoot._indicesOfPersistentFiltersUsedByThisComponent, _enginesRoot._transientEntityFilters, _enginesRoot._indicesOfTransientFiltersUsedByThisComponent); } /// /// this whole structure is usable inside DOTS JOBS and BURST /// public readonly struct SveltoFilters { static readonly SharedStaticWrapper uniqueContextID; #if UNITY_BURST [Unity.Burst.BurstDiscard] //SharedStatic values must be initialized from not burstified code #endif public static FilterContextID GetNewContextID() { return new FilterContextID((ushort)Interlocked.Increment(ref uniqueContextID.Data)); } internal SveltoFilters(SharedSveltoDictionaryNative persistentEntityFilters, SharedSveltoDictionaryNative> indicesOfPersistentFiltersUsedByThisComponent, SharedSveltoDictionaryNative transientEntityFilters, SharedSveltoDictionaryNative> indicesOfTransientFiltersUsedByThisComponent) { _persistentEntityFilters = persistentEntityFilters; _indicesOfPersistentFiltersUsedByThisComponent = indicesOfPersistentFiltersUsedByThisComponent; _transientEntityFilters = transientEntityFilters; _indicesOfTransientFiltersUsedByThisComponent = indicesOfTransientFiltersUsedByThisComponent; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref EntityFilterCollection GetOrCreatePersistentFilter(int filterID, FilterContextID filterContextId) where T : struct, _IInternalEntityComponent { return ref GetOrCreatePersistentFilter(new CombinedFilterID(filterID, filterContextId)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref EntityFilterCollection GetOrCreatePersistentFilter(CombinedFilterID filterID) where T : struct, _IInternalEntityComponent { var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID(filterID); if (_persistentEntityFilters.TryFindIndex(componentAndFilterID, out var index) == true) return ref _persistentEntityFilters.GetDirectValueByRef(index); _persistentEntityFilters.Add(componentAndFilterID, new EntityFilterCollection(filterID)); var lastIndex = _persistentEntityFilters.count - 1; var componentId = ComponentTypeID.id; if (_indicesOfPersistentFiltersUsedByThisComponent.TryFindIndex(componentId, out var getIndex) == false) { var newArray = new NativeDynamicArrayCast(1, Allocator.Persistent); newArray.Add(lastIndex); _indicesOfPersistentFiltersUsedByThisComponent.Add(componentId, newArray); } else { ref var array = ref _indicesOfPersistentFiltersUsedByThisComponent.GetDirectValueByRef(getIndex); array.Add(lastIndex); } return ref _persistentEntityFilters.GetDirectValueByRef((uint)lastIndex); } /// /// Create a persistent filter. Persistent filters are not deleted after each submission, /// however they have a maintenance cost that must be taken into account and will affect /// entities submission performance. /// Persistent filters keep track of the entities group swaps and they are automatically updated accordingly. /// /// /// #if UNITY_BURST && UNITY_COLLECTIONS [Unity.Burst.BurstDiscard] //not burst compatible because of ComponentTypeID.id and GetOrAdd callback; #endif [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref EntityFilterCollection CreatePersistentFilter(CombinedFilterID filterID) where T : struct, _IInternalEntityComponent { var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID(filterID); if (_persistentEntityFilters.TryFindIndex(componentAndFilterID, out var index) == true) throw new ECSException("filter already exists"); var filterCollection = new EntityFilterCollection(filterID); _persistentEntityFilters.Add(componentAndFilterID, filterCollection); var lastIndex = _persistentEntityFilters.count - 1; _indicesOfPersistentFiltersUsedByThisComponent.GetOrAdd(ComponentTypeID.id, _builder).Add(lastIndex); return ref _persistentEntityFilters.GetDirectValueByRef((uint)lastIndex); } static NativeDynamicArrayCast Builder() { return new NativeDynamicArrayCast(1, Allocator.Persistent); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref EntityFilterCollection GetPersistentFilter(int filterID, FilterContextID filterContextId) where T : struct, _IInternalEntityComponent { return ref GetPersistentFilter(new CombinedFilterID(filterID, filterContextId)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref EntityFilterCollection GetPersistentFilter(CombinedFilterID filterID) where T : struct, _IInternalEntityComponent { var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID(filterID); if (_persistentEntityFilters.TryFindIndex(componentAndFilterID, out var index) == true) return ref _persistentEntityFilters.GetDirectValueByRef(index); throw new ECSException("filter not found"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetPersistentFilter(CombinedFilterID combinedFilterID, out EntityFilterCollection entityCollection) where T : struct, _IInternalEntityComponent { var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID(combinedFilterID); if (_persistentEntityFilters.TryFindIndex(componentAndFilterID, out var index) == true) { entityCollection = _persistentEntityFilters.GetDirectValueByRef(index); return true; } entityCollection = default; return false; } /// /// Svelto.ECS tracks the filters linked to each /// component. This allows to iterate over all the filters of a given filter context linked to a component. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public EntityFilterCollectionsEnumerator GetPersistentFilters() where T : struct, _IInternalEntityComponent { if (_indicesOfPersistentFiltersUsedByThisComponent.TryFindIndex(ComponentTypeID.id, out var index) == true) return new EntityFilterCollectionsEnumerator( _indicesOfPersistentFiltersUsedByThisComponent.GetDirectValueByRef(index), _persistentEntityFilters); throw new ECSException($"no filters associated with the type {TypeCache.name}"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public EntityFilterCollectionsWithContextEnumerator GetPersistentFilters(FilterContextID filterContextId) where T : struct, _IInternalEntityComponent { if (_indicesOfPersistentFiltersUsedByThisComponent.TryFindIndex(ComponentTypeID.id, out var index) == true) return new EntityFilterCollectionsWithContextEnumerator( _indicesOfPersistentFiltersUsedByThisComponent.GetDirectValueByRef(index), _persistentEntityFilters, filterContextId); throw new ECSException($"no filters associated with the type {TypeCache.name}"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetPersistentFilters(FilterContextID filterContextId, out EntityFilterCollectionsWithContextEnumerator enumerator) where T : struct, _IInternalEntityComponent { if (_indicesOfPersistentFiltersUsedByThisComponent.TryFindIndex(ComponentTypeID.id, out var index) == true) { ref var filterIndices = ref _indicesOfPersistentFiltersUsedByThisComponent.GetDirectValueByRef(index); enumerator = new EntityFilterCollectionsWithContextEnumerator(filterIndices, _persistentEntityFilters, filterContextId); return true; } enumerator = default; return false; } /// /// Creates a transient filter. Transient filters are deleted after each submission /// transient filters are identified by filterID and Context and can be linked to several groups. /// So for each group there can be as many as necessary transient filters with different ID and contextID /// /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref EntityFilterCollection GetOrCreateTransientFilter(CombinedFilterID combinedFilterID, bool trackFilter = false) where T : struct, _IInternalEntityComponent { var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID(combinedFilterID); if (_transientEntityFilters.TryFindIndex(componentAndFilterID, out var index)) return ref _transientEntityFilters.GetDirectValueByRef(index); return ref InternalCreateTransientFilter(combinedFilterID, componentAndFilterID, trackFilter); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref EntityFilterCollection GetOrCreateTransientFilter(int filterID, FilterContextID filterContextId) where T : struct, _IInternalEntityComponent { return ref GetOrCreateTransientFilter(new CombinedFilterID(filterID, filterContextId)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref EntityFilterCollection CreateTransientFilter(CombinedFilterID combinedFilterID, bool trackFilter = false) where T : struct, _IInternalEntityComponent { CombinedFilterComponentID componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID(combinedFilterID); #if DEBUG && !PROFILE_SVELTO if (_transientEntityFilters.TryFindIndex(componentAndFilterID, out _)) throw new ECSException($"filter already exists {TypeCache.name}"); #endif return ref InternalCreateTransientFilter(combinedFilterID, componentAndFilterID, trackFilter); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetTransientFilter(CombinedFilterID filterID, out EntityFilterCollection entityCollection) where T : struct, _IInternalEntityComponent { var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID(filterID); if (_transientEntityFilters.TryFindIndex(componentAndFilterID, out var index)) { entityCollection = _transientEntityFilters.GetDirectValueByRef(index); return true; } entityCollection = default; return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref EntityFilterCollection GetTransientFilter(CombinedFilterID filterID) where T : struct, _IInternalEntityComponent { var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID(filterID); if (_transientEntityFilters.TryFindIndex(componentAndFilterID, out var index)) { return ref _transientEntityFilters.GetDirectValueByRef(index); } throw new ECSException($"no filters associated with the type {TypeCache.name}"); } /// /// Svelto.ECS tracks the filters linked to each /// component. This allows to iterate over all the filters of a given filter context linked to a component. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public EntityFilterCollectionsEnumerator GetTransientFilters() where T : struct, _IInternalEntityComponent { if (_indicesOfTransientFiltersUsedByThisComponent.TryFindIndex(ComponentTypeID.id, out var index) == true) return new EntityFilterCollectionsEnumerator( _indicesOfTransientFiltersUsedByThisComponent.GetDirectValueByRef(index), _transientEntityFilters); throw new ECSException($"no filters associated with the type {TypeCache.name}"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public EntityFilterCollectionsWithContextEnumerator GetTransientFilters(FilterContextID filterContextId) where T : struct, _IInternalEntityComponent { if (_indicesOfTransientFiltersUsedByThisComponent.TryFindIndex(ComponentTypeID.id, out var index) == true) return new EntityFilterCollectionsWithContextEnumerator( _indicesOfTransientFiltersUsedByThisComponent.GetDirectValueByRef(index), _transientEntityFilters, filterContextId); throw new ECSException($"no filters associated with the type {TypeCache.name}"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetTransientFilters(FilterContextID filterContextId, out EntityFilterCollectionsWithContextEnumerator enumerator) where T : struct, _IInternalEntityComponent { if (_indicesOfTransientFiltersUsedByThisComponent.TryFindIndex(ComponentTypeID.id, out var index) == true) { enumerator = new EntityFilterCollectionsWithContextEnumerator( _indicesOfTransientFiltersUsedByThisComponent.GetDirectValueByRef(index), _transientEntityFilters, filterContextId); return true; } enumerator = default; return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] ref EntityFilterCollection InternalCreateTransientFilter(CombinedFilterID filterID, CombinedFilterComponentID componentAndFilterID, bool trackFilter) where T : struct, _IInternalEntityComponent { var filterCollection = new EntityFilterCollection(filterID); _transientEntityFilters.Add(componentAndFilterID, filterCollection); if (trackFilter) { var typeRef = ComponentTypeID.id; var lastIndex = _transientEntityFilters.count - 1; _indicesOfTransientFiltersUsedByThisComponent.GetOrAdd(ComponentTypeID.id, _builder).Add(lastIndex); } return ref _transientEntityFilters.GetDirectValueByRef((uint)(_transientEntityFilters.count - 1)); } public struct EntityFilterCollectionsEnumerator { internal EntityFilterCollectionsEnumerator(NativeDynamicArrayCast getDirectValueByRef, SharedSveltoDictionaryNative sharedSveltoDictionaryNative): this() { _getDirectValueByRef = getDirectValueByRef; _sharedSveltoDictionaryNative = sharedSveltoDictionaryNative; } public EntityFilterCollectionsEnumerator GetEnumerator() { return this; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { if (_currentIndex < _getDirectValueByRef.count) { _currentIndex++; return true; } return false; } public ref EntityFilterCollection Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref _sharedSveltoDictionaryNative.GetDirectValueByRef((uint)_currentIndex - 1); } readonly NativeDynamicArrayCast _getDirectValueByRef; readonly SharedSveltoDictionaryNative _sharedSveltoDictionaryNative; int _currentIndex; } /// /// TODO: ABSOLUTELY UNIT TEST THIS AS THE CODE WAS WRONG!!! /// public struct EntityFilterCollectionsWithContextEnumerator { internal EntityFilterCollectionsWithContextEnumerator(NativeDynamicArrayCast filterIndices, SharedSveltoDictionaryNative sharedSveltoDictionaryNative, FilterContextID filterContextId): this() { _filterIndices = filterIndices; _sharedSveltoDictionaryNative = sharedSveltoDictionaryNative; _filterContextId = filterContextId; } public EntityFilterCollectionsWithContextEnumerator GetEnumerator() { return this; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { while (_currentIndex++ < _filterIndices.count && _sharedSveltoDictionaryNative.GetDirectValueByRef((uint)_filterIndices[(uint)_currentIndex - 1]).combinedFilterID .contextID.id != _filterContextId.id); if (_currentIndex - 1 < _filterIndices.count) return true; return false; } public ref EntityFilterCollection Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref _sharedSveltoDictionaryNative.GetDirectValueByRef((uint)_filterIndices[(uint)_currentIndex - 1]); } readonly NativeDynamicArrayCast _filterIndices; readonly SharedSveltoDictionaryNative _sharedSveltoDictionaryNative; readonly FilterContextID _filterContextId; int _currentIndex; } readonly SharedSveltoDictionaryNative _persistentEntityFilters; readonly SharedSveltoDictionaryNative> _indicesOfPersistentFiltersUsedByThisComponent; readonly SharedSveltoDictionaryNative _transientEntityFilters; readonly SharedSveltoDictionaryNative> _indicesOfTransientFiltersUsedByThisComponent; static readonly Func> _builder = Builder; } } }