@@ -1 +1 @@ | |||
Subproject commit d75300010cb12c0aa80e5f7a4c7c425c07f4bcf2 | |||
Subproject commit ff24942680a5e5d257541ed857a140e62525fe45 |
@@ -1,10 +1,29 @@ | |||
# Changelog | |||
All notable changes to this project will be documented in this file. Changes are listed in random order of importance. | |||
## [3.4.0] - 03-2023 | |||
~~~~ | |||
* removed static caches used in performance critical paths as they were causing unexpected performance issues (the fetching of static data is slower than i imagined) | |||
* add Native prefix in front of the native memory utilities method names | |||
* largely improved the console logger system | |||
* minor improvements to the platform profiler structs | |||
* improvements to the ThreadSafeObjectPool class (some refactoring too) | |||
* added several datastructures previously belonging to Svelto.ECS | |||
* all the FastClear methods are gone. The standard clear method now is aware of the type used and will clear it in the fastest way possible | |||
* MemClear is added in case memory needs to be cleared explicitly | |||
* added new SveltoStream, Unmanaged and Managed stream classes, their use case will be documented one day | |||
* renamed the Svelto.Common.DataStructures namespace to Svelto.DataStructures | |||
* added FixedTypedArray* methods. Fixed size arrays embedded in structs are now possible | |||
* FasterList extension to convert to Span and ByteSpan | |||
* Fix reported bugs | |||
* Minor Svelto Dictionary improvements | |||
* Added ValueContainer, a simple int, Tvalue dictionary based on sparse set. It has very specific use cases at the moment. Mainly to be used for the new ECS OOP Abstraction resoruce manager | |||
## [3.3.2] - 04-06-2022 | |||
* Internal refactoring to support future features. Currently it may translate to a small performance boost | |||
* IEntityComponent and IEntityViewComponent now implements IBaseEntityComponent. This shouldn't affect existing code | |||
* IEntityComponent and IEntityViewComponent now implements _IInternalEntityComponent. This shouldn't affect existing code | |||
* Improve thread-safety of entity building | |||
* Fixed serious bug that affected the integrity of the EntityIDs values during RemoveEX callbacks | |||
* The point above may result in a performance boost in the Filters updates during submission | |||
@@ -4,7 +4,6 @@ using System.Diagnostics; | |||
#endif | |||
using System; | |||
using System.Collections.Generic; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -86,9 +85,9 @@ namespace Svelto.ECS | |||
#if DONT_USE | |||
[Conditional("MEANINGLESS")] | |||
#endif | |||
void ClearDebugChecks() { _multipleOperationOnSameEGIDChecker.FastClear(); } | |||
void ClearDebugChecks() { _multipleOperationOnSameEGIDChecker.Clear(); } | |||
readonly FasterDictionary<EGID, uint> _multipleOperationOnSameEGIDChecker; | |||
readonly FasterDictionary<ExclusiveGroupStruct, HashSet<uint>> _idChecker; | |||
readonly Svelto.DataStructures.FasterDictionary<EGID, uint> _multipleOperationOnSameEGIDChecker; | |||
readonly Svelto.DataStructures.FasterDictionary<ExclusiveGroupStruct, HashSet<uint>> _idChecker; | |||
} | |||
} |
@@ -1,4 +1,4 @@ | |||
using System; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Reflection; | |||
using System.Threading; | |||
@@ -29,23 +29,30 @@ namespace Svelto.ECS | |||
public static int counter; | |||
} | |||
public class ComponentID<T> where T : struct, IBaseEntityComponent | |||
public class ComponentID<T> where T : struct, _IInternalEntityComponent | |||
{ | |||
public static readonly SharedStaticWrapper<int, ComponentID<T>> id; | |||
#if UNITY_BURST | |||
//todo: any reason to not do this? If I don't, I cannot Create filters in ready functions and | |||
//I have to remove the CreateFilter method | |||
static ComponentID() | |||
{ | |||
Init(); | |||
} | |||
#if UNITY_BURST | |||
[Unity.Burst.BurstDiscard] | |||
//SharedStatic values must be initialized from not burstified code | |||
#endif | |||
public static void Init() | |||
static void Init() | |||
{ | |||
id.Data = Interlocked.Increment(ref BurstCompatibleCounter.counter); | |||
DBC.ECS.Check.Ensure(id.Data < ushort.MaxValue, "too many types registered, HOW :)"); | |||
} | |||
} | |||
public class ComponentBuilder<T> : IComponentBuilder where T : struct, IBaseEntityComponent | |||
public class ComponentBuilder<T> : IComponentBuilder where T : struct, _IInternalEntityComponent | |||
{ | |||
internal static readonly Type ENTITY_COMPONENT_TYPE; | |||
internal static readonly bool IS_ENTITY_VIEW_COMPONENT; | |||
@@ -69,9 +76,8 @@ namespace Svelto.ECS | |||
SetEGIDWithoutBoxing<T>.Warmup(); | |||
#endif | |||
ComponentID<T>.Init(); | |||
ENTITY_COMPONENT_NAME = ENTITY_COMPONENT_TYPE.ToString(); | |||
IS_UNMANAGED = TypeType.isUnmanaged<T>(); //attention this is important as it serves as warm up for Type<T> | |||
IS_UNMANAGED = TypeCache<T>.isUnmanaged; //attention this is important as it serves as warm up for Type<T> | |||
#if UNITY_NATIVE | |||
if (IS_UNMANAGED) | |||
EntityComponentIDMap.Register<T>(new Filler<T>()); | |||
@@ -86,7 +92,7 @@ namespace Svelto.ECS | |||
else | |||
{ | |||
if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_INFO_COMPONENT && | |||
ENTITY_COMPONENT_TYPE.IsUnmanagedEx() == false) | |||
TypeCache<T>.isUnmanaged == false) | |||
throw new Exception( | |||
$"Entity Component check failed, unexpected struct type (must be unmanaged) {ENTITY_COMPONENT_TYPE}"); | |||
} | |||
@@ -104,7 +110,7 @@ namespace Svelto.ECS | |||
public bool isUnmanaged => IS_UNMANAGED; | |||
static ThreadLocal<EntityViewComponentCache> _localCache = new ThreadLocal<EntityViewComponentCache>(() => new EntityViewComponentCache()); | |||
static readonly ThreadLocal<EntityViewComponentCache> _localCache = new ThreadLocal<EntityViewComponentCache>(() => new EntityViewComponentCache()); | |||
public void BuildEntityAndAddToList(ITypeSafeDictionary dictionary, EGID egid, IEnumerable<object> implementors) | |||
{ | |||
@@ -8,7 +8,7 @@ namespace Svelto.ECS | |||
/// <summary> | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
public readonly struct EGIDMapper<T> : IEGIDMapper where T : struct, IBaseEntityComponent | |||
public readonly struct EGIDMapper<T> : IEGIDMapper where T : struct, _IInternalEntityComponent | |||
{ | |||
public int count => _map.count; | |||
public ExclusiveGroupStruct groupID { get; } | |||
@@ -54,8 +54,8 @@ namespace Svelto.ECS | |||
} | |||
//reset the number of entities created so far | |||
_lastNumberEntitiesCreatedPerGroup.FastClear(); | |||
lastComponentsToAddPerGroup.FastClear(); | |||
_lastNumberEntitiesCreatedPerGroup.Clear(); | |||
lastComponentsToAddPerGroup.Clear(); | |||
return; | |||
} | |||
@@ -81,12 +81,12 @@ namespace Svelto.ECS | |||
for (var j = 0; j < componentTypesCount; ++j) | |||
componentTypesDictionary[j].Dispose(); | |||
componentDictionariesPerType[i].FastClear(); | |||
componentDictionariesPerType[i].Clear(); | |||
} | |||
} | |||
//reset the number of entities created so far | |||
_lastNumberEntitiesCreatedPerGroup.FastClear(); | |||
_lastNumberEntitiesCreatedPerGroup.Clear(); | |||
// _totalEntitiesToAdd = 0; | |||
} | |||
@@ -6,7 +6,6 @@ using System; | |||
using System.Collections.Generic; | |||
using DBC.ECS; | |||
using Svelto.Common; | |||
using Svelto.Common.DataStructures; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
using Svelto.ECS.Schedulers; | |||
@@ -18,13 +17,13 @@ namespace Svelto.ECS | |||
static EnginesRoot() | |||
{ | |||
GroupHashMap.Init(); | |||
SharedDictonary.Init(); | |||
//SharedDictonary.Init(); | |||
SerializationDescriptorMap.Init(); | |||
_swapEntities = SwapEntities; | |||
_swapEntities = SwapEntities; | |||
_removeEntities = RemoveEntities; | |||
_removeGroup = RemoveGroup; | |||
_swapGroup = SwapGroup; | |||
_removeGroup = RemoveGroup; | |||
_swapGroup = SwapGroup; | |||
} | |||
/// <summary> | |||
@@ -37,18 +36,18 @@ namespace Svelto.ECS | |||
/// </summary> | |||
public EnginesRoot(EntitiesSubmissionScheduler entitiesComponentScheduler) | |||
{ | |||
_entitiesOperations = new EntitiesOperations(); | |||
_idChecker = new FasterDictionary<ExclusiveGroupStruct, HashSet<uint>>(); | |||
_entitiesOperations = new EntitiesOperations(); | |||
_idChecker = new FasterDictionary<ExclusiveGroupStruct, HashSet<uint>>(); | |||
_cachedRangeOfSubmittedIndices = new FasterList<(uint, uint)>(); | |||
_transientEntityIDsLeftAndAffectedByRemoval = new FasterList<uint>(); | |||
_cachedRangeOfSubmittedIndices = new FasterList<(uint, uint)>(); | |||
_transientEntityIDsLeftAndAffectedByRemoval = new FasterList<uint>(); | |||
_transientEntityIDsLeftWithoutDuplicates = new FasterDictionary<uint, int>(); | |||
_multipleOperationOnSameEGIDChecker = new FasterDictionary<EGID, uint>(); | |||
_multipleOperationOnSameEGIDChecker = new FasterDictionary<EGID, uint>(); | |||
#if UNITY_NATIVE //because of the thread count, ATM this is only for unity | |||
_nativeSwapOperationQueue = new Svelto.ECS.DataStructures.AtomicNativeBags(Allocator.Persistent); | |||
_nativeRemoveOperationQueue = new Svelto.ECS.DataStructures.AtomicNativeBags(Allocator.Persistent); | |||
_nativeAddOperationQueue = new Svelto.ECS.DataStructures.AtomicNativeBags(Allocator.Persistent); | |||
_nativeSwapOperationQueue = new AtomicNativeBags(Allocator.Persistent); | |||
_nativeRemoveOperationQueue = new AtomicNativeBags(Allocator.Persistent); | |||
_nativeAddOperationQueue = new AtomicNativeBags(Allocator.Persistent); | |||
#endif | |||
_serializationDescriptorMap = new SerializationDescriptorMap(); | |||
_reactiveEnginesAdd = new FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnAdd>>>(); | |||
@@ -66,24 +65,27 @@ namespace Svelto.ECS | |||
new FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnDispose>>>(); | |||
_reactiveEnginesSubmission = new FasterList<IReactOnSubmission>(); | |||
_enginesSet = new FasterList<IEngine>(); | |||
_enginesTypeSet = new HashSet<Type>(); | |||
_disposableEngines = new FasterList<IDisposable>(); | |||
_reactiveEnginesSubmissionStarted = new FasterList<IReactOnSubmissionStarted>(); | |||
_enginesSet = new FasterList<IEngine>(); | |||
_enginesTypeSet = new HashSet<Type>(); | |||
_disposableEngines = new FasterList<IDisposable>(); | |||
_groupEntityComponentsDB = | |||
new FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>(); | |||
_groupsPerEntity = | |||
new FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>>(); | |||
_groupedEntityToAdd = new DoubleBufferedEntitiesToAdd(); | |||
_entityStreams = EntitiesStreams.Create(); | |||
_entityStreams = EntitiesStreams.Create(); | |||
#if SVELTO_LEGACY_FILTERS | |||
_groupFilters = | |||
new FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, LegacyGroupFilters>>(); | |||
#endif | |||
_entityLocator.InitEntityReferenceMap(); | |||
_entitiesDB = new EntitiesDB(this, _entityLocator); | |||
InitFilters(); | |||
scheduler = entitiesComponentScheduler; | |||
scheduler = entitiesComponentScheduler; | |||
scheduler.onTick = new EntitiesSubmitter(this); | |||
#if UNITY_NATIVE | |||
AllocateNativeOperations(); | |||
@@ -91,7 +93,7 @@ namespace Svelto.ECS | |||
} | |||
protected EnginesRoot(EntitiesSubmissionScheduler entitiesComponentScheduler, | |||
EnginesReadyOption enginesWaitForReady) : this(entitiesComponentScheduler) | |||
EnginesReadyOption enginesWaitForReady): this(entitiesComponentScheduler) | |||
{ | |||
_enginesWaitForReady = enginesWaitForReady; | |||
} | |||
@@ -109,9 +111,14 @@ namespace Svelto.ECS | |||
GC.SuppressFinalize(this); | |||
} | |||
public bool IsValid() | |||
{ | |||
return _isDisposed == false; | |||
} | |||
public void AddEngine(IEngine engine) | |||
{ | |||
var type = engine.GetType(); | |||
var type = engine.GetType(); | |||
var refWrapper = new RefWrapperType(type); | |||
Check.Require(engine != null, "Engine to add is invalid or null"); | |||
Check.Require( | |||
@@ -122,28 +129,42 @@ namespace Svelto.ECS | |||
try | |||
{ | |||
if (engine is IReactOnAdd viewEngineAdd) | |||
#pragma warning disable CS0612 | |||
CheckReactEngineComponents(typeof(IReactOnAdd<>), viewEngineAdd, _reactiveEnginesAdd, type.Name); | |||
#pragma warning restore CS0612 | |||
if (engine is IReactOnAddEx viewEngineAddEx) | |||
CheckReactEngineComponents(typeof(IReactOnAddEx<>), viewEngineAddEx, _reactiveEnginesAddEx, type.Name); | |||
CheckReactEngineComponents( | |||
typeof(IReactOnAddEx<>), viewEngineAddEx, _reactiveEnginesAddEx, type.Name); | |||
if (engine is IReactOnRemove viewEngineRemove) | |||
CheckReactEngineComponents(typeof(IReactOnRemove<>), viewEngineRemove, _reactiveEnginesRemove, type.Name); | |||
CheckReactEngineComponents( | |||
#pragma warning disable CS0612 | |||
typeof(IReactOnRemove<>), viewEngineRemove, _reactiveEnginesRemove, type.Name); | |||
#pragma warning restore CS0612 | |||
if (engine is IReactOnRemoveEx viewEngineRemoveEx) | |||
CheckReactEngineComponents(typeof(IReactOnRemoveEx<>), viewEngineRemoveEx, _reactiveEnginesRemoveEx, type.Name); | |||
CheckReactEngineComponents( | |||
typeof(IReactOnRemoveEx<>), viewEngineRemoveEx, _reactiveEnginesRemoveEx, type.Name); | |||
if (engine is IReactOnDispose viewEngineDispose) | |||
CheckReactEngineComponents(typeof(IReactOnDispose<>), viewEngineDispose, _reactiveEnginesDispose, type.Name); | |||
CheckReactEngineComponents( | |||
typeof(IReactOnDispose<>), viewEngineDispose, _reactiveEnginesDispose, type.Name); | |||
if (engine is IReactOnSwap viewEngineSwap) | |||
#pragma warning disable CS0612 | |||
CheckReactEngineComponents(typeof(IReactOnSwap<>), viewEngineSwap, _reactiveEnginesSwap, type.Name); | |||
#pragma warning restore CS0612 | |||
if (engine is IReactOnSwapEx viewEngineSwapEx) | |||
CheckReactEngineComponents(typeof(IReactOnSwapEx<>), viewEngineSwapEx, _reactiveEnginesSwapEx, type.Name); | |||
CheckReactEngineComponents( | |||
typeof(IReactOnSwapEx<>), viewEngineSwapEx, _reactiveEnginesSwapEx, type.Name); | |||
if (engine is IReactOnSubmission submissionEngine) | |||
_reactiveEnginesSubmission.Add(submissionEngine); | |||
if (engine is IReactOnSubmissionStarted submissionEngineStarted) | |||
_reactiveEnginesSubmissionStarted.Add(submissionEngineStarted); | |||
_enginesTypeSet.Add(refWrapper); | |||
_enginesSet.Add(engine); | |||
@@ -159,14 +180,16 @@ namespace Svelto.ECS | |||
} | |||
catch (Exception e) | |||
{ | |||
throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " "), | |||
throw new ECSException( | |||
"Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " "), | |||
e); | |||
} | |||
} | |||
public void Ready() | |||
{ | |||
Check.Require(_enginesWaitForReady == EnginesReadyOption.WaitForReady, | |||
Check.Require( | |||
_enginesWaitForReady == EnginesReadyOption.WaitForReady, | |||
"The engine has not been initialise to wait for an external ready trigger"); | |||
foreach (var engine in _enginesSet) | |||
@@ -212,11 +235,6 @@ namespace Svelto.ECS | |||
void Dispose(bool disposing) | |||
{ | |||
_isDisposing = disposing; | |||
if (disposing == false) | |||
return; | |||
using (var profiler = new PlatformProfiler("Final Dispose")) | |||
{ | |||
//Note: The engines are disposed before the the remove callback to give the chance to behave | |||
@@ -228,6 +246,7 @@ namespace Svelto.ECS | |||
{ | |||
if (engine is IDisposingEngine dengine) | |||
dengine.isDisposing = true; | |||
engine.Dispose(); | |||
} | |||
catch (Exception e) | |||
@@ -236,28 +255,31 @@ namespace Svelto.ECS | |||
} | |||
foreach (var groups in _groupEntityComponentsDB) | |||
foreach (var entityList in groups.value) | |||
try | |||
{ | |||
ITypeSafeDictionary typeSafeDictionary = entityList.value; | |||
typeSafeDictionary.ExecuteEnginesDisposeCallbacks_Group(_reactiveEnginesDispose, groups.key, | |||
profiler); | |||
} | |||
catch (Exception e) | |||
{ | |||
Console.LogException(e); | |||
} | |||
foreach (var entityList in groups.value) | |||
try | |||
{ | |||
ITypeSafeDictionary typeSafeDictionary = entityList.value; | |||
typeSafeDictionary.ExecuteEnginesDisposeCallbacks_Group( | |||
_reactiveEnginesDispose, groups.key, | |||
profiler); | |||
} | |||
catch (Exception e) | |||
{ | |||
Console.LogException(e); | |||
} | |||
foreach (var groups in _groupEntityComponentsDB) | |||
foreach (var entityList in groups.value) | |||
entityList.value.Dispose(); | |||
foreach (var entityList in groups.value) | |||
entityList.value.Dispose(); | |||
#if SVELTO_LEGACY_FILTERS | |||
foreach (var type in _groupFilters) | |||
foreach (var group in type.value) | |||
group.value.Dispose(); | |||
foreach (var group in type.value) | |||
group.value.Dispose(); | |||
_groupFilters.Clear(); | |||
#endif | |||
DisposeFilters(); | |||
@@ -277,6 +299,7 @@ namespace Svelto.ECS | |||
_reactiveEnginesRemove.Clear(); | |||
_reactiveEnginesDispose.Clear(); | |||
_reactiveEnginesSubmission.Clear(); | |||
_reactiveEnginesSubmissionStarted.Clear(); | |||
_groupedEntityToAdd.Dispose(); | |||
@@ -285,6 +308,8 @@ namespace Svelto.ECS | |||
_entityStreams.Dispose(); | |||
scheduler.Dispose(); | |||
} | |||
_isDisposed = true; | |||
} | |||
void NotifyReactiveEnginesOnSubmission() | |||
@@ -293,10 +318,17 @@ namespace Svelto.ECS | |||
for (var i = 0; i < enginesCount; i++) | |||
_reactiveEnginesSubmission[i].EntitiesSubmitted(); | |||
} | |||
void NotifyReactiveEnginesOnSubmissionStarted() | |||
{ | |||
var enginesCount = _reactiveEnginesSubmissionStarted.count; | |||
for (var i = 0; i < enginesCount; i++) | |||
_reactiveEnginesSubmissionStarted[i].EntitiesSubmissionStarting(); | |||
} | |||
public readonly struct EntitiesSubmitter | |||
{ | |||
public EntitiesSubmitter(EnginesRoot enginesRoot) : this() | |||
public EntitiesSubmitter(EnginesRoot enginesRoot): this() | |||
{ | |||
_enginesRoot = new Svelto.DataStructures.WeakReference<EnginesRoot>(enginesRoot); | |||
} | |||
@@ -305,18 +337,20 @@ namespace Svelto.ECS | |||
{ | |||
Check.Require(_enginesRoot.IsValid, "ticking an GCed engines root?"); | |||
var enginesRootTarget = _enginesRoot.Target; | |||
var enginesRootTarget = _enginesRoot.Target; | |||
var entitiesSubmissionScheduler = enginesRootTarget.scheduler; | |||
if (entitiesSubmissionScheduler.paused == false) | |||
{ | |||
Check.Require(entitiesSubmissionScheduler.isRunning == false, | |||
enginesRootTarget.NotifyReactiveEnginesOnSubmissionStarted(); | |||
Check.Require( | |||
entitiesSubmissionScheduler.isRunning == false, | |||
"A submission started while the previous one was still flushing"); | |||
entitiesSubmissionScheduler.isRunning = true; | |||
using (var profiler = new PlatformProfiler("Svelto.ECS - Entities Submission")) | |||
{ | |||
var iterations = 0; | |||
var iterations = 0; | |||
var hasEverSubmitted = false; | |||
// We need to clear transient filters before processing callbacks since the callbacks may add | |||
@@ -327,15 +361,14 @@ namespace Svelto.ECS | |||
enginesRootTarget.FlushNativeOperations(profiler); | |||
#endif | |||
//todo: proper unit test structural changes made as result of add/remove callbacks | |||
while (enginesRootTarget.HasMadeNewStructuralChangesInThisIteration() | |||
&& iterations++ < MAX_SUBMISSION_ITERATIONS) | |||
while (enginesRootTarget.HasMadeNewStructuralChangesInThisIteration() | |||
&& iterations++ < MAX_SUBMISSION_ITERATIONS) | |||
{ | |||
hasEverSubmitted = true; | |||
_enginesRoot.Target.SingleSubmission(profiler); | |||
#if UNITY_NATIVE | |||
if (enginesRootTarget.HasMadeNewStructuralChangesInThisIteration()) | |||
enginesRootTarget.FlushNativeOperations(profiler); | |||
enginesRootTarget.FlushNativeOperations(profiler); | |||
#endif | |||
} | |||
@@ -364,32 +397,22 @@ namespace Svelto.ECS | |||
const int MAX_SUBMISSION_ITERATIONS = 10; | |||
internal bool _isDisposing; | |||
readonly FasterList<IDisposable> _disposableEngines; | |||
readonly FasterList<IEngine> _enginesSet; | |||
readonly HashSet<Type> _enginesTypeSet; | |||
readonly EnginesReadyOption _enginesWaitForReady; | |||
readonly FasterList<IEngine> _enginesSet; | |||
readonly HashSet<Type> _enginesTypeSet; | |||
readonly EnginesReadyOption _enginesWaitForReady; | |||
readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnAdd>>> _reactiveEnginesAdd; | |||
readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnAddEx>>> | |||
_reactiveEnginesAddEx; | |||
readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnRemove>>> | |||
_reactiveEnginesRemove; | |||
readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnRemoveEx>>> | |||
_reactiveEnginesRemoveEx; | |||
readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnAddEx>>> _reactiveEnginesAddEx; | |||
readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnRemove>>> _reactiveEnginesRemove; | |||
readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnRemoveEx>>> _reactiveEnginesRemoveEx; | |||
readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnSwap>>> _reactiveEnginesSwap; | |||
readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnSwapEx>>> | |||
_reactiveEnginesSwapEx; | |||
readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnDispose>>> | |||
_reactiveEnginesDispose; | |||
readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnSwapEx>>> _reactiveEnginesSwapEx; | |||
readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnDispose>>> _reactiveEnginesDispose; | |||
readonly FasterList<IReactOnSubmission> _reactiveEnginesSubmission; | |||
readonly FasterList<IReactOnSubmissionStarted> _reactiveEnginesSubmissionStarted; | |||
bool _isDisposed; | |||
} | |||
public enum EnginesReadyOption | |||
@@ -177,10 +177,11 @@ namespace Svelto.ECS | |||
// <EntityComponentType <groupID <entityID, EntityComponent>>> | |||
internal readonly FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>> | |||
_groupsPerEntity; | |||
#if SVELTO_LEGACY_FILTERS | |||
//The filters stored for each component and group | |||
internal readonly FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, LegacyGroupFilters>> | |||
_groupFilters; | |||
#endif | |||
readonly EntitiesDB _entitiesDB; | |||
@@ -14,6 +14,7 @@ namespace Svelto.ECS | |||
_enginesRoot = new Svelto.DataStructures.WeakReference<EnginesRoot>(weakReference); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityInitializer BuildEntity<T> | |||
(uint entityID, ExclusiveBuildGroup groupStructId, IEnumerable<object> implementors = null | |||
, [CallerMemberName] string caller = null) where T : IEntityDescriptor, new() | |||
@@ -23,6 +24,7 @@ namespace Svelto.ECS | |||
, TypeCache<T>.type, implementors, caller); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityInitializer BuildEntity<T> | |||
(EGID egid, IEnumerable<object> implementors = null | |||
, [CallerMemberName] string caller = null) where T : IEntityDescriptor, new() | |||
@@ -31,6 +33,7 @@ namespace Svelto.ECS | |||
, TypeCache<T>.type, implementors, caller); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityInitializer BuildEntity<T> | |||
(EGID egid, T entityDescriptor, IEnumerable<object> implementors | |||
, [CallerMemberName] string caller = null) where T : IEntityDescriptor | |||
@@ -39,6 +42,7 @@ namespace Svelto.ECS | |||
, implementors, caller); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityInitializer BuildEntity<T> | |||
(uint entityID, ExclusiveBuildGroup groupStructId, T descriptorEntity, IEnumerable<object> implementors | |||
, [CallerMemberName] string caller = null) where T : IEntityDescriptor | |||
@@ -48,6 +52,15 @@ namespace Svelto.ECS | |||
, implementors, caller); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityInitializer BuildEntity | |||
(EGID egid, IComponentBuilder[] componentsToBuild, Type type, IEnumerable<object> implementors = null | |||
, [CallerMemberName] string caller = null) | |||
{ | |||
return _enginesRoot.Target.BuildEntity(egid, componentsToBuild, type, implementors, caller); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void PreallocateEntitySpace<T>(ExclusiveGroupStruct groupStructId, uint numberOfEntities) | |||
where T : IEntityDescriptor, new() | |||
{ | |||
@@ -55,13 +68,6 @@ namespace Svelto.ECS | |||
, EntityDescriptorTemplate<T>.descriptor.componentsToBuild); | |||
} | |||
public EntityInitializer BuildEntity | |||
(EGID egid, IComponentBuilder[] componentsToBuild, Type type, IEnumerable<object> implementors = null | |||
, [CallerMemberName] string caller = null) | |||
{ | |||
return _enginesRoot.Target.BuildEntity(egid, componentsToBuild, type, implementors, caller); | |||
} | |||
#if UNITY_NATIVE | |||
public Native.NativeEntityFactory ToNative<T> | |||
([CallerMemberName] string caller = null) | |||
@@ -127,26 +127,26 @@ namespace Svelto.ECS | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
void QueueRemoveGroupOperation(ExclusiveBuildGroup groupID, string caller) | |||
{ | |||
_entitiesOperations.AddRemoveGroupOperation(groupID, caller); | |||
_entitiesOperations.QueueRemoveGroupOperation(groupID, caller); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
void QueueSwapGroupOperation(ExclusiveBuildGroup fromGroupID, ExclusiveBuildGroup toGroupID, string caller) | |||
{ | |||
_entitiesOperations.AddSwapGroupOperation(fromGroupID, toGroupID, caller); | |||
_entitiesOperations.QueueSwapGroupOperation(fromGroupID, toGroupID, caller); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
void QueueSwapEntityOperation | |||
(EGID fromID, EGID toID, IComponentBuilder[] componentBuilders, string caller) | |||
{ | |||
_entitiesOperations.AddSwapOperation(fromID, toID, componentBuilders, caller); | |||
_entitiesOperations.QueueSwapOperation(fromID, toID, componentBuilders, caller); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
void QueueRemoveEntityOperation(EGID entityEGID, IComponentBuilder[] componentBuilders, string caller) | |||
{ | |||
_entitiesOperations.AddRemoveOperation(entityEGID, componentBuilders, caller); | |||
_entitiesOperations.QueueRemoveOperation(entityEGID, componentBuilders, caller); | |||
} | |||
} | |||
} |
@@ -11,12 +11,16 @@ namespace Svelto.ECS | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
void SingleSubmission(PlatformProfiler profiler) | |||
{ | |||
ClearDebugChecks(); //this must be done first as I need the carry the last states after the submission | |||
_entitiesOperations.ExecuteRemoveAndSwappingOperations(_swapEntities, _removeEntities, _removeGroup | |||
, _swapGroup, this); | |||
_entitiesOperations.ExecuteRemoveAndSwappingOperations( | |||
_swapEntities, | |||
_removeEntities, | |||
_removeGroup, | |||
_swapGroup, | |||
this); | |||
AddEntities(profiler); | |||
ClearDebugChecks(); //this must be done first as I need the carry the last states after the submission | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -37,9 +41,9 @@ namespace Svelto.ECS | |||
} | |||
} | |||
static void RemoveEntities | |||
(FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, FasterList<(uint, string)>>> | |||
removeOperations, FasterList<EGID> entitiesRemoved, EnginesRoot enginesRoot) | |||
static void RemoveEntities( | |||
FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, FasterList<(uint, string)>>> | |||
removeOperations, FasterList<EGID> entitiesRemoved, EnginesRoot enginesRoot) | |||
{ | |||
using (var sampler = new PlatformProfiler("remove Entities")) | |||
{ | |||
@@ -56,54 +60,60 @@ namespace Svelto.ECS | |||
{ | |||
foreach (var entitiesToRemove in removeOperations) | |||
{ | |||
ExclusiveGroupStruct group = entitiesToRemove.key; | |||
var fromGroupDictionary = enginesRoot.GetDBGroup(group); | |||
ExclusiveGroupStruct group = entitiesToRemove.key; | |||
var fromGroupDictionary = enginesRoot.GetDBGroup(group); | |||
foreach (var groupedEntitiesToRemove in entitiesToRemove.value) | |||
{ | |||
var componentType = groupedEntitiesToRemove.key; | |||
var componentType = groupedEntitiesToRemove.key; | |||
ITypeSafeDictionary fromComponentsDictionary = fromGroupDictionary[componentType]; | |||
FasterList<(uint, string)> infosToProcess = groupedEntitiesToRemove.value; | |||
fromComponentsDictionary.ExecuteEnginesRemoveCallbacks( | |||
infosToProcess, enginesRoot._reactiveEnginesRemove, group, in sampler); | |||
infosToProcess, | |||
enginesRoot._reactiveEnginesRemove, | |||
group, | |||
in sampler); | |||
} | |||
} | |||
} | |||
using (sampler.Sample("Remove Entities")) | |||
{ | |||
enginesRoot._cachedRangeOfSubmittedIndices.FastClear(); | |||
enginesRoot._cachedRangeOfSubmittedIndices.Clear(); | |||
foreach (var entitiesToRemove in removeOperations) | |||
{ | |||
ExclusiveGroupStruct fromGroup = entitiesToRemove.key; | |||
var fromGroupDictionary = enginesRoot.GetDBGroup(fromGroup); | |||
ExclusiveGroupStruct fromGroup = entitiesToRemove.key; | |||
var fromGroupDictionary = enginesRoot.GetDBGroup(fromGroup); | |||
foreach (var groupedEntitiesToRemove in entitiesToRemove.value) | |||
{ | |||
RefWrapperType componentType = groupedEntitiesToRemove.key; | |||
RefWrapperType componentType = groupedEntitiesToRemove.key; | |||
ITypeSafeDictionary fromComponentsDictionary = fromGroupDictionary[componentType]; | |||
FasterList<(uint, string)> entityIDsToRemove = groupedEntitiesToRemove.value; | |||
enginesRoot._transientEntityIDsLeftAndAffectedByRemoval.FastClear(); | |||
enginesRoot._transientEntityIDsLeftAndAffectedByRemoval.Clear(); | |||
fromComponentsDictionary.RemoveEntitiesFromDictionary( | |||
entityIDsToRemove, enginesRoot._transientEntityIDsLeftAndAffectedByRemoval); | |||
entityIDsToRemove, | |||
enginesRoot._transientEntityIDsLeftAndAffectedByRemoval); | |||
//important: remove from the filter must happen after remove from the dictionary | |||
//as we need to read the new indices linked to entities after the removal | |||
enginesRoot.RemoveEntitiesFromPersistentFilters( | |||
entityIDsToRemove, fromGroup, componentType, fromComponentsDictionary | |||
, enginesRoot._transientEntityIDsLeftAndAffectedByRemoval); | |||
entityIDsToRemove, | |||
fromGroup, | |||
componentType, | |||
fromComponentsDictionary, | |||
enginesRoot._transientEntityIDsLeftAndAffectedByRemoval); | |||
//store new database count after the entities are removed from the datatabase, plus the number of entities removed | |||
enginesRoot._cachedRangeOfSubmittedIndices.Add(((uint, uint))( | |||
fromComponentsDictionary.count | |||
, fromComponentsDictionary.count | |||
+ entityIDsToRemove.count)); | |||
enginesRoot._cachedRangeOfSubmittedIndices.Add( | |||
((uint, uint))(fromComponentsDictionary.count, | |||
fromComponentsDictionary.count + entityIDsToRemove.count)); | |||
} | |||
} | |||
} | |||
@@ -120,33 +130,36 @@ namespace Svelto.ECS | |||
{ | |||
foreach (var entitiesToRemove in removeOperations) | |||
{ | |||
ExclusiveGroupStruct group = entitiesToRemove.key; | |||
var fromGroupDictionary = enginesRoot.GetDBGroup(group); | |||
ExclusiveGroupStruct group = entitiesToRemove.key; | |||
var fromGroupDictionary = enginesRoot.GetDBGroup(group); | |||
foreach (var groupedEntitiesToRemove in entitiesToRemove.value) | |||
{ | |||
rangeEnumerator.MoveNext(); | |||
var componentType = groupedEntitiesToRemove.key; | |||
var componentType = groupedEntitiesToRemove.key; | |||
ITypeSafeDictionary fromComponentsDictionary = fromGroupDictionary[componentType]; | |||
//get all the engines linked to TValue | |||
if (!enginesRoot._reactiveEnginesRemoveEx.TryGetValue( | |||
componentType, out var entityComponentsEngines)) | |||
componentType, | |||
out var entityComponentsEngines)) | |||
continue; | |||
fromComponentsDictionary.ExecuteEnginesRemoveCallbacksFast( | |||
entityComponentsEngines, group, rangeEnumerator.Current, in sampler); | |||
entityComponentsEngines, | |||
group, | |||
rangeEnumerator.Current, | |||
in sampler); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
static void SwapEntities | |||
(FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, | |||
FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>> swapEntitiesOperations | |||
, FasterList<(EGID, EGID)> entitiesIDSwaps, EnginesRoot enginesRoot) | |||
static void SwapEntities(FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, | |||
FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>> swapEntitiesOperations, | |||
FasterList<(EGID, EGID)> entitiesIDSwaps, EnginesRoot enginesRoot) | |||
{ | |||
using (var sampler = new PlatformProfiler("Swap entities between groups")) | |||
{ | |||
@@ -163,7 +176,7 @@ namespace Svelto.ECS | |||
using (sampler.Sample("Swap Entities")) | |||
{ | |||
enginesRoot._cachedRangeOfSubmittedIndices.FastClear(); | |||
enginesRoot._cachedRangeOfSubmittedIndices.Clear(); | |||
//Entities to swap are organised in order to minimise the amount of dictionary lookups. | |||
//swapEntitiesOperations iterations happen in the following order: | |||
@@ -172,53 +185,62 @@ namespace Svelto.ECS | |||
//now swap the set of FromGroup -> ToGroup entities per ID. | |||
foreach (var entitiesToSwap in swapEntitiesOperations) | |||
{ | |||
ExclusiveGroupStruct fromGroup = entitiesToSwap.key; | |||
var fromGroupDictionary = enginesRoot.GetDBGroup(fromGroup); | |||
ExclusiveGroupStruct fromGroup = entitiesToSwap.key; | |||
var fromGroupDictionary = enginesRoot.GetDBGroup(fromGroup); | |||
//iterate all the fromgroups | |||
foreach (var groupedEntitiesToSwap in entitiesToSwap.value) | |||
{ | |||
var componentType = groupedEntitiesToSwap.key; | |||
ITypeSafeDictionary fromComponentsDictionary = fromGroupDictionary[componentType]; | |||
var componentType = groupedEntitiesToSwap.key; | |||
ITypeSafeDictionary fromComponentsDictionaryDB = fromGroupDictionary[componentType]; | |||
//get the subset of togroups that come from from group | |||
foreach (var entitiesInfoToSwap in groupedEntitiesToSwap.value) | |||
{ | |||
ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key; | |||
ITypeSafeDictionary toComponentsDictionary = | |||
enginesRoot.GetOrAddTypeSafeDictionary( | |||
toGroup, enginesRoot.GetOrAddDBGroup(toGroup), componentType | |||
, fromComponentsDictionary); | |||
ITypeSafeDictionary toComponentsDictionaryDB = enginesRoot.GetOrAddTypeSafeDictionary( | |||
toGroup, | |||
enginesRoot.GetOrAddDBGroup(toGroup), | |||
componentType, | |||
fromComponentsDictionaryDB); | |||
DBC.ECS.Check.Assert(toComponentsDictionary != null | |||
, "something went wrong with the creation of dictionaries"); | |||
DBC.ECS.Check.Assert( | |||
toComponentsDictionaryDB != null, | |||
"something went wrong with the creation of dictionaries"); | |||
//this list represents the set of entities that come from fromGroup and need | |||
//to be swapped to toGroup. Most of the times will be 1 of few units. | |||
FasterList<(uint, uint, string)> fromEntityToEntityIDs = entitiesInfoToSwap.value; | |||
//ensure that to dictionary has enough room to store the new entities | |||
toComponentsDictionary.EnsureCapacity( | |||
(uint)(toComponentsDictionary.count + (uint)fromEntityToEntityIDs.count)); | |||
toComponentsDictionaryDB.EnsureCapacity( | |||
(uint)(toComponentsDictionaryDB.count + (uint)fromEntityToEntityIDs.count)); | |||
//fortunately swap means that entities are added at the end of each destination | |||
//dictionary list, so we can just iterate the list using the indices ranges added in the | |||
//_cachedIndices | |||
enginesRoot._cachedRangeOfSubmittedIndices.Add(((uint, uint))( | |||
toComponentsDictionary.count | |||
, toComponentsDictionary.count | |||
+ fromEntityToEntityIDs.count)); | |||
enginesRoot._cachedRangeOfSubmittedIndices.Add( | |||
((uint, uint))(toComponentsDictionaryDB.count, | |||
toComponentsDictionaryDB.count + fromEntityToEntityIDs.count)); | |||
enginesRoot._transientEntityIDsLeftAndAffectedByRemoval.FastClear(); | |||
enginesRoot._transientEntityIDsLeftAndAffectedByRemoval.Clear(); | |||
fromComponentsDictionary.SwapEntitiesBetweenDictionaries( | |||
fromEntityToEntityIDs, fromGroup, toGroup, toComponentsDictionary | |||
, enginesRoot._transientEntityIDsLeftAndAffectedByRemoval); | |||
fromComponentsDictionaryDB.SwapEntitiesBetweenDictionaries( | |||
fromEntityToEntityIDs, | |||
fromGroup, | |||
toGroup, | |||
toComponentsDictionaryDB, | |||
enginesRoot._transientEntityIDsLeftAndAffectedByRemoval); | |||
//important: this must happen after the entities are swapped in the database | |||
enginesRoot.SwapEntityBetweenPersistentFilters( | |||
fromEntityToEntityIDs, fromComponentsDictionary, toComponentsDictionary, fromGroup | |||
, toGroup, componentType, enginesRoot._transientEntityIDsLeftAndAffectedByRemoval); | |||
fromEntityToEntityIDs, | |||
fromComponentsDictionaryDB, | |||
toComponentsDictionaryDB, | |||
fromGroup, | |||
toGroup, | |||
componentType, | |||
enginesRoot._transientEntityIDsLeftAndAffectedByRemoval); | |||
} | |||
} | |||
} | |||
@@ -236,19 +258,26 @@ namespace Svelto.ECS | |||
//get all the engines linked to TValue | |||
if (!enginesRoot._reactiveEnginesSwap.TryGetValue( | |||
new RefWrapperType(componentType), out var entityComponentsEngines)) | |||
new RefWrapperType(componentType), | |||
out var entityComponentsEngines)) | |||
continue; | |||
foreach (var entitiesInfoToSwap in groupedEntitiesToSwap.value) | |||
{ | |||
ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key; | |||
ITypeSafeDictionary toComponentsDictionary = | |||
GetTypeSafeDictionary(toGroup, enginesRoot.GetDBGroup(toGroup), componentType); | |||
ITypeSafeDictionary toComponentsDictionary = GetTypeSafeDictionary( | |||
toGroup, | |||
enginesRoot.GetDBGroup(toGroup), | |||
componentType); | |||
var infosToProcess = entitiesInfoToSwap.value; | |||
toComponentsDictionary.ExecuteEnginesSwapCallbacks( | |||
infosToProcess, entityComponentsEngines, fromGroup, toGroup, in sampler); | |||
infosToProcess, | |||
entityComponentsEngines, | |||
fromGroup, | |||
toGroup, | |||
in sampler); | |||
} | |||
} | |||
} | |||
@@ -271,15 +300,22 @@ namespace Svelto.ECS | |||
//get all the engines linked to TValue | |||
if (!enginesRoot._reactiveEnginesSwapEx.TryGetValue( | |||
new RefWrapperType(componentType), out var entityComponentsEngines)) | |||
new RefWrapperType(componentType), | |||
out var entityComponentsEngines)) | |||
continue; | |||
ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key; | |||
ITypeSafeDictionary toComponentsDictionary = | |||
GetTypeSafeDictionary(toGroup, enginesRoot.GetDBGroup(toGroup), componentType); | |||
ITypeSafeDictionary toComponentsDictionary = GetTypeSafeDictionary( | |||
toGroup, | |||
enginesRoot.GetDBGroup(toGroup), | |||
componentType); | |||
toComponentsDictionary.ExecuteEnginesSwapCallbacksFast( | |||
entityComponentsEngines, fromGroup, toGroup, rangeEnumerator.Current, in sampler); | |||
entityComponentsEngines, | |||
fromGroup, | |||
toGroup, | |||
rangeEnumerator.Current, | |||
in sampler); | |||
} | |||
} | |||
} | |||
@@ -295,7 +331,7 @@ namespace Svelto.ECS | |||
//I need to iterate the previous current, which is now other | |||
if (_groupedEntityToAdd.AnyPreviousEntityCreated()) | |||
{ | |||
_cachedRangeOfSubmittedIndices.FastClear(); | |||
_cachedRangeOfSubmittedIndices.Clear(); | |||
using (sampler.Sample("Add operations")) | |||
{ | |||
try | |||
@@ -312,12 +348,11 @@ namespace Svelto.ECS | |||
//add the entityComponents in the group | |||
foreach (var entityComponentsToSubmit in groupToSubmit.components) | |||
{ | |||
var type = entityComponentsToSubmit.key; | |||
var type = entityComponentsToSubmit.key; | |||
var fromDictionary = entityComponentsToSubmit.value; | |||
var wrapper = new RefWrapperType(type); | |||
var wrapper = new RefWrapperType(type); | |||
var toDictionary = | |||
GetOrAddTypeSafeDictionary(groupID, groupDB, wrapper, fromDictionary); | |||
var toDictionary = GetOrAddTypeSafeDictionary(groupID, groupDB, wrapper, fromDictionary); | |||
//all the new entities are added at the end of each dictionary list, so we can | |||
//just iterate the list using the indices ranges added in the _cachedIndices | |||
@@ -326,7 +361,7 @@ namespace Svelto.ECS | |||
//Fill the DB with the entity components generated this frame. | |||
fromDictionary.AddEntitiesToDictionary(toDictionary, groupID | |||
#if SLOW_SVELTO_SUBMISSION | |||
, entityLocator | |||
, entityLocator | |||
#endif | |||
); | |||
} | |||
@@ -345,13 +380,12 @@ namespace Svelto.ECS | |||
foreach (var entityComponentsToSubmit in groupToSubmit.components) | |||
{ | |||
var type = entityComponentsToSubmit.key; | |||
var type = entityComponentsToSubmit.key; | |||
var wrapper = new RefWrapperType(type); | |||
var toDictionary = GetTypeSafeDictionary(groupID, groupDB, wrapper); | |||
enumerator.MoveNext(); | |||
toDictionary.ExecuteEnginesAddEntityCallbacksFast( | |||
_reactiveEnginesAddEx, groupID, enumerator.Current, in sampler); | |||
toDictionary.ExecuteEnginesAddEntityCallbacksFast(_reactiveEnginesAddEx, groupID, enumerator.Current, in sampler); | |||
} | |||
} | |||
} | |||
@@ -372,7 +406,7 @@ namespace Svelto.ECS | |||
//design of the transient buffer of added entities. | |||
foreach (var entityComponentsToSubmit in groupToSubmit.components) | |||
{ | |||
var type = entityComponentsToSubmit.key; | |||
var type = entityComponentsToSubmit.key; | |||
var fromDictionary = entityComponentsToSubmit.value; | |||
//this contains the total number of components ever submitted in the DB | |||
@@ -425,11 +459,10 @@ namespace Svelto.ECS | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
void SwapEntitiesBetweenGroups | |||
(ExclusiveGroupStruct fromGroupId, ExclusiveGroupStruct toGroupId, PlatformProfiler platformProfiler) | |||
void SwapEntitiesBetweenGroups(ExclusiveGroupStruct fromGroupId, ExclusiveGroupStruct toGroupId, PlatformProfiler platformProfiler) | |||
{ | |||
FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup = GetDBGroup(fromGroupId); | |||
FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup = GetOrAddDBGroup(toGroupId); | |||
FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup = GetOrAddDBGroup(toGroupId); | |||
_entityLocator.UpdateAllGroupReferenceLocators(fromGroupId, toGroupId); | |||
@@ -439,12 +472,11 @@ namespace Svelto.ECS | |||
RefWrapperType refWrapperType = dictionaryOfEntities.key; | |||
ITypeSafeDictionary fromDictionary = dictionaryOfEntities.value; | |||
ITypeSafeDictionary toDictionary = | |||
GetOrAddTypeSafeDictionary(toGroupId, toGroup, refWrapperType, fromDictionary); | |||
ITypeSafeDictionary toDictionary = GetOrAddTypeSafeDictionary(toGroupId, toGroup, refWrapperType, fromDictionary); | |||
fromDictionary.AddEntitiesToDictionary(toDictionary, toGroupId | |||
#if SLOW_SVELTO_SUBMISSION | |||
, this.entityLocator | |||
, this.entityLocator | |||
#endif | |||
); | |||
} | |||
@@ -455,12 +487,11 @@ namespace Svelto.ECS | |||
RefWrapperType refWrapperType = dictionaryOfEntities.key; | |||
ITypeSafeDictionary fromDictionary = dictionaryOfEntities.value; | |||
ITypeSafeDictionary toDictionary = GetTypeSafeDictionary(toGroupId, toGroup, refWrapperType); | |||
ITypeSafeDictionary toDictionary = GetTypeSafeDictionary(toGroupId, toGroup, refWrapperType); | |||
//SwapEX happens inside | |||
fromDictionary.ExecuteEnginesSwapCallbacks_Group(_reactiveEnginesSwap, _reactiveEnginesSwapEx | |||
, toDictionary, fromGroupId, toGroupId | |||
, platformProfiler); | |||
fromDictionary.ExecuteEnginesSwapCallbacks_Group(_reactiveEnginesSwap, _reactiveEnginesSwapEx, toDictionary, | |||
fromGroupId, toGroupId, platformProfiler); | |||
} | |||
//remove entities from dictionaries | |||
@@ -473,9 +504,9 @@ namespace Svelto.ECS | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
ITypeSafeDictionary GetOrAddTypeSafeDictionary | |||
(ExclusiveGroupStruct groupId, FasterDictionary<RefWrapperType, ITypeSafeDictionary> groupPerComponentType | |||
, RefWrapperType type, ITypeSafeDictionary fromDictionary) | |||
ITypeSafeDictionary GetOrAddTypeSafeDictionary(ExclusiveGroupStruct groupId, | |||
FasterDictionary<RefWrapperType, ITypeSafeDictionary> groupPerComponentType, RefWrapperType type, | |||
ITypeSafeDictionary fromDictionary) | |||
{ | |||
//be sure that the TypeSafeDictionary for the entity Type exists | |||
if (groupPerComponentType.TryGetValue(type, out ITypeSafeDictionary toEntitiesDictionary) == false) | |||
@@ -497,9 +528,8 @@ namespace Svelto.ECS | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
static ITypeSafeDictionary GetTypeSafeDictionary | |||
(ExclusiveGroupStruct groupID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> @group | |||
, RefWrapperType refWrapper) | |||
static ITypeSafeDictionary GetTypeSafeDictionary(ExclusiveGroupStruct groupID, | |||
FasterDictionary<RefWrapperType, ITypeSafeDictionary> @group, RefWrapperType refWrapper) | |||
{ | |||
if (@group.TryGetValue(refWrapper, out ITypeSafeDictionary fromTypeSafeDictionary) == false) | |||
{ | |||
@@ -510,25 +540,24 @@ namespace Svelto.ECS | |||
} | |||
readonly DoubleBufferedEntitiesToAdd _groupedEntityToAdd; | |||
readonly EntitiesOperations _entitiesOperations; | |||
readonly EntitiesOperations _entitiesOperations; | |||
//transient caches>>>>>>>>>>>>>>>>>>>>> | |||
readonly FasterList<(uint, uint)> _cachedRangeOfSubmittedIndices; | |||
readonly FasterList<(uint, uint)> _cachedRangeOfSubmittedIndices; | |||
readonly FasterDictionary<uint, int> _transientEntityIDsLeftWithoutDuplicates; | |||
readonly FasterList<uint> _transientEntityIDsLeftAndAffectedByRemoval; | |||
readonly FasterList<uint> _transientEntityIDsLeftAndAffectedByRemoval; | |||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | |||
static readonly | |||
Action<FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, | |||
FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>>, FasterList<(EGID, EGID)> | |||
, | |||
EnginesRoot> _swapEntities; | |||
, EnginesRoot> _swapEntities; | |||
static readonly Action< | |||
FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, FasterList<(uint, string)>>>, | |||
FasterList<EGID>, EnginesRoot> _removeEntities; | |||
static readonly Action<ExclusiveGroupStruct, EnginesRoot> _removeGroup; | |||
static readonly Action<ExclusiveGroupStruct, EnginesRoot> _removeGroup; | |||
static readonly Action<ExclusiveGroupStruct, ExclusiveGroupStruct, EnginesRoot> _swapGroup; | |||
} | |||
} |
@@ -20,7 +20,7 @@ namespace Svelto.ECS | |||
EntityCollection<T> InternalQueryEntities<T> | |||
(FasterDictionary<RefWrapperType, ITypeSafeDictionary> entitiesInGroupPerType) | |||
where T : struct, IBaseEntityComponent | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
uint count = 0; | |||
IBuffer<T> buffer; | |||
@@ -46,7 +46,7 @@ namespace Svelto.ECS | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
public EntityCollection<T> QueryEntities<T>(ExclusiveGroupStruct groupStructId) | |||
where T : struct, IBaseEntityComponent | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
if (groupEntityComponentsDB.TryGetValue(groupStructId, out var entitiesInGroupPerType) == false) | |||
{ | |||
@@ -57,7 +57,7 @@ namespace Svelto.ECS | |||
} | |||
public EntityCollection<T1, T2> QueryEntities<T1, T2>(ExclusiveGroupStruct groupStruct) | |||
where T1 : struct, IBaseEntityComponent where T2 : struct, IBaseEntityComponent | |||
where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent | |||
{ | |||
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false) | |||
{ | |||
@@ -75,16 +75,16 @@ namespace Svelto.ECS | |||
.FastConcat("'. Entity 2: ' count: ".FastConcat(T2entities.count) | |||
.FastConcat(" ", typeof(T2).ToString()) | |||
.FastConcat( | |||
"' group: ", groupStruct.ToName()))); | |||
"' group: ", groupStruct.ToName())).FastConcat(" this means that you are mixing descriptors in the same group that do not share the components that you are querying")); | |||
#endif | |||
return new EntityCollection<T1, T2>(T1entities, T2entities); | |||
} | |||
public EntityCollection<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroupStruct groupStruct) | |||
where T1 : struct, IBaseEntityComponent | |||
where T2 : struct, IBaseEntityComponent | |||
where T3 : struct, IBaseEntityComponent | |||
where T1 : struct, _IInternalEntityComponent | |||
where T2 : struct, _IInternalEntityComponent | |||
where T3 : struct, _IInternalEntityComponent | |||
{ | |||
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false) | |||
{ | |||
@@ -105,17 +105,17 @@ namespace Svelto.ECS | |||
" Entity 2: ".FastConcat(typeof(T2).ToString()).FastConcat(" count: ") | |||
.FastConcat(T2entities.count) | |||
.FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString())) | |||
.FastConcat(" count: ").FastConcat(T3entities.count))); | |||
.FastConcat(" count: ").FastConcat(T3entities.count)).FastConcat(" this means that you are mixing descriptors in the same group that do not share the components that you are querying")); | |||
#endif | |||
return new EntityCollection<T1, T2, T3>(T1entities, T2entities, T3entities); | |||
} | |||
public EntityCollection<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4>(ExclusiveGroupStruct groupStruct) | |||
where T1 : struct, IBaseEntityComponent | |||
where T2 : struct, IBaseEntityComponent | |||
where T3 : struct, IBaseEntityComponent | |||
where T4 : struct, IBaseEntityComponent | |||
where T1 : struct, _IInternalEntityComponent | |||
where T2 : struct, _IInternalEntityComponent | |||
where T3 : struct, _IInternalEntityComponent | |||
where T4 : struct, _IInternalEntityComponent | |||
{ | |||
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false) | |||
{ | |||
@@ -141,49 +141,49 @@ namespace Svelto.ECS | |||
.FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString())) | |||
.FastConcat(" count: ").FastConcat(T3entities.count) | |||
.FastConcat(" Entity 4: ".FastConcat(typeof(T4).ToString())) | |||
.FastConcat(" count: ").FastConcat(T4entities.count))); | |||
.FastConcat(" count: ").FastConcat(T4entities.count)).FastConcat(" this means that you are mixing descriptors in the same group that do not share the components that you are querying")); | |||
#endif | |||
return new EntityCollection<T1, T2, T3, T4>(T1entities, T2entities, T3entities, T4entities); | |||
} | |||
public GroupsEnumerable<T> QueryEntities<T> | |||
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T : struct, IBaseEntityComponent | |||
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T : struct, _IInternalEntityComponent | |||
{ | |||
return new GroupsEnumerable<T>(this, groups); | |||
} | |||
/// <summary> | |||
/// Note: Remember that EntityViewComponents are always put at the end of the generic parameters tuple. | |||
/// It won't compile otherwise | |||
/// The Query entity code won't inexplicably compile otherwise | |||
/// </summary> | |||
/// <returns></returns> | |||
public GroupsEnumerable<T1, T2> QueryEntities<T1, T2>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
where T1 : struct, IBaseEntityComponent where T2 : struct, IBaseEntityComponent | |||
where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent | |||
{ | |||
return new GroupsEnumerable<T1, T2>(this, groups); | |||
} | |||
public GroupsEnumerable<T1, T2, T3> QueryEntities<T1, T2, T3> | |||
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T1 : struct, IBaseEntityComponent | |||
where T2 : struct, IBaseEntityComponent | |||
where T3 : struct, IBaseEntityComponent | |||
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T1 : struct, _IInternalEntityComponent | |||
where T2 : struct, _IInternalEntityComponent | |||
where T3 : struct, _IInternalEntityComponent | |||
{ | |||
return new GroupsEnumerable<T1, T2, T3>(this, groups); | |||
} | |||
public GroupsEnumerable<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4> | |||
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T1 : struct, IBaseEntityComponent | |||
where T2 : struct, IBaseEntityComponent | |||
where T3 : struct, IBaseEntityComponent | |||
where T4 : struct, IBaseEntityComponent | |||
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T1 : struct, _IInternalEntityComponent | |||
where T2 : struct, _IInternalEntityComponent | |||
where T3 : struct, _IInternalEntityComponent | |||
where T4 : struct, _IInternalEntityComponent | |||
{ | |||
return new GroupsEnumerable<T1, T2, T3, T4>(this, groups); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroupStruct groupStructId) | |||
where T : struct, IBaseEntityComponent | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false) | |||
throw new EntityGroupNotFoundException(typeof(T), groupStructId.ToName()); | |||
@@ -193,7 +193,7 @@ namespace Svelto.ECS | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryQueryMappedEntities<T> | |||
(ExclusiveGroupStruct groupStructId, out EGIDMapper<T> mapper) where T : struct, IBaseEntityComponent | |||
(ExclusiveGroupStruct groupStructId, out EGIDMapper<T> mapper) where T : struct, _IInternalEntityComponent | |||
{ | |||
mapper = default; | |||
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false | |||
@@ -206,7 +206,7 @@ namespace Svelto.ECS | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool Exists<T>(EGID entityGID) where T : struct, IBaseEntityComponent | |||
public bool Exists<T>(EGID entityGID) where T : struct, _IInternalEntityComponent | |||
{ | |||
if (SafeQueryEntityDictionary<T>(entityGID.groupID, out var casted) == false) | |||
return false; | |||
@@ -215,7 +215,7 @@ namespace Svelto.ECS | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool Exists<T>(uint id, ExclusiveGroupStruct group) where T : struct, IBaseEntityComponent | |||
public bool Exists<T>(uint id, ExclusiveGroupStruct group) where T : struct, _IInternalEntityComponent | |||
{ | |||
if (SafeQueryEntityDictionary<T>(group, out var casted) == false) | |||
return false; | |||
@@ -236,13 +236,13 @@ namespace Svelto.ECS | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool HasAny<T>(ExclusiveGroupStruct groupStruct) where T : struct, IBaseEntityComponent | |||
public bool HasAny<T>(ExclusiveGroupStruct groupStruct) where T : struct, _IInternalEntityComponent | |||
{ | |||
return Count<T>(groupStruct) > 0; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public int Count<T>(ExclusiveGroupStruct groupStruct) where T : struct, IBaseEntityComponent | |||
public int Count<T>(ExclusiveGroupStruct groupStruct) where T : struct, _IInternalEntityComponent | |||
{ | |||
if (SafeQueryEntityDictionary<T>(groupStruct, out var typeSafeDictionary) == false) | |||
return 0; | |||
@@ -250,17 +250,15 @@ namespace Svelto.ECS | |||
return (int)typeSafeDictionary.count; | |||
} | |||
public bool FoundInGroups<T1>() where T1 : IBaseEntityComponent | |||
public bool FoundInGroups<T1>() where T1 : _IInternalEntityComponent | |||
{ | |||
return groupsPerComponent.ContainsKey(TypeRefWrapper<T1>.wrapper); | |||
} | |||
public bool IsDisposing => _enginesRoot._isDisposing; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
bool SafeQueryEntityDictionary<T> | |||
(out ITypeSafeDictionary typeSafeDictionary | |||
, FasterDictionary<RefWrapperType, ITypeSafeDictionary> entitiesInGroupPerType) where T : IBaseEntityComponent | |||
, FasterDictionary<RefWrapperType, ITypeSafeDictionary> entitiesInGroupPerType) where T : _IInternalEntityComponent | |||
{ | |||
if (entitiesInGroupPerType.TryGetValue(new RefWrapperType(TypeCache<T>.type), out var safeDictionary) | |||
== false) | |||
@@ -277,7 +275,7 @@ namespace Svelto.ECS | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal bool SafeQueryEntityDictionary<T> | |||
(ExclusiveGroupStruct group, out ITypeSafeDictionary typeSafeDictionary) where T : IBaseEntityComponent | |||
(ExclusiveGroupStruct group, out ITypeSafeDictionary typeSafeDictionary) where T : _IInternalEntityComponent | |||
{ | |||
if (UnsafeQueryEntityDictionary(group, TypeCache<T>.type, out var safeDictionary) == false) | |||
{ | |||
@@ -1,83 +1,95 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
using Svelto.Utilities; | |||
namespace Svelto.ECS | |||
{ | |||
class EntitiesOperations | |||
{ | |||
/// <summary> | |||
/// Todo: need to go back here and add a ton of comments | |||
/// </summary> | |||
public EntitiesOperations() | |||
{ | |||
_thisSubmissionInfo.Init(); | |||
_lastSubmittedInfo.Init(); | |||
_newGroupDictionary = NewGroupDictionary; | |||
_newGroupsDictionary = NewGroupsDictionary; | |||
_recycleDictionary = RecycleDictionary; | |||
_newList = NewList; | |||
_clearList = ClearList; | |||
_newGroupsDictionaryWithCaller = NewGroupsDictionaryWithCaller; | |||
_recycleGroupDictionaryWithCaller = RecycleGroupDictionaryWithCaller; | |||
_recycleDicitionaryWithCaller = RecycleDicitionaryWithCaller; | |||
_newListWithCaller = NewListWithCaller; | |||
_clearListWithCaller = ClearListWithCaller; | |||
} | |||
public void AddSwapOperation(EGID fromID, EGID toID, IComponentBuilder[] componentBuilders, string caller) | |||
public void QueueRemoveGroupOperation(ExclusiveBuildGroup groupID, string caller) | |||
{ | |||
_thisSubmissionInfo._entitiesSwapped.Add((fromID, toID)); | |||
//todo: limit the number of dictionaries that can be cached | |||
//recycle or create dictionaries of components per group | |||
var swappedComponentsPerType = _thisSubmissionInfo._currentSwapEntitiesOperations.RecycleOrAdd( | |||
fromID.groupID, | |||
() => new FasterDictionary<RefWrapperType, | |||
FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>(), | |||
(ref FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>> recycled) => | |||
recycled.FastClear()); | |||
foreach (IComponentBuilder operation in componentBuilders) | |||
{ | |||
swappedComponentsPerType | |||
//recycle or create dictionaries per component type | |||
.RecycleOrAdd(new RefWrapperType(operation.GetEntityComponentType()), | |||
() => new FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>(), | |||
(ref FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>> target) => | |||
target.FastClear()) | |||
//recycle or create list of entities to swap | |||
.RecycleOrAdd(toID.groupID, () => new FasterList<(uint, uint, string)>(), | |||
(ref FasterList<(uint, uint, string)> target) => target.FastClear()) | |||
//add entity to swap | |||
.Add((fromID.entityID, toID.entityID, caller)); | |||
} | |||
_thisSubmissionInfo._groupsToRemove.Add((groupID, caller)); | |||
} | |||
public void AddRemoveOperation(EGID entityEgid, IComponentBuilder[] componentBuilders, string caller) | |||
public void QueueRemoveOperation(EGID entityEgid, IComponentBuilder[] componentBuilders, string caller) | |||
{ | |||
_thisSubmissionInfo._entitiesRemoved.Add(entityEgid); | |||
//todo: limit the number of dictionaries that can be cached | |||
//recycle or create dictionaries of components per group | |||
var removedComponentsPerType = _thisSubmissionInfo._currentRemoveEntitiesOperations.RecycleOrAdd( | |||
entityEgid.groupID, () => new FasterDictionary<RefWrapperType, FasterList<(uint, string)>>(), | |||
(ref FasterDictionary<RefWrapperType, FasterList<(uint, string)>> recycled) => recycled.FastClear()); | |||
entityEgid.groupID, _newGroupsDictionary, _recycleDictionary); | |||
foreach (IComponentBuilder operation in componentBuilders) | |||
foreach (var operation in componentBuilders) | |||
{ | |||
removedComponentsPerType | |||
//recycle or create dictionaries per component type | |||
.RecycleOrAdd(new RefWrapperType(operation.GetEntityComponentType()), | |||
() => new FasterList<(uint, string)>(), | |||
(ref FasterList<(uint, string)> target) => target.FastClear()) | |||
//add entity to swap | |||
.Add((entityEgid.entityID, caller)); | |||
removedComponentsPerType //recycle or create dictionaries per component type | |||
.RecycleOrAdd(new RefWrapperType(operation.GetEntityComponentType()), _newList, _clearList) | |||
//add entity to remove | |||
.Add((entityEgid.entityID, caller)); | |||
} | |||
} | |||
public void AddRemoveGroupOperation(ExclusiveBuildGroup groupID, string caller) | |||
public void QueueSwapGroupOperation(ExclusiveBuildGroup fromGroupID, ExclusiveBuildGroup toGroupID, string caller) | |||
{ | |||
_thisSubmissionInfo._groupsToRemove.Add((groupID, caller)); | |||
_thisSubmissionInfo._groupsToSwap.Add((fromGroupID, toGroupID, caller)); | |||
} | |||
public void AddSwapGroupOperation(ExclusiveBuildGroup fromGroupID, ExclusiveBuildGroup toGroupID, string caller) | |||
public void QueueSwapOperation(EGID fromID, EGID toID, IComponentBuilder[] componentBuilders, string caller) | |||
{ | |||
_thisSubmissionInfo._groupsToSwap.Add((fromGroupID, toGroupID, caller)); | |||
_thisSubmissionInfo._entitiesSwapped.Add((fromID, toID)); | |||
//todo: limit the number of dictionaries that can be cached | |||
//recycle or create dictionaries of components per group | |||
//Get the dictionary that holds the entities that are swapping from fromID | |||
var swappedComponentsPerType = _thisSubmissionInfo._currentSwapEntitiesOperations.RecycleOrAdd( | |||
fromID.groupID, _newGroupsDictionaryWithCaller, _recycleGroupDictionaryWithCaller); | |||
var componentBuildersLength = componentBuilders.Length - 1; | |||
for (var index = componentBuildersLength; index >= 0; index--) | |||
{ | |||
var operation = componentBuilders[index]; | |||
//Get the dictionary for each component that holds the list of entities to swap | |||
swappedComponentsPerType //recycle or create dictionaries per component type | |||
.RecycleOrAdd(new RefWrapperType(operation.GetEntityComponentType()), _newGroupDictionary, _recycleDicitionaryWithCaller) | |||
//recycle or create list of entities to swap | |||
.RecycleOrAdd(toID.groupID, _newListWithCaller, _clearListWithCaller) | |||
//add entity to swap | |||
.Add((fromID.entityID, toID.entityID, caller)); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.Synchronized)] | |||
public bool AnyOperationQueued() | |||
{ | |||
return _thisSubmissionInfo.AnyOperationQueued(); | |||
} | |||
public void ExecuteRemoveAndSwappingOperations( | |||
Action<FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, | |||
FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>>, FasterList<(EGID, EGID)> | |||
, | |||
EnginesRoot> swapEntities, | |||
Action<FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, FasterList<(uint, string)>>>, | |||
FasterList<EGID>, EnginesRoot> removeEntities, Action<ExclusiveGroupStruct, EnginesRoot> removeGroup, | |||
public void ExecuteRemoveAndSwappingOperations(Action<FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, | |||
FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>>, FasterList<(EGID, EGID)>, | |||
EnginesRoot> swapEntities, Action<FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, FasterList<(uint, string)>>>, | |||
FasterList<EGID>, EnginesRoot> removeEntities, Action<ExclusiveGroupStruct, EnginesRoot> removeGroup, | |||
Action<ExclusiveGroupStruct, ExclusiveGroupStruct, EnginesRoot> swapGroup, EnginesRoot enginesRoot) | |||
{ | |||
(_thisSubmissionInfo, _lastSubmittedInfo) = (_lastSubmittedInfo, _thisSubmissionInfo); | |||
@@ -85,7 +97,6 @@ namespace Svelto.ECS | |||
/// todo: entity references should be updated before calling all the methods to avoid callbacks handling | |||
/// references that should be marked as invalid. | |||
foreach (var (group, caller) in _lastSubmittedInfo._groupsToRemove) | |||
{ | |||
try | |||
{ | |||
removeGroup(group, enginesRoot); | |||
@@ -93,16 +104,14 @@ namespace Svelto.ECS | |||
catch | |||
{ | |||
var str = "Crash while removing a whole group on ".FastConcat(group.ToString()) | |||
.FastConcat(" from : ", caller); | |||
.FastConcat(" from : ", caller); | |||
Console.LogError(str); | |||
throw; | |||
} | |||
} | |||
foreach (var (fromGroup, toGroup, caller) in _lastSubmittedInfo._groupsToSwap) | |||
{ | |||
try | |||
{ | |||
swapGroup(fromGroup, toGroup, enginesRoot); | |||
@@ -110,32 +119,80 @@ namespace Svelto.ECS | |||
catch | |||
{ | |||
var str = "Crash while swapping a whole group on " | |||
.FastConcat(fromGroup.ToString(), " ", toGroup.ToString()).FastConcat(" from : ", caller); | |||
.FastConcat(fromGroup.ToString(), " ", toGroup.ToString()).FastConcat(" from : ", caller); | |||
Console.LogError(str); | |||
throw; | |||
} | |||
} | |||
if (_lastSubmittedInfo._entitiesSwapped.count > 0) | |||
swapEntities(_lastSubmittedInfo._currentSwapEntitiesOperations, _lastSubmittedInfo._entitiesSwapped, | |||
enginesRoot); | |||
swapEntities(_lastSubmittedInfo._currentSwapEntitiesOperations, _lastSubmittedInfo._entitiesSwapped | |||
, enginesRoot); | |||
if (_lastSubmittedInfo._entitiesRemoved.count > 0) | |||
removeEntities(_lastSubmittedInfo._currentRemoveEntitiesOperations, _lastSubmittedInfo._entitiesRemoved, | |||
enginesRoot); | |||
removeEntities(_lastSubmittedInfo._currentRemoveEntitiesOperations, _lastSubmittedInfo._entitiesRemoved | |||
, enginesRoot); | |||
_lastSubmittedInfo.Clear(); | |||
} | |||
FasterDictionary<RefWrapperType, FasterList<(uint, string)>> NewGroupsDictionary() | |||
{ | |||
return new FasterDictionary<RefWrapperType, FasterList<(uint, string)>>(); | |||
} | |||
void RecycleDictionary(ref FasterDictionary<RefWrapperType, FasterList<(uint, string)>> recycled) | |||
{ | |||
recycled.Recycle(); | |||
} | |||
FasterList<(uint, string)> NewList() | |||
{ | |||
return new FasterList<(uint, string)>(); | |||
} | |||
public bool AnyOperationQueued() => _thisSubmissionInfo.AnyOperationQueued(); | |||
void ClearList(ref FasterList<(uint, string)> target) | |||
{ | |||
target.Clear(); | |||
} | |||
void RecycleDicitionaryWithCaller(ref FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>> target) | |||
{ | |||
target.Recycle(); | |||
} | |||
void ClearListWithCaller(ref FasterList<(uint, uint, string)> target) | |||
{ | |||
target.Clear(); | |||
} | |||
FasterList<(uint, uint, string)> NewListWithCaller() | |||
{ | |||
return new FasterList<(uint, uint, string)>(); | |||
} | |||
FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>> NewGroupsDictionaryWithCaller() | |||
{ | |||
return new FasterDictionary<RefWrapperType, //add case | |||
FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>(); | |||
} | |||
void RecycleGroupDictionaryWithCaller(ref FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>> recycled) | |||
{ | |||
recycled.Recycle(); | |||
} | |||
FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>> NewGroupDictionary() | |||
{ | |||
return new FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>(); | |||
} | |||
struct Info | |||
{ | |||
//from group //actual component type | |||
//from group //actual component type | |||
internal FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, | |||
// to group ID //entityIDs , debugInfo | |||
// to group ID //entityIDs , debugInfo | |||
FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>> | |||
_currentSwapEntitiesOperations; | |||
@@ -147,18 +204,21 @@ namespace Svelto.ECS | |||
public FasterList<(ExclusiveBuildGroup, ExclusiveBuildGroup, string)> _groupsToSwap; | |||
public FasterList<(ExclusiveBuildGroup, string)> _groupsToRemove; | |||
internal bool AnyOperationQueued() => | |||
_entitiesSwapped.count > 0 || _entitiesRemoved.count > 0 || _groupsToSwap.count > 0 || | |||
_groupsToRemove.count > 0; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal bool AnyOperationQueued() | |||
{ | |||
return _entitiesSwapped.count > 0 || _entitiesRemoved.count > 0 || _groupsToSwap.count > 0 | |||
|| _groupsToRemove.count > 0; | |||
} | |||
internal void Clear() | |||
{ | |||
_currentSwapEntitiesOperations.FastClear(); | |||
_currentRemoveEntitiesOperations.FastClear(); | |||
_entitiesSwapped.FastClear(); | |||
_entitiesRemoved.FastClear(); | |||
_groupsToRemove.FastClear(); | |||
_groupsToSwap.FastClear(); | |||
_currentSwapEntitiesOperations.Recycle(); | |||
_currentRemoveEntitiesOperations.Recycle(); | |||
_entitiesSwapped.Clear(); | |||
_entitiesRemoved.Clear(); | |||
_groupsToRemove.Clear(); | |||
_groupsToSwap.Clear(); | |||
} | |||
internal void Init() | |||
@@ -177,7 +237,18 @@ namespace Svelto.ECS | |||
} | |||
} | |||
Info _thisSubmissionInfo; | |||
Info _lastSubmittedInfo; | |||
Info _thisSubmissionInfo; | |||
readonly Func<FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>> _newGroupDictionary; | |||
readonly Func<FasterDictionary<RefWrapperType, FasterList<(uint, string)>>> _newGroupsDictionary; | |||
readonly ActionRef<FasterDictionary<RefWrapperType, FasterList<(uint, string)>>> _recycleDictionary; | |||
readonly Func<FasterList<(uint, string)>> _newList; | |||
readonly ActionRef<FasterList<(uint, string)>> _clearList; | |||
readonly Func<FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>> _newGroupsDictionaryWithCaller; | |||
readonly ActionRef<FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>>> _recycleGroupDictionaryWithCaller; | |||
readonly ActionRef<FasterDictionary<ExclusiveGroupStruct, FasterList<(uint, uint, string)>>> _recycleDicitionaryWithCaller; | |||
readonly Func<FasterList<(uint, uint, string)>> _newListWithCaller; | |||
readonly ActionRef<FasterList<(uint, uint, string)>> _clearListWithCaller; | |||
} | |||
} |
@@ -4,7 +4,7 @@ using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly ref struct EntityCollection<T> where T : struct, IBaseEntityComponent | |||
public readonly ref struct EntityCollection<T> where T : struct, _IInternalEntityComponent | |||
{ | |||
public EntityCollection(IBuffer<T> buffer, IEntityIDs entityIDs, uint count) : this() | |||
{ | |||
@@ -21,8 +21,8 @@ namespace Svelto.ECS | |||
internal readonly IEntityIDs _entityIDs; | |||
} | |||
public readonly ref struct EntityCollection<T1, T2> where T1 : struct, IBaseEntityComponent | |||
where T2 : struct, IBaseEntityComponent | |||
public readonly ref struct EntityCollection<T1, T2> where T1 : struct, _IInternalEntityComponent | |||
where T2 : struct, _IInternalEntityComponent | |||
{ | |||
internal EntityCollection(in EntityCollection<T1> array1, in EntityCollection<T2> array2) | |||
{ | |||
@@ -45,9 +45,9 @@ namespace Svelto.ECS | |||
} | |||
} | |||
public readonly ref struct EntityCollection<T1, T2, T3> where T3 : struct, IBaseEntityComponent | |||
where T2 : struct, IBaseEntityComponent | |||
where T1 : struct, IBaseEntityComponent | |||
public readonly ref struct EntityCollection<T1, T2, T3> where T3 : struct, _IInternalEntityComponent | |||
where T2 : struct, _IInternalEntityComponent | |||
where T1 : struct, _IInternalEntityComponent | |||
{ | |||
internal EntityCollection | |||
(in EntityCollection<T1> array1, in EntityCollection<T2> array2, in EntityCollection<T3> array3) | |||
@@ -78,10 +78,10 @@ namespace Svelto.ECS | |||
public int count => (int)buffer1.count; | |||
} | |||
public readonly ref struct EntityCollection<T1, T2, T3, T4> where T1 : struct, IBaseEntityComponent | |||
where T2 : struct, IBaseEntityComponent | |||
where T3 : struct, IBaseEntityComponent | |||
where T4 : struct, IBaseEntityComponent | |||
public readonly ref struct EntityCollection<T1, T2, T3, T4> where T1 : struct, _IInternalEntityComponent | |||
where T2 : struct, _IInternalEntityComponent | |||
where T3 : struct, _IInternalEntityComponent | |||
where T4 : struct, _IInternalEntityComponent | |||
{ | |||
internal EntityCollection | |||
(in EntityCollection<T1> array1, in EntityCollection<T2> array2, in EntityCollection<T3> array3 | |||
@@ -1,5 +1,6 @@ | |||
using System; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -70,21 +71,21 @@ namespace Svelto.ECS | |||
_componentsToBuild = Construct(extraEntities.count, extraEntities.ToArrayFast(out _)); | |||
} | |||
public void Add<T>() where T : struct, IBaseEntityComponent | |||
public void Add<T>() where T : struct, _IInternalEntityComponent | |||
{ | |||
IComponentBuilder[] extraEntities = { new ComponentBuilder<T>() }; | |||
_componentsToBuild = Construct(extraEntities.Length, extraEntities); | |||
} | |||
public void Add<T, U>() where T : struct, IBaseEntityComponent where U : struct, IBaseEntityComponent | |||
public void Add<T, U>() where T : struct, _IInternalEntityComponent where U : struct, _IInternalEntityComponent | |||
{ | |||
IComponentBuilder[] extraEntities = { new ComponentBuilder<T>(), new ComponentBuilder<U>() }; | |||
_componentsToBuild = Construct(extraEntities.Length, extraEntities); | |||
} | |||
public void Add<T, U, V>() where T : struct, IBaseEntityComponent | |||
where U : struct, IBaseEntityComponent | |||
where V : struct, IBaseEntityComponent | |||
public void Add<T, U, V>() where T : struct, _IInternalEntityComponent | |||
where U : struct, _IInternalEntityComponent | |||
where V : struct, _IInternalEntityComponent | |||
{ | |||
IComponentBuilder[] extraEntities = | |||
{ | |||
@@ -1,3 +1,5 @@ | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
@@ -53,15 +55,15 @@ namespace Svelto.ECS | |||
return this; | |||
} | |||
protected void Add<T>() where T : struct, IBaseEntityComponent | |||
protected void Add<T>() where T : struct, _IInternalEntityComponent | |||
{ | |||
_dynamicDescriptor.Add<T>(); | |||
} | |||
protected void Add<T, U>() where T : struct, IBaseEntityComponent where U : struct, IBaseEntityComponent | |||
protected void Add<T, U>() where T : struct, _IInternalEntityComponent where U : struct, _IInternalEntityComponent | |||
{ | |||
_dynamicDescriptor.Add<T, U>(); | |||
} | |||
protected void Add<T, U, V>() where T : struct, IBaseEntityComponent where U : struct, IBaseEntityComponent where V : struct, IBaseEntityComponent | |||
protected void Add<T, U, V>() where T : struct, _IInternalEntityComponent where U : struct, _IInternalEntityComponent where V : struct, _IInternalEntityComponent | |||
{ | |||
_dynamicDescriptor.Add<T, U, V>(); | |||
} | |||
@@ -1,28 +1,45 @@ | |||
namespace Svelto.ECS | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public abstract class GenericEntityDescriptor<T> : IEntityDescriptor where T : struct, IBaseEntityComponent | |||
public abstract class GenericEntityDescriptor<T>: IEntityDescriptor | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
static readonly IComponentBuilder[] _componentBuilders; | |||
static GenericEntityDescriptor() { _componentBuilders = new IComponentBuilder[] {new ComponentBuilder<T>()}; } | |||
static GenericEntityDescriptor() | |||
{ | |||
_componentBuilders = new IComponentBuilder[] | |||
{ | |||
new ComponentBuilder<T>() | |||
}; | |||
} | |||
public IComponentBuilder[] componentsToBuild => _componentBuilders; | |||
} | |||
public abstract class GenericEntityDescriptor<T, U> : IEntityDescriptor | |||
where T : struct, IBaseEntityComponent where U : struct, IBaseEntityComponent | |||
public abstract class GenericEntityDescriptor<T, U>: IEntityDescriptor | |||
where T : struct, _IInternalEntityComponent | |||
where U : struct, _IInternalEntityComponent | |||
{ | |||
static readonly IComponentBuilder[] _componentBuilders; | |||
static GenericEntityDescriptor() | |||
{ | |||
_componentBuilders = new IComponentBuilder[] {new ComponentBuilder<T>(), new ComponentBuilder<U>()}; | |||
_componentBuilders = new IComponentBuilder[] | |||
{ | |||
new ComponentBuilder<T>(), | |||
new ComponentBuilder<U>() | |||
}; | |||
} | |||
public IComponentBuilder[] componentsToBuild => _componentBuilders; | |||
} | |||
public abstract class GenericEntityDescriptor<T, U, V> : IEntityDescriptor | |||
where T : struct, IBaseEntityComponent where U : struct, IBaseEntityComponent where V : struct, IBaseEntityComponent | |||
public abstract class GenericEntityDescriptor<T, U, V>: IEntityDescriptor | |||
where T : struct, _IInternalEntityComponent | |||
where U : struct, _IInternalEntityComponent | |||
where V : struct, _IInternalEntityComponent | |||
{ | |||
static readonly IComponentBuilder[] _componentBuilders; | |||
@@ -30,18 +47,20 @@ | |||
{ | |||
_componentBuilders = new IComponentBuilder[] | |||
{ | |||
new ComponentBuilder<T>(), | |||
new ComponentBuilder<U>(), | |||
new ComponentBuilder<V>() | |||
new ComponentBuilder<T>(), | |||
new ComponentBuilder<U>(), | |||
new ComponentBuilder<V>() | |||
}; | |||
} | |||
public IComponentBuilder[] componentsToBuild => _componentBuilders; | |||
} | |||
public abstract class GenericEntityDescriptor<T, U, V, W> : IEntityDescriptor | |||
where T : struct, IBaseEntityComponent where U : struct, IBaseEntityComponent where V : struct, IBaseEntityComponent | |||
where W : struct, IBaseEntityComponent | |||
public abstract class GenericEntityDescriptor<T, U, V, W>: IEntityDescriptor | |||
where T : struct, _IInternalEntityComponent | |||
where U : struct, _IInternalEntityComponent | |||
where V : struct, _IInternalEntityComponent | |||
where W : struct, _IInternalEntityComponent | |||
{ | |||
static readonly IComponentBuilder[] _componentBuilders; | |||
@@ -49,19 +68,22 @@ | |||
{ | |||
_componentBuilders = new IComponentBuilder[] | |||
{ | |||
new ComponentBuilder<T>(), | |||
new ComponentBuilder<U>(), | |||
new ComponentBuilder<V>(), | |||
new ComponentBuilder<W>() | |||
new ComponentBuilder<T>(), | |||
new ComponentBuilder<U>(), | |||
new ComponentBuilder<V>(), | |||
new ComponentBuilder<W>() | |||
}; | |||
} | |||
public IComponentBuilder[] componentsToBuild => _componentBuilders; | |||
} | |||
public abstract class GenericEntityDescriptor<T, U, V, W, X> : IEntityDescriptor | |||
where T : struct, IBaseEntityComponent where U : struct, IBaseEntityComponent where V : struct, IBaseEntityComponent | |||
where W : struct, IBaseEntityComponent where X : struct, IBaseEntityComponent | |||
public abstract class GenericEntityDescriptor<T, U, V, W, X>: IEntityDescriptor | |||
where T : struct, _IInternalEntityComponent | |||
where U : struct, _IInternalEntityComponent | |||
where V : struct, _IInternalEntityComponent | |||
where W : struct, _IInternalEntityComponent | |||
where X : struct, _IInternalEntityComponent | |||
{ | |||
static readonly IComponentBuilder[] _componentBuilders; | |||
@@ -69,20 +91,24 @@ | |||
{ | |||
_componentBuilders = new IComponentBuilder[] | |||
{ | |||
new ComponentBuilder<T>(), | |||
new ComponentBuilder<U>(), | |||
new ComponentBuilder<V>(), | |||
new ComponentBuilder<W>(), | |||
new ComponentBuilder<X>() | |||
new ComponentBuilder<T>(), | |||
new ComponentBuilder<U>(), | |||
new ComponentBuilder<V>(), | |||
new ComponentBuilder<W>(), | |||
new ComponentBuilder<X>() | |||
}; | |||
} | |||
public IComponentBuilder[] componentsToBuild => _componentBuilders; | |||
} | |||
public abstract class GenericEntityDescriptor<T, U, V, W, X, Y> : IEntityDescriptor | |||
where T : struct, IBaseEntityComponent where U : struct, IBaseEntityComponent where V : struct, IBaseEntityComponent | |||
where W : struct, IBaseEntityComponent where X : struct, IBaseEntityComponent where Y : struct, IBaseEntityComponent | |||
public abstract class GenericEntityDescriptor<T, U, V, W, X, Y>: IEntityDescriptor | |||
where T : struct, _IInternalEntityComponent | |||
where U : struct, _IInternalEntityComponent | |||
where V : struct, _IInternalEntityComponent | |||
where W : struct, _IInternalEntityComponent | |||
where X : struct, _IInternalEntityComponent | |||
where Y : struct, _IInternalEntityComponent | |||
{ | |||
static readonly IComponentBuilder[] _componentBuilders; | |||
@@ -90,12 +116,12 @@ | |||
{ | |||
_componentBuilders = new IComponentBuilder[] | |||
{ | |||
new ComponentBuilder<T>(), | |||
new ComponentBuilder<U>(), | |||
new ComponentBuilder<V>(), | |||
new ComponentBuilder<W>(), | |||
new ComponentBuilder<X>(), | |||
new ComponentBuilder<Y>() | |||
new ComponentBuilder<T>(), | |||
new ComponentBuilder<U>(), | |||
new ComponentBuilder<V>(), | |||
new ComponentBuilder<W>(), | |||
new ComponentBuilder<X>(), | |||
new ComponentBuilder<Y>() | |||
}; | |||
} | |||
@@ -1,6 +1,9 @@ | |||
using Svelto.ECS.Hybrid; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
struct EntityInfoComponent: IBaseEntityComponent | |||
struct EntityInfoComponent: IManagedComponent | |||
{ | |||
public IComponentBuilder[] componentsToBuild; | |||
} |
@@ -1,4 +1,5 @@ | |||
using Svelto.DataStructures; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
@@ -8,17 +9,18 @@ namespace Svelto.ECS | |||
public EntityInitializer(EGID id, FasterDictionary<RefWrapperType, ITypeSafeDictionary> group, | |||
in EntityReference reference) | |||
{ | |||
_group = group; | |||
_ID = id; | |||
_group = group; | |||
_ID = id; | |||
this.reference = reference; | |||
} | |||
public EGID EGID => _ID; | |||
public EGID EGID => _ID; | |||
public readonly EntityReference reference; | |||
public void Init<T>(T initializer) where T : struct, IBaseEntityComponent | |||
public void Init<T>(T initializer) where T : struct, _IInternalEntityComponent | |||
{ | |||
if (_group.TryGetValue(new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE), | |||
if (_group.TryGetValue( | |||
new RefWrapperType(TypeCache<T>.type), | |||
out var typeSafeDictionary) == false) | |||
return; | |||
@@ -32,24 +34,26 @@ namespace Svelto.ECS | |||
dictionary.GetDirectValueByRef(findElementIndex) = initializer; | |||
} | |||
public ref T GetOrAdd<T>() where T : struct, IBaseEntityComponent | |||
internal ref T GetOrAdd<T>() where T : unmanaged, IEntityComponent | |||
{ | |||
ref var entityDictionary = ref _group.GetOrAdd( | |||
new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE), TypeSafeDictionaryFactory<T>.Create); | |||
new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE), | |||
() => new UnmanagedTypeSafeDictionary<T>(1)); | |||
var dictionary = (ITypeSafeDictionary<T>)entityDictionary; | |||
return ref dictionary.GetOrAdd(_ID.entityID); | |||
} | |||
public ref T Get<T>() where T : struct, IBaseEntityComponent | |||
public ref T Get<T>() where T : struct, _IInternalEntityComponent | |||
{ | |||
return ref (_group[new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE)] as ITypeSafeDictionary<T>) | |||
return ref (_group[new RefWrapperType(TypeCache<T>.type)] as ITypeSafeDictionary<T>) | |||
.GetValueByRef(_ID.entityID); | |||
} | |||
public bool Has<T>() where T : struct, IBaseEntityComponent | |||
public bool Has<T>() where T : struct, _IInternalEntityComponent | |||
{ | |||
if (_group.TryGetValue(new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE), | |||
if (_group.TryGetValue( | |||
new RefWrapperType(TypeCache<T>.type), | |||
out var typeSafeDictionary)) | |||
{ | |||
var dictionary = (ITypeSafeDictionary<T>)typeSafeDictionary; | |||
@@ -61,7 +65,7 @@ namespace Svelto.ECS | |||
return false; | |||
} | |||
readonly EGID _ID; | |||
readonly EGID _ID; | |||
readonly FasterDictionary<RefWrapperType, ITypeSafeDictionary> _group; | |||
} | |||
} |
@@ -1,13 +1,14 @@ | |||
using System.Runtime.CompilerServices; | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Native; | |||
using Svelto.ECS.DataStructures; | |||
using Svelto.ECS.Reference; | |||
namespace Svelto.ECS | |||
{ | |||
// The EntityLocatorMap provides a bidirectional map to help locate entities without using an EGID which might | |||
// change in runtime. The Entity Locator map uses a reusable unique identifier struct called EntityLocator to | |||
// change at runtime. The Entity Locator map uses a reusable unique identifier struct called EntityLocator to | |||
// find the last known EGID from last entity submission. | |||
public partial class EnginesRoot | |||
{ | |||
@@ -15,8 +16,8 @@ namespace Svelto.ECS | |||
{ | |||
internal EntityReference ClaimReference() | |||
{ | |||
int tempFreeIndex; | |||
int newFreeIndex; | |||
int tempFreeIndex; | |||
int newFreeIndex; | |||
uint version; | |||
do | |||
@@ -26,14 +27,14 @@ namespace Svelto.ECS | |||
if ((uint)tempFreeIndex >= _entityReferenceMap.count) | |||
{ | |||
newFreeIndex = tempFreeIndex + 1; | |||
version = 0; | |||
version = 0; | |||
} | |||
else | |||
{ | |||
ref EntityReferenceMapElement element = ref _entityReferenceMap[tempFreeIndex]; | |||
// The recycle entities form a linked list, using the egid.entityID to store the next element. | |||
newFreeIndex = (int)element.egid.entityID; | |||
version = element.version; | |||
version = element.version; | |||
} | |||
} while (tempFreeIndex != _nextFreeIndex.CompareExchange(newFreeIndex, tempFreeIndex)); | |||
@@ -51,7 +52,8 @@ namespace Svelto.ECS | |||
internal void SetReference(EntityReference reference, EGID egid) | |||
{ | |||
// Since references can be claimed in parallel now, it might happen that they are set out of order, | |||
// so we need to resize instead of add. | |||
// so we need to resize instead of add. TODO: what did this comment mean? | |||
if (reference.index >= _entityReferenceMap.count) | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO //THIS IS TO VALIDATE DATE DBC LIKE | |||
@@ -66,19 +68,18 @@ namespace Svelto.ECS | |||
#if DEBUG && !PROFILE_SVELTO | |||
// These debug tests should be enough to detect if indices are being used correctly under native factories | |||
if (_entityReferenceMap[reference.index].version != reference.version || | |||
_entityReferenceMap[reference.index].egid.groupID != ExclusiveGroupStruct.Invalid) | |||
ref var entityReferenceMapElement = ref _entityReferenceMap[reference.index]; | |||
if (entityReferenceMapElement.version != reference.version | |||
|| entityReferenceMapElement.egid.groupID != ExclusiveGroupStruct.Invalid) | |||
{ | |||
throw new ECSException("Entity reference already set. This should never happen, please report it."); | |||
} | |||
#endif | |||
_entityReferenceMap[reference.index] = new EntityReferenceMapElement(egid, reference.version); | |||
// Update reverse map from egid to locator. | |||
var groupMap = | |||
_egidToReferenceMap.GetOrAdd(egid.groupID | |||
, () => new SharedSveltoDictionaryNative<uint, EntityReference>(0)); | |||
var groupMap = _egidToReferenceMap.GetOrAdd( | |||
egid.groupID, () => new SharedSveltoDictionaryNative<uint, EntityReference>(0)); | |||
groupMap[egid.entityID] = reference; | |||
} | |||
@@ -89,9 +90,8 @@ namespace Svelto.ECS | |||
_entityReferenceMap[reference.index].egid = to; | |||
var groupMap = | |||
_egidToReferenceMap.GetOrAdd( | |||
to.groupID, () => new SharedSveltoDictionaryNative<uint, EntityReference>(0)); | |||
var groupMap = _egidToReferenceMap.GetOrAdd( | |||
to.groupID, () => new SharedSveltoDictionaryNative<uint, EntityReference>(0)); | |||
groupMap[to.entityID] = reference; | |||
} | |||
@@ -102,7 +102,7 @@ namespace Svelto.ECS | |||
// Invalidate the entity locator element by bumping its version and setting the egid to point to a not existing element. | |||
ref var entityReferenceMapElement = ref _entityReferenceMap[reference.index]; | |||
entityReferenceMapElement.egid = new EGID((uint)(int)_nextFreeIndex, 0); | |||
entityReferenceMapElement.egid = new EGID((uint)(int)_nextFreeIndex, 0); //keep the free linked list updated | |||
entityReferenceMapElement.version++; | |||
// Mark the element as the last element used. | |||
@@ -112,8 +112,8 @@ namespace Svelto.ECS | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
EntityReference FetchAndRemoveReference(EGID @from) | |||
{ | |||
var egidToReference = _egidToReferenceMap[@from.groupID]; | |||
var reference = egidToReference[@from.entityID]; | |||
SharedSveltoDictionaryNative<uint, EntityReference> egidToReference = _egidToReferenceMap[@from.groupID]; | |||
EntityReference reference = egidToReference[@from.entityID]; //todo: double searching fro entityID | |||
egidToReference.Remove(@from.entityID); | |||
return reference; | |||
@@ -154,12 +154,22 @@ namespace Svelto.ECS | |||
if (groupMap.TryGetValue(egid.entityID, out var locator)) | |||
return locator; | |||
#if DEBUG && !PROFILE_SVELTO | |||
else throw new ECSException($"Entity {egid} does not exist. Are you creating it? Try getting it from initializer.reference."); | |||
else | |||
throw new ECSException( | |||
$"Entity {egid} does not exist. Are you creating it? Try getting it from initializer.reference."); | |||
#endif | |||
} | |||
return EntityReference.Invalid; | |||
} | |||
public SharedSveltoDictionaryNative<uint, EntityReference> GetEntityReferenceMap(ExclusiveGroupStruct groupID) | |||
{ | |||
if (_egidToReferenceMap.TryGetValue(groupID, out var groupMap) == false) | |||
throw new ECSException("reference group map not found"); | |||
return groupMap; | |||
} | |||
public bool TryGetEGID(EntityReference reference, out EGID egid) | |||
{ | |||
@@ -168,9 +178,10 @@ namespace Svelto.ECS | |||
return false; | |||
// Make sure we are querying for the current version of the locator. | |||
// Otherwise the locator is pointing to a removed entity. | |||
if (_entityReferenceMap[reference.index].version == reference.version) | |||
ref var entityReferenceMapElement = ref _entityReferenceMap[reference.index]; | |||
if (entityReferenceMapElement.version == reference.version) | |||
{ | |||
egid = _entityReferenceMap[reference.index].egid; | |||
egid = entityReferenceMapElement.egid; | |||
return true; | |||
} | |||
@@ -183,17 +194,17 @@ namespace Svelto.ECS | |||
throw new ECSException("Invalid Reference"); | |||
// Make sure we are querying for the current version of the locator. | |||
// Otherwise the locator is pointing to a removed entity. | |||
if (_entityReferenceMap[reference.index].version != reference.version) | |||
ref var entityReferenceMapElement = ref _entityReferenceMap[reference.index]; | |||
if (entityReferenceMapElement.version != reference.version) | |||
throw new ECSException("outdated Reference"); | |||
return _entityReferenceMap[reference.index].egid; | |||
return entityReferenceMapElement.egid; | |||
} | |||
internal void PreallocateReferenceMaps(ExclusiveGroupStruct groupID, uint size) | |||
{ | |||
_egidToReferenceMap | |||
.GetOrAdd(groupID, () => new SharedSveltoDictionaryNative<uint, EntityReference>(size)) | |||
.EnsureCapacity(size); | |||
_egidToReferenceMap.GetOrAdd( | |||
groupID, () => new SharedSveltoDictionaryNative<uint, EntityReference>(size)).EnsureCapacity(size); | |||
_entityReferenceMap.Resize(size); | |||
} | |||
@@ -219,15 +230,21 @@ namespace Svelto.ECS | |||
_egidToReferenceMap.Dispose(); | |||
} | |||
SharedNativeInt _nextFreeIndex; | |||
SharedNativeInt _nextFreeIndex; | |||
NativeDynamicArrayCast<EntityReferenceMapElement> _entityReferenceMap; | |||
//todo: this should be just one dictionary <EGID, REference> it's a double one to be | |||
//able to remove entire groups at once. IT's wasteful since the operation is very rare | |||
//we should find an alternative solution | |||
//alternatively since the groups are guaranteed to be sequential an array should be used instead | |||
//than a dictionary for groups. It could be a good case to implement a 4k chunk based sparseset | |||
SharedSveltoDictionaryNative<ExclusiveGroupStruct, SharedSveltoDictionaryNative<uint, EntityReference>> | |||
_egidToReferenceMap; | |||
} | |||
EntityReferenceMap entityLocator => _entityLocator; | |||
EntityReferenceMap _entityLocator; | |||
EntityReferenceMap _entityLocator; | |||
} | |||
} |
@@ -1,4 +1,5 @@ | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures.Native; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -27,5 +28,10 @@ namespace Svelto.ECS | |||
{ | |||
return _entityReferencesMap.GetEntityReference(egid); | |||
} | |||
public SharedSveltoDictionaryNative<uint, EntityReference> GetEntityReferenceMap(ExclusiveGroupStruct groupID) | |||
{ | |||
return _entityReferencesMap.GetEntityReferenceMap(groupID); | |||
} | |||
} | |||
} |
@@ -9,8 +9,8 @@ namespace Svelto.ECS | |||
/// <summary> | |||
/// Todo: EntityReference shouldn't map EGIDs as dictionaries keys but directly the indices in the EntityDB arrays | |||
/// </summary> | |||
[Serialization.DoNotSerialize] | |||
[Serializable] | |||
[Serialization.DoNotSerialize] //it's not a serializable field for svelto serializable system | |||
[Serializable] | |||
[StructLayout(LayoutKind.Explicit)] | |||
public struct EntityReference : IEquatable<EntityReference> | |||
{ | |||
@@ -34,6 +34,8 @@ namespace Svelto.ECS | |||
public EntityReference(uint uniqueId) : this(uniqueId, 0) {} | |||
public EntityReference(ulong GID):this() { _GID = GID; } | |||
public EntityReference(uint uniqueId, uint version) : this() | |||
{ | |||
_GID = MAKE_GLOBAL_ID(uniqueId, version); | |||
@@ -70,6 +72,11 @@ namespace Svelto.ECS | |||
return entitiesDB.TryGetEGID(this, out egid); | |||
} | |||
public ulong ToULong() | |||
{ | |||
return _GID; | |||
} | |||
static ulong MAKE_GLOBAL_ID(uint uniqueId, uint version) | |||
{ | |||
return (ulong)version << 32 | ((ulong)uniqueId & 0xFFFFFFFF); | |||
@@ -1,10 +0,0 @@ | |||
namespace Svelto.ECS | |||
{ | |||
enum EntitySubmitOperationType | |||
{ | |||
Swap, | |||
Remove, | |||
RemoveGroup, | |||
SwapGroup | |||
} | |||
} |
@@ -1,6 +1,8 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Hybrid; | |||
using Svelto.ECS.Internal; | |||
using Svelto.Utilities; | |||
namespace Svelto.ECS | |||
@@ -34,7 +36,7 @@ namespace Svelto.ECS | |||
internal static void SetEntityViewComponentImplementors<T>(this IComponentBuilder componentBuilder, | |||
ref T entityComponent, IEnumerable<object> implementors, | |||
ComponentBuilder<T>.EntityViewComponentCache localCache) where T : struct, IBaseEntityComponent | |||
ComponentBuilder<T>.EntityViewComponentCache localCache) where T : struct, _IInternalEntityComponent | |||
{ | |||
DBC.ECS.Check.Require(implementors != null, | |||
NULL_IMPLEMENTOR_ERROR.FastConcat(" entityComponent ", | |||
@@ -0,0 +1,20 @@ | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct CombinedFilterID | |||
{ | |||
internal readonly long id; | |||
public FilterContextID contextID => new FilterContextID((uint)((id & 0xFFFF0000) >> 16)); | |||
public uint filterID => (uint)(id >> 32); | |||
public CombinedFilterID(int filterID, FilterContextID contextID) | |||
{ | |||
id = (long)filterID << 32 | (uint)contextID.id << 16; | |||
} | |||
public static implicit operator CombinedFilterID((int filterID, FilterContextID contextID) data) | |||
{ | |||
return new CombinedFilterID(data.filterID, data.contextID); | |||
} | |||
} | |||
} |
@@ -1,7 +1,5 @@ | |||
using System.Collections.Generic; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Native; | |||
using Svelto.ECS.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
@@ -67,7 +65,7 @@ namespace Svelto.ECS | |||
var filters = _persistentEntityFilters.unsafeValues; | |||
//remove duplicates | |||
_transientEntityIDsLeftWithoutDuplicates.FastClear(); | |||
_transientEntityIDsLeftWithoutDuplicates.Clear(); | |||
var entityAffectedCount = entityIDsLeftAndAffectedByRemoval.count; | |||
for (int i = 0; i < entityAffectedCount; i++) | |||
{ | |||
@@ -129,7 +127,7 @@ namespace Svelto.ECS | |||
var numberOfFilters = listOfFilters.count; | |||
//remove duplicates | |||
_transientEntityIDsLeftWithoutDuplicates.FastClear(); | |||
_transientEntityIDsLeftWithoutDuplicates.Clear(); | |||
var entityAffectedCount = entityIDsLeftAndAffectedByRemoval.count; | |||
for (int i = 0; i < entityAffectedCount; i++) | |||
{ | |||
@@ -1,57 +1,24 @@ | |||
using System; | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using System.Threading; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Native; | |||
using Svelto.ECS.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public struct FilterContextID | |||
{ | |||
public readonly uint id; | |||
internal FilterContextID(uint id) | |||
{ | |||
DBC.ECS.Check.Require(id < ushort.MaxValue, "too many types registered, HOW :)"); | |||
this.id = id; | |||
} | |||
public static FilterContextID GetNewContextID() | |||
{ | |||
return EntitiesDB.SveltoFilters.GetNewContextID(); | |||
} | |||
} | |||
public readonly struct CombinedFilterID | |||
{ | |||
internal readonly long id; | |||
public FilterContextID contextID => new FilterContextID((uint)((id & 0xFFFF0000) >> 16)); | |||
public uint filterID => (uint)(id >> 32); | |||
public CombinedFilterID(int filterID, FilterContextID contextID) | |||
{ | |||
id = (long)filterID << 32 | (uint)contextID.id << 16; | |||
} | |||
public static implicit operator CombinedFilterID((int filterID, FilterContextID contextID) data) | |||
{ | |||
return new CombinedFilterID(data.filterID, data.contextID); | |||
} | |||
} | |||
//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 | |||
//TypeCounter | |||
public static long CombineFilterIDs<T>(CombinedFilterID combinedFilterID) where T: struct, IBaseEntityComponent | |||
public static long CombineFilterIDs<T>(CombinedFilterID combinedFilterID) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
var id = (uint)ComponentID<T>.id.Data; | |||
var combineFilterIDs = (long)combinedFilterID.id | id; | |||
return combineFilterIDs; | |||
@@ -62,7 +29,8 @@ namespace Svelto.ECS | |||
{ | |||
public SveltoFilters GetFilters() | |||
{ | |||
return new SveltoFilters(_enginesRoot._persistentEntityFilters, | |||
return new SveltoFilters( | |||
_enginesRoot._persistentEntityFilters, | |||
_enginesRoot._indicesOfPersistentFiltersUsedByThisComponent, _enginesRoot._transientEntityFilters); | |||
} | |||
@@ -72,11 +40,11 @@ namespace Svelto.ECS | |||
public readonly struct SveltoFilters | |||
{ | |||
static readonly SharedStaticWrapper<int, Internal_FilterHelper> uniqueContextID; | |||
#if UNITY_BURST | |||
#if UNITY_BURST | |||
[Unity.Burst.BurstDiscard] | |||
//SharedStatic values must be initialized from not burstified code | |||
#endif | |||
#endif | |||
public static FilterContextID GetNewContextID() | |||
{ | |||
return new FilterContextID((uint)Interlocked.Increment(ref uniqueContextID.Data)); | |||
@@ -84,26 +52,28 @@ namespace Svelto.ECS | |||
public SveltoFilters(SharedSveltoDictionaryNative<long, EntityFilterCollection> persistentEntityFilters, | |||
SharedSveltoDictionaryNative<NativeRefWrapperType, NativeDynamicArrayCast<int>> | |||
indicesOfPersistentFiltersUsedByThisComponent, | |||
indicesOfPersistentFiltersUsedByThisComponent, | |||
SharedSveltoDictionaryNative<long, EntityFilterCollection> transientEntityFilters) | |||
{ | |||
_persistentEntityFilters = persistentEntityFilters; | |||
_persistentEntityFilters = persistentEntityFilters; | |||
_indicesOfPersistentFiltersUsedByThisComponent = indicesOfPersistentFiltersUsedByThisComponent; | |||
_transientEntityFilters = transientEntityFilters; | |||
_transientEntityFilters = transientEntityFilters; | |||
} | |||
#if UNITY_BURST | |||
#if UNITY_BURST //the following methods do not make sense without burst as they are workaround for burst | |||
public ref EntityFilterCollection GetOrCreatePersistentFilter<T>(int filterID, | |||
FilterContextID filterContextId, NativeRefWrapperType typeRef) where T : unmanaged, IEntityComponent | |||
FilterContextID filterContextId, NativeRefWrapperType typeRef) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
return ref GetOrCreatePersistentFilter<T>(new CombinedFilterID(filterID, filterContextId), typeRef); | |||
} | |||
public ref EntityFilterCollection GetOrCreatePersistentFilter<T>(CombinedFilterID filterID, | |||
NativeRefWrapperType typeRef) where T : unmanaged, IEntityComponent | |||
NativeRefWrapperType typeRef) | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
long combineFilterIDs = Internal_FilterHelper.CombineFilterIDs<T>(filterID); | |||
if (_persistentEntityFilters.TryFindIndex(combineFilterIDs, out var index) == true) | |||
return ref _persistentEntityFilters.GetDirectValueByRef(index); | |||
@@ -135,59 +105,91 @@ namespace Svelto.ECS | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
#if UNITY_BURST && UNITY_COLLECTIONS | |||
[Unity.Collections.NotBurstCompatible] | |||
[Unity.Burst.BurstDiscard] //not burst compatible because of TypeRefWrapper<T>.wrapper; | |||
#endif | |||
public ref EntityFilterCollection GetOrCreatePersistentFilter<T>(int filterID, FilterContextID filterContextId) | |||
where T : unmanaged, IBaseEntityComponent | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref EntityFilterCollection GetOrCreatePersistentFilter<T>(int filterID, | |||
FilterContextID filterContextId) | |||
where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
return ref GetOrCreatePersistentFilter<T>(new CombinedFilterID(filterID, filterContextId)); | |||
} | |||
#if UNITY_BURST && UNITY_COLLECTIONS | |||
[Unity.Collections.NotBurstCompatible] | |||
[Unity.Burst.BurstDiscard] //not burst compatible because of TypeRefWrapper<T>.wrapper and GetOrAdd callback; | |||
#endif | |||
public ref EntityFilterCollection GetOrCreatePersistentFilter<T>(CombinedFilterID filterID) | |||
where T : unmanaged, IBaseEntityComponent | |||
where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
long combineFilterIDs = Internal_FilterHelper.CombineFilterIDs<T>(filterID); | |||
if (_persistentEntityFilters.TryFindIndex(combineFilterIDs, out var index) == true) | |||
return ref _persistentEntityFilters.GetDirectValueByRef(index); | |||
var typeRef = TypeRefWrapper<T>.wrapper; | |||
var typeRef = TypeRefWrapper<T>.wrapper; | |||
var filterCollection = new EntityFilterCollection(filterID); | |||
_persistentEntityFilters.Add(combineFilterIDs, filterCollection); | |||
var lastIndex = _persistentEntityFilters.count - 1; | |||
_indicesOfPersistentFiltersUsedByThisComponent.GetOrAdd(new NativeRefWrapperType(typeRef), | |||
() => new NativeDynamicArrayCast<int>(1, Svelto.Common.Allocator.Persistent)).Add(lastIndex); | |||
_indicesOfPersistentFiltersUsedByThisComponent.GetOrAdd(new NativeRefWrapperType(typeRef), _builder).Add(lastIndex); | |||
return ref _persistentEntityFilters.GetDirectValueByRef((uint)lastIndex); | |||
} | |||
#if UNITY_BURST && UNITY_COLLECTIONS | |||
[Unity.Burst.BurstDiscard] //not burst compatible because of TypeRefWrapper<T>.wrapper and GetOrAdd callback; | |||
#endif | |||
public ref EntityFilterCollection CreatePersistentFilter<T>(CombinedFilterID filterID) | |||
where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
long combineFilterIDs = Internal_FilterHelper.CombineFilterIDs<T>(filterID); | |||
if (_persistentEntityFilters.TryFindIndex(combineFilterIDs, out var index) == true) | |||
throw new ECSException("filter already exists"); | |||
var typeRef = TypeRefWrapper<T>.wrapper; | |||
var filterCollection = new EntityFilterCollection(filterID); | |||
_persistentEntityFilters.Add(combineFilterIDs, filterCollection); | |||
var lastIndex = _persistentEntityFilters.count - 1; | |||
_indicesOfPersistentFiltersUsedByThisComponent.GetOrAdd(new NativeRefWrapperType(typeRef), _builder).Add(lastIndex); | |||
return ref _persistentEntityFilters.GetDirectValueByRef((uint)lastIndex); | |||
} | |||
static NativeDynamicArrayCast<int> Builder() | |||
{ | |||
return new NativeDynamicArrayCast<int>(1, Allocator.Persistent); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref EntityFilterCollection GetPersistentFilter<T>(int filterID, FilterContextID filterContextId) | |||
where T : unmanaged, IBaseEntityComponent | |||
where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
return ref GetPersistentFilter<T>(new CombinedFilterID(filterID, filterContextId)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref EntityFilterCollection GetPersistentFilter<T>(CombinedFilterID filterID) | |||
where T : unmanaged, IBaseEntityComponent | |||
where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
long combineFilterIDs = Internal_FilterHelper.CombineFilterIDs<T>(filterID); | |||
if (_persistentEntityFilters.TryFindIndex(combineFilterIDs, out var index) == true) | |||
return ref _persistentEntityFilters.GetDirectValueByRef(index); | |||
throw new Exception("filter not found"); | |||
throw new ECSException("filter not found"); | |||
} | |||
public bool TryGetPersistentFilter<T>(CombinedFilterID combinedFilterID, out EntityFilterCollection entityCollection) | |||
where T : unmanaged, IBaseEntityComponent | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryGetPersistentFilter<T>(CombinedFilterID combinedFilterID, | |||
out EntityFilterCollection entityCollection) | |||
where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
long combineFilterIDs = Internal_FilterHelper.CombineFilterIDs<T>(combinedFilterID); | |||
if (_persistentEntityFilters.TryFindIndex(combineFilterIDs, out var index) == true) | |||
{ | |||
entityCollection = _persistentEntityFilters.GetDirectValueByRef(index); | |||
@@ -198,7 +200,9 @@ namespace Svelto.ECS | |||
return false; | |||
} | |||
public EntityFilterCollectionsEnumerator GetPersistentFilters<T>() where T : unmanaged, IBaseEntityComponent | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityFilterCollectionsEnumerator GetPersistentFilters<T>() | |||
where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
if (_indicesOfPersistentFiltersUsedByThisComponent.TryFindIndex( | |||
new NativeRefWrapperType(new RefWrapperType(typeof(T))), out var index) == true) | |||
@@ -206,9 +210,10 @@ namespace Svelto.ECS | |||
_indicesOfPersistentFiltersUsedByThisComponent.GetDirectValueByRef(index), | |||
_persistentEntityFilters); | |||
throw new Exception($"no filters associated with the type {TypeCache<T>.name}"); | |||
throw new ECSException($"no filters associated with the type {TypeCache<T>.name}"); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityFilterCollectionsWithContextEnumerator GetPersistentFilters<T>(FilterContextID filterContextId) | |||
{ | |||
if (_indicesOfPersistentFiltersUsedByThisComponent.TryFindIndex( | |||
@@ -217,10 +222,12 @@ namespace Svelto.ECS | |||
_indicesOfPersistentFiltersUsedByThisComponent.GetDirectValueByRef(index), | |||
_persistentEntityFilters, filterContextId); | |||
throw new Exception($"no filters associated with the type {TypeCache<T>.name}"); | |||
throw new ECSException($"no filters associated with the type {TypeCache<T>.name}"); | |||
} | |||
public bool TryGetPersistentFilters<T>(FilterContextID filterContextId, out EntityFilterCollectionsWithContextEnumerator enumerator) | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryGetPersistentFilters<T>(FilterContextID filterContextId, | |||
out EntityFilterCollectionsWithContextEnumerator enumerator) | |||
{ | |||
if (_indicesOfPersistentFiltersUsedByThisComponent.TryFindIndex( | |||
new NativeRefWrapperType(new RefWrapperType(typeof(T))), out var index) == true) | |||
@@ -236,12 +243,75 @@ namespace Svelto.ECS | |||
return false; | |||
} | |||
/// <summary> | |||
/// Creates a transient filter. Transient filters are deleted after each submission | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref EntityFilterCollection GetOrCreateTransientFilter<T>(CombinedFilterID filterID) | |||
where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
var combineFilterIDs = Internal_FilterHelper.CombineFilterIDs<T>(filterID); | |||
if (_transientEntityFilters.TryFindIndex(combineFilterIDs, out var index)) | |||
return ref _transientEntityFilters.GetDirectValueByRef(index); | |||
var filterCollection = new EntityFilterCollection(filterID); | |||
_transientEntityFilters.Add(combineFilterIDs, filterCollection); | |||
return ref _transientEntityFilters.GetDirectValueByRef((uint)(_transientEntityFilters.count - 1)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool TryGetTransientFilter<T>(CombinedFilterID filterID, out EntityFilterCollection entityCollection) | |||
where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
var combineFilterIDs = Internal_FilterHelper.CombineFilterIDs<T>(filterID); | |||
if (_transientEntityFilters.TryFindIndex(combineFilterIDs, out var index)) | |||
{ | |||
entityCollection = _transientEntityFilters.GetDirectValueByRef(index); | |||
return true; | |||
} | |||
entityCollection = default; | |||
return false; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref EntityFilterCollection GetTransientFilter<T>(CombinedFilterID filterID) | |||
where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
var combineFilterIDs = Internal_FilterHelper.CombineFilterIDs<T>(filterID); | |||
if (_transientEntityFilters.TryFindIndex(combineFilterIDs, out var index)) | |||
{ | |||
return ref _transientEntityFilters.GetDirectValueByRef(index); | |||
} | |||
throw new ECSException($"no filters associated with the type {TypeCache<T>.name}"); | |||
} | |||
public void CreateTransientFilter<T>(CombinedFilterID filterID) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
var combineFilterIDs = Internal_FilterHelper.CombineFilterIDs<T>(filterID); | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_transientEntityFilters.TryFindIndex(combineFilterIDs, out _)) | |||
throw new ECSException($"filter already exists {TypeCache<T>.name}"); | |||
#endif | |||
var filterCollection = new EntityFilterCollection(filterID); | |||
_transientEntityFilters.Add(combineFilterIDs, filterCollection); | |||
} | |||
public struct EntityFilterCollectionsEnumerator | |||
{ | |||
public EntityFilterCollectionsEnumerator(NativeDynamicArrayCast<int> getDirectValueByRef, | |||
SharedSveltoDictionaryNative<long, EntityFilterCollection> sharedSveltoDictionaryNative) : this() | |||
SharedSveltoDictionaryNative<long, EntityFilterCollection> sharedSveltoDictionaryNative): this() | |||
{ | |||
_getDirectValueByRef = getDirectValueByRef; | |||
_getDirectValueByRef = getDirectValueByRef; | |||
_sharedSveltoDictionaryNative = sharedSveltoDictionaryNative; | |||
} | |||
@@ -250,104 +320,84 @@ namespace Svelto.ECS | |||
return this; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool MoveNext() | |||
{ | |||
if (_currentIndex < _getDirectValueByRef.count) | |||
{ | |||
_currentIndex++; | |||
return true; | |||
} | |||
return false; | |||
} | |||
public ref EntityFilterCollection Current => | |||
ref _sharedSveltoDictionaryNative.GetDirectValueByRef((uint)_currentIndex - 1); | |||
public ref EntityFilterCollection Current | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get | |||
{ | |||
return ref _sharedSveltoDictionaryNative.GetDirectValueByRef((uint)_currentIndex - 1); | |||
} | |||
} | |||
readonly NativeDynamicArrayCast<int> _getDirectValueByRef; | |||
readonly NativeDynamicArrayCast<int> _getDirectValueByRef; | |||
readonly SharedSveltoDictionaryNative<long, EntityFilterCollection> _sharedSveltoDictionaryNative; | |||
int _currentIndex; | |||
int _currentIndex; | |||
} | |||
public struct EntityFilterCollectionsWithContextEnumerator | |||
{ | |||
public EntityFilterCollectionsWithContextEnumerator(NativeDynamicArrayCast<int> getDirectValueByRef, | |||
SharedSveltoDictionaryNative<long, EntityFilterCollection> sharedSveltoDictionaryNative, | |||
FilterContextID filterContextId) : this() | |||
FilterContextID filterContextId): this() | |||
{ | |||
_getDirectValueByRef = getDirectValueByRef; | |||
_getDirectValueByRef = getDirectValueByRef; | |||
_sharedSveltoDictionaryNative = sharedSveltoDictionaryNative; | |||
_filterContextId = filterContextId; | |||
_filterContextId = filterContextId; | |||
} | |||
public EntityFilterCollectionsWithContextEnumerator GetEnumerator() | |||
{ | |||
return this; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool MoveNext() | |||
{ | |||
while (_currentIndex++ < _getDirectValueByRef.count && | |||
_sharedSveltoDictionaryNative.GetDirectValueByRef((uint)_currentIndex - 1).combinedFilterID | |||
.contextID.id != _filterContextId.id) ; | |||
if (_currentIndex - 1 < _getDirectValueByRef.count) | |||
.contextID.id != _filterContextId.id) ; | |||
if (_currentIndex - 1 < _getDirectValueByRef.count) | |||
return true; | |||
return false; | |||
} | |||
public ref EntityFilterCollection Current => | |||
ref _sharedSveltoDictionaryNative.GetDirectValueByRef((uint)_currentIndex - 1); | |||
readonly NativeDynamicArrayCast<int> _getDirectValueByRef; | |||
readonly SharedSveltoDictionaryNative<long, EntityFilterCollection> _sharedSveltoDictionaryNative; | |||
readonly FilterContextID _filterContextId; | |||
int _currentIndex; | |||
} | |||
/// <summary> | |||
/// Creates a transient filter. Transient filters are deleted after each submission | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
public ref EntityFilterCollection GetOrCreateTransientFilter<T>(CombinedFilterID filterID) | |||
where T : unmanaged, IBaseEntityComponent | |||
{ | |||
var combineFilterIDs = Internal_FilterHelper.CombineFilterIDs<T>(filterID); | |||
if (_transientEntityFilters.TryFindIndex(combineFilterIDs, out var index)) | |||
return ref _transientEntityFilters.GetDirectValueByRef(index); | |||
var filterCollection = new EntityFilterCollection(filterID); | |||
_transientEntityFilters.Add(combineFilterIDs, filterCollection); | |||
return ref _transientEntityFilters.GetDirectValueByRef((uint)(_transientEntityFilters.count - 1)); | |||
} | |||
public bool TryGetTransientFilter<T>(CombinedFilterID filterID, out EntityFilterCollection entityCollection) | |||
where T : unmanaged, IBaseEntityComponent | |||
{ | |||
var combineFilterIDs = Internal_FilterHelper.CombineFilterIDs<T>(filterID); | |||
if (_transientEntityFilters.TryFindIndex(combineFilterIDs, out var index)) | |||
public ref EntityFilterCollection Current | |||
{ | |||
entityCollection = _transientEntityFilters.GetDirectValueByRef(index); | |||
return true; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get | |||
{ | |||
return ref _sharedSveltoDictionaryNative.GetDirectValueByRef((uint)_currentIndex - 1); | |||
} | |||
} | |||
entityCollection = default; | |||
return false; | |||
readonly NativeDynamicArrayCast<int> _getDirectValueByRef; | |||
readonly SharedSveltoDictionaryNative<long, EntityFilterCollection> _sharedSveltoDictionaryNative; | |||
readonly FilterContextID _filterContextId; | |||
int _currentIndex; | |||
} | |||
readonly SharedSveltoDictionaryNative<long, EntityFilterCollection> _persistentEntityFilters; | |||
readonly SharedSveltoDictionaryNative<NativeRefWrapperType, NativeDynamicArrayCast<int>> | |||
_indicesOfPersistentFiltersUsedByThisComponent; | |||
_indicesOfPersistentFiltersUsedByThisComponent; | |||
readonly SharedSveltoDictionaryNative<long, EntityFilterCollection> _transientEntityFilters; | |||
static readonly Func<NativeDynamicArrayCast<int>> _builder = Builder; | |||
} | |||
} | |||
} |
@@ -2,6 +2,7 @@ | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures.Native; | |||
using Svelto.ECS.Internal; | |||
using Svelto.ECS.Native; | |||
namespace Svelto.ECS | |||
@@ -22,28 +23,28 @@ namespace Svelto.ECS | |||
public EntityFilterIterator GetEnumerator() => new EntityFilterIterator(this); | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool Add<T>(EGID egid, NativeEGIDMapper<T> mmap) where T : unmanaged, IBaseEntityComponent | |||
public bool Add<T>(EGID egid, NativeEGIDMapper<T> mmap) where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
DBC.ECS.Check.Require(mmap.groupID == egid.groupID, "not compatible NativeEgidMapper used"); | |||
return Add(egid, mmap.GetIndex(egid.entityID)); | |||
} | |||
public bool Add<T>(EGID egid, NativeEGIDMultiMapper<T> mmap) where T : unmanaged, IBaseEntityComponent | |||
public bool Add<T>(EGID egid, NativeEGIDMultiMapper<T> mmap) where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
return Add(egid, mmap.GetIndex(egid)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool Add(EGID egid, uint toIndex) | |||
public bool Add(EGID egid, uint indexInComponentArray) | |||
{ | |||
return GetOrCreateGroupFilter(egid.groupID).Add(egid.entityID, toIndex); | |||
return GetOrCreateGroupFilter(egid.groupID).Add(egid.entityID, indexInComponentArray); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Add(uint entityID, ExclusiveGroupStruct groupId, uint index) | |||
public void Add(uint entityID, ExclusiveGroupStruct groupId, uint indexInComponentArray) | |||
{ | |||
Add(new EGID(entityID, groupId), index); | |||
Add(new EGID(entityID, groupId), indexInComponentArray); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -86,6 +87,20 @@ namespace Svelto.ECS | |||
return groupFilter; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public GroupFilters CreateGroupFilter(ExclusiveBuildGroup group) | |||
{ | |||
if (_filtersPerGroup.TryGetValue(group, out var groupFilter) == false) | |||
{ | |||
groupFilter = new GroupFilters(group); | |||
_filtersPerGroup.Add(group, groupFilter); | |||
return groupFilter; | |||
} | |||
throw new ECSException("group already linked to filter {group}"); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public GroupFilters GetGroupFilter(ExclusiveGroupStruct group) | |||
{ | |||
@@ -146,10 +161,11 @@ namespace Svelto.ECS | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool Add(uint entityId, uint entityIndex) | |||
public bool Add(uint entityId, uint indexInComponentArray) | |||
{ | |||
//TODO: when sentinels are finished, we need to add AsWriter here | |||
return _entityIDToDenseIndex.TryAdd(entityId, entityIndex, out _); | |||
//cannot write in parallel | |||
return _entityIDToDenseIndex.TryAdd(entityId, indexInComponentArray, out _); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -183,7 +199,7 @@ namespace Svelto.ECS | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal void Clear() | |||
{ | |||
_entityIDToDenseIndex.FastClear(); | |||
_entityIDToDenseIndex.Clear(); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -0,0 +1,19 @@ | |||
namespace Svelto.ECS | |||
{ | |||
public struct FilterContextID | |||
{ | |||
public readonly uint id; | |||
internal FilterContextID(uint id) | |||
{ | |||
DBC.ECS.Check.Require(id < ushort.MaxValue, "too many types registered, HOW :)"); | |||
this.id = id; | |||
} | |||
public static FilterContextID GetNewContextID() | |||
{ | |||
return EntitiesDB.SveltoFilters.GetNewContextID(); | |||
} | |||
} | |||
} |
@@ -1,6 +1,8 @@ | |||
#if SVELTO_LEGACY_FILTERS | |||
using DBC.ECS; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Native; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -32,14 +34,14 @@ namespace Svelto.ECS | |||
} | |||
public ref LegacyFilterGroup CreateOrGetFilterForGroup<T>(int filterID, ExclusiveGroupStruct groupID) | |||
where T : struct, IBaseEntityComponent | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
var refWrapper = TypeRefWrapper<T>.wrapper; | |||
return ref CreateOrGetFilterForGroup(filterID, groupID, refWrapper); | |||
} | |||
public bool HasFiltersForGroup<T>(ExclusiveGroupStruct groupID) where T : struct, IBaseEntityComponent | |||
public bool HasFiltersForGroup<T>(ExclusiveGroupStruct groupID) where T : struct, _IInternalEntityComponent | |||
{ | |||
if (_filtersLegacy.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == false) | |||
return false; | |||
@@ -48,7 +50,7 @@ namespace Svelto.ECS | |||
} | |||
public bool HasFilterForGroup<T>(int filterID, ExclusiveGroupStruct groupID) | |||
where T : struct, IBaseEntityComponent | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
if (_filtersLegacy.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == false) | |||
return false; | |||
@@ -60,7 +62,7 @@ namespace Svelto.ECS | |||
} | |||
public ref LegacyGroupFilters CreateOrGetFiltersForGroup<T>(ExclusiveGroupStruct groupID) | |||
where T : struct, IBaseEntityComponent | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
var fasterDictionary = _filtersLegacy.GetOrAdd(TypeRefWrapper<T>.wrapper, | |||
() => new FasterDictionary<ExclusiveGroupStruct, LegacyGroupFilters>()); | |||
@@ -70,7 +72,7 @@ namespace Svelto.ECS | |||
} | |||
public ref LegacyGroupFilters GetFiltersForGroup<T>(ExclusiveGroupStruct groupID) | |||
where T : struct, IBaseEntityComponent | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_filtersLegacy.ContainsKey(TypeRefWrapper<T>.wrapper) == false) | |||
@@ -84,7 +86,7 @@ namespace Svelto.ECS | |||
} | |||
public ref LegacyFilterGroup GetFilterForGroup<T>(int filterId, ExclusiveGroupStruct groupID) | |||
where T : struct, IBaseEntityComponent | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_filtersLegacy.ContainsKey(TypeRefWrapper<T>.wrapper) == false) | |||
@@ -97,7 +99,7 @@ namespace Svelto.ECS | |||
} | |||
public bool TryGetFilterForGroup<T>(int filterId, ExclusiveGroupStruct groupID, | |||
out LegacyFilterGroup groupLegacyFilter) where T : struct, IBaseEntityComponent | |||
out LegacyFilterGroup groupLegacyFilter) where T : struct, _IInternalEntityComponent | |||
{ | |||
groupLegacyFilter = default; | |||
@@ -114,7 +116,7 @@ namespace Svelto.ECS | |||
} | |||
public bool TryGetFiltersForGroup<T>(ExclusiveGroupStruct groupID, | |||
out LegacyGroupFilters legacyGroupFilters) where T : struct, IBaseEntityComponent | |||
out LegacyGroupFilters legacyGroupFilters) where T : struct, _IInternalEntityComponent | |||
{ | |||
legacyGroupFilters = default; | |||
@@ -166,7 +168,7 @@ namespace Svelto.ECS | |||
fasterDictionary[@group].DisposeFilter(resetFilterID); | |||
} | |||
public bool TryRemoveEntityFromFilter<T>(int filtersID, EGID egid) where T : struct, IBaseEntityComponent | |||
public bool TryRemoveEntityFromFilter<T>(int filtersID, EGID egid) where T : struct, _IInternalEntityComponent | |||
{ | |||
if (TryGetFilterForGroup<T>(filtersID, egid.groupID, out var filter)) | |||
return filter.TryRemove(egid.entityID); | |||
@@ -174,7 +176,7 @@ namespace Svelto.ECS | |||
return false; | |||
} | |||
public void RemoveEntityFromFilter<T>(int filtersID, EGID egid) where T : struct, IBaseEntityComponent | |||
public void RemoveEntityFromFilter<T>(int filtersID, EGID egid) where T : struct, _IInternalEntityComponent | |||
{ | |||
ref var filter = ref GetFilterForGroup<T>(filtersID, egid.groupID); | |||
@@ -207,4 +209,5 @@ namespace Svelto.ECS | |||
_filtersLegacy; | |||
} | |||
} | |||
} | |||
} | |||
#endif |
@@ -1,7 +1,8 @@ | |||
using System.Runtime.CompilerServices; | |||
#if SVELTO_LEGACY_FILTERS | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures.Native; | |||
using Svelto.ECS.DataStructures; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -125,7 +126,7 @@ namespace Svelto.ECS | |||
if (_denseListOfIndicesToEntityComponentArray.isValid == false) | |||
throw new ECSException($"invalid Filter"); | |||
#endif | |||
_indexOfEntityInDenseList.FastClear(); | |||
_indexOfEntityInDenseList.Clear(); | |||
_reverseEIDs.Clear(); | |||
_denseListOfIndicesToEntityComponentArray.Clear(); | |||
} | |||
@@ -195,7 +196,7 @@ namespace Svelto.ECS | |||
} | |||
else | |||
{ | |||
_indexOfEntityInDenseList.FastClear(); | |||
_indexOfEntityInDenseList.Clear(); | |||
_reverseEIDs.Clear(); | |||
_denseListOfIndicesToEntityComponentArray.Clear(); | |||
} | |||
@@ -209,4 +210,5 @@ namespace Svelto.ECS | |||
internal readonly ExclusiveGroupStruct _exclusiveGroupStruct; | |||
internal readonly int _ID; | |||
} | |||
} | |||
} | |||
#endif |
@@ -1,5 +1,6 @@ | |||
using System.Runtime.CompilerServices; | |||
using Svelto.ECS.DataStructures; | |||
#if SVELTO_LEGACY_FILTERS | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -35,4 +36,5 @@ namespace Svelto.ECS | |||
readonly NativeDynamicArrayCast<uint> _denseListOfIndicesToEntityComponentArray; | |||
readonly int _count; | |||
} | |||
} | |||
} | |||
#endif |
@@ -1,4 +1,5 @@ | |||
using Svelto.DataStructures; | |||
#if SVELTO_LEGACY_FILTERS | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Native; | |||
namespace Svelto.ECS | |||
@@ -69,7 +70,7 @@ namespace Svelto.ECS | |||
foreach (var filter in _legacyFilters) | |||
filter.value.Dispose(); | |||
_legacyFilters.FastClear(); | |||
_legacyFilters.Clear(); | |||
} | |||
internal ref LegacyFilterGroup CreateOrGetFilter(int filterID) | |||
@@ -101,4 +102,5 @@ namespace Svelto.ECS | |||
//filterID, filter | |||
SharedSveltoDictionaryNative<int, LegacyFilterGroup> _legacyFilters; | |||
} | |||
} | |||
} | |||
#endif |
@@ -1,10 +1,11 @@ | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures.Native; | |||
using Svelto.ECS.Internal; | |||
using Svelto.ECS.Native; | |||
namespace Svelto.ECS | |||
{ | |||
public struct NativeEntityFilterCollection<T> where T : unmanaged, IBaseEntityComponent | |||
public struct NativeEntityFilterCollection<T> where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
internal NativeEntityFilterCollection(NativeEGIDMultiMapper<T> mmap) | |||
{ | |||
@@ -114,8 +115,8 @@ namespace Svelto.ECS | |||
internal void Clear() | |||
{ | |||
_indexToEntityId.FastClear(); | |||
_entityIDToDenseIndex.FastClear(); | |||
_indexToEntityId.Clear(); | |||
_entityIDToDenseIndex.Clear(); | |||
} | |||
internal bool HasEntity(uint entityId) => _entityIDToDenseIndex.ContainsKey(entityId); | |||
@@ -1,6 +1,8 @@ | |||
namespace Svelto.ECS | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly ref struct NativeEntityFilterIterator<T> where T : unmanaged, IBaseEntityComponent | |||
public readonly ref struct NativeEntityFilterIterator<T> where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
internal NativeEntityFilterIterator(NativeEntityFilterCollection<T> filter) | |||
{ | |||
@@ -1,6 +1,7 @@ | |||
using System.Threading; | |||
using Svelto.Common; | |||
using Svelto.ECS.DataStructures; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -18,11 +19,11 @@ namespace Svelto.ECS | |||
void FillFromByteArray(EntityInitializer init, NativeBag buffer); | |||
} | |||
class Filler<T> : IFiller where T : struct, IBaseEntityComponent | |||
class Filler<T> : IFiller where T : struct, _IInternalEntityComponent | |||
{ | |||
static Filler() | |||
{ | |||
DBC.ECS.Check.Require(TypeType.isUnmanaged<T>() == true, "invalid type used"); | |||
DBC.ECS.Check.Require(TypeCache<T>.isUnmanaged == true, "invalid type used"); | |||
} | |||
//it's an internal interface | |||
@@ -50,7 +51,7 @@ namespace Svelto.ECS | |||
TYPE_IDS = new Svelto.DataStructures.FasterList<IFiller>(); | |||
} | |||
internal static void Register<T>(IFiller entityBuilder) where T : struct, IBaseEntityComponent | |||
internal static void Register<T>(IFiller entityBuilder) where T : struct, _IInternalEntityComponent | |||
{ | |||
var location = EntityComponentID<T>.ID.Data = GlobalTypeID.NextID<T>(); | |||
TYPE_IDS.AddAt(location, entityBuilder); | |||
@@ -7,10 +7,10 @@ namespace Svelto.ECS | |||
{ | |||
public partial class EntitiesDB | |||
{ | |||
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1>() where T1 : IBaseEntityComponent | |||
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1>(bool ignoreDisabledBit = false) where T1 : _IInternalEntityComponent | |||
{ | |||
FasterList<ExclusiveGroupStruct> result = localgroups.Value.groupArray; | |||
result.FastClear(); | |||
result.Clear(); | |||
if (groupsPerComponent.TryGetValue(TypeRefWrapper<T1>.wrapper | |||
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> result1) | |||
== false) | |||
@@ -22,20 +22,19 @@ namespace Svelto.ECS | |||
for (int j = 0; j < result1Count; j++) | |||
{ | |||
var group = fasterDictionaryNodes1[j].key; | |||
if (group.IsEnabled()) | |||
{ | |||
result.Add(group); | |||
} | |||
if (ignoreDisabledBit == false && group.IsEnabled() == false) continue; | |||
result.Add(group); | |||
} | |||
return result; | |||
} | |||
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1, T2>() | |||
where T1 : IBaseEntityComponent where T2 : IBaseEntityComponent | |||
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1, T2>(bool ignoreDisabledBit = false) | |||
where T1 : _IInternalEntityComponent where T2 : _IInternalEntityComponent | |||
{ | |||
FasterList<ExclusiveGroupStruct> result = localgroups.Value.groupArray; | |||
result.FastClear(); | |||
result.Clear(); | |||
if (groupsPerComponent.TryGetValue(TypeRefWrapper<T1>.wrapper | |||
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> result1) | |||
== false) | |||
@@ -53,7 +52,7 @@ namespace Svelto.ECS | |||
for (int i = 0; i < result1Count; i++) | |||
{ | |||
var groupID = fasterDictionaryNodes1[i].key; | |||
if (!groupID.IsEnabled()) continue; | |||
if (ignoreDisabledBit == false && groupID.IsEnabled() == false) continue; | |||
for (int j = 0; j < result2Count; j++) | |||
{ | |||
@@ -82,8 +81,8 @@ namespace Svelto.ECS | |||
/// <typeparam name="T2"></typeparam> | |||
/// <typeparam name="T3"></typeparam> | |||
/// <returns></returns> | |||
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1, T2, T3>() | |||
where T1 : IBaseEntityComponent where T2 : IBaseEntityComponent where T3 : IBaseEntityComponent | |||
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1, T2, T3>(bool ignoreDisabledBit = false) | |||
where T1 : _IInternalEntityComponent where T2 : _IInternalEntityComponent where T3 : _IInternalEntityComponent | |||
{ | |||
FasterList<FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>> localArray = | |||
localgroups.Value.listOfGroups; | |||
@@ -98,7 +97,7 @@ namespace Svelto.ECS | |||
return new LocalFasterReadOnlyList<ExclusiveGroupStruct>( | |||
FasterReadOnlyList<ExclusiveGroupStruct>.DefaultEmptyList); | |||
localgroups.Value.groups.FastClear(); | |||
localgroups.Value.groups.Clear(); | |||
FasterDictionary<ExclusiveGroupStruct, ExclusiveGroupStruct> localGroups = localgroups.Value.groups; | |||
@@ -114,10 +113,9 @@ namespace Svelto.ECS | |||
foreach (var value in localArray[startIndex]) | |||
{ | |||
if (value.key.IsEnabled()) | |||
{ | |||
localGroups.Add(value.key, value.key); | |||
} | |||
if (ignoreDisabledBit == false && value.key.IsEnabled() == false) continue; | |||
localGroups.Add(value.key, value.key); | |||
} | |||
var groupData = localArray[++startIndex % 3]; | |||
@@ -132,11 +130,11 @@ namespace Svelto.ECS | |||
, (uint) localGroups.count); | |||
} | |||
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1, T2, T3, T4>() | |||
where T1 : IBaseEntityComponent | |||
where T2 : IBaseEntityComponent | |||
where T3 : IBaseEntityComponent | |||
where T4 : IBaseEntityComponent | |||
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1, T2, T3, T4>(bool ignoreDisabledBit = false) | |||
where T1 : _IInternalEntityComponent | |||
where T2 : _IInternalEntityComponent | |||
where T3 : _IInternalEntityComponent | |||
where T4 : _IInternalEntityComponent | |||
{ | |||
FasterList<FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>> localArray = | |||
localgroups.Value.listOfGroups; | |||
@@ -154,26 +152,27 @@ namespace Svelto.ECS | |||
return new LocalFasterReadOnlyList<ExclusiveGroupStruct>( | |||
FasterReadOnlyList<ExclusiveGroupStruct>.DefaultEmptyList); | |||
localgroups.Value.groups.FastClear(); | |||
localgroups.Value.groups.Clear(); | |||
FasterDictionary<ExclusiveGroupStruct, ExclusiveGroupStruct> localGroups = localgroups.Value.groups; | |||
var localGroups = localgroups.Value.groups; | |||
int startIndex = 0; | |||
int min = int.MaxValue; | |||
for (int i = 0; i < 4; i++) | |||
if (localArray[i].count < min) | |||
{ | |||
var fasterDictionary = localArray[i]; | |||
if (fasterDictionary.count < min) | |||
{ | |||
min = localArray[i].count; | |||
min = fasterDictionary.count; | |||
startIndex = i; | |||
} | |||
} | |||
foreach (var value in localArray[startIndex]) | |||
{ | |||
if (value.key.IsEnabled()) | |||
{ | |||
localGroups.Add(value.key, value.key); | |||
} | |||
if (ignoreDisabledBit == false && value.key.IsEnabled() == false) continue; | |||
localGroups.Add(value.key, value.key); | |||
} | |||
var groupData = localArray[++startIndex & 3]; //&3 == %4 | |||
@@ -24,7 +24,7 @@ namespace Svelto.ECS | |||
public override string ToString() | |||
{ | |||
return this.group.ToName(); | |||
return group.ToName(); | |||
} | |||
internal ExclusiveGroupStruct @group { get; } | |||
@@ -3,6 +3,7 @@ using System.Diagnostics; | |||
using System.Runtime.CompilerServices; | |||
using System.Runtime.InteropServices; | |||
using System.Threading; | |||
#pragma warning disable CS0660, CS0661 | |||
namespace Svelto.ECS | |||
{ | |||
@@ -11,7 +12,7 @@ namespace Svelto.ECS | |||
//the type doesn't implement IEqualityComparer, what implements it is a custom comparer | |||
public readonly struct ExclusiveGroupStruct : IEquatable<ExclusiveGroupStruct>, IComparable<ExclusiveGroupStruct> | |||
{ | |||
public static readonly ExclusiveGroupStruct Invalid = default; //must stay here because of Burst | |||
public static readonly ExclusiveGroupStruct Invalid; //must stay here because of Burst | |||
public ExclusiveGroupStruct(byte[] data, uint pos):this() | |||
{ | |||
@@ -24,13 +25,7 @@ namespace Svelto.ECS | |||
DBC.ECS.Check.Ensure(id < _globalId, "Invalid group ID deserialiased"); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public override bool Equals(object obj) | |||
{ | |||
return obj is ExclusiveGroupStruct other && Equals(other); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public override int GetHashCode() | |||
{ | |||
@@ -146,7 +141,7 @@ namespace Svelto.ECS | |||
_idInternal = groupID; | |||
} | |||
ExclusiveGroupStruct(uint groupID, byte bytemask):this() | |||
internal ExclusiveGroupStruct(uint groupID, byte bytemask):this() | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (groupID >= 0xFFFFFF) | |||
@@ -17,14 +17,13 @@ namespace Svelto.ECS | |||
internal static readonly ThreadLocal<bool> skipStaticCompoundConstructorsWith2Tags = new ThreadLocal<bool>(); | |||
} | |||
interface ITouchedByReflection | |||
{ | |||
} | |||
interface ITouchedByReflection { } | |||
public abstract class GroupCompound<G1, G2, G3, G4> : ITouchedByReflection where G1 : GroupTag<G1> | |||
where G2 : GroupTag<G2> | |||
where G3 : GroupTag<G3> | |||
where G4 : GroupTag<G4> | |||
public abstract class GroupCompound<G1, G2, G3, G4>: ITouchedByReflection | |||
where G1 : GroupTag<G1> | |||
where G2 : GroupTag<G2> | |||
where G3 : GroupTag<G3> | |||
where G4 : GroupTag<G4> | |||
{ | |||
static GroupCompound() | |||
{ | |||
@@ -32,22 +31,26 @@ namespace Svelto.ECS | |||
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 && | |||
GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value == false) | |||
{ | |||
var | |||
group = | |||
new ExclusiveGroup(); //todo: it's a bit of a waste to create a class here even if this is a static constructor | |||
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor( | |||
typeof(GroupCompound<G1, G2, G3, G4>).TypeHandle); | |||
var group = new ExclusiveGroup( | |||
GroupTag<G1>.bitmask | GroupTag<G2>.bitmask | GroupTag<G3>.bitmask | GroupTag<G4>.bitmask | |||
| bitmask); | |||
_Groups = new FasterList<ExclusiveGroupStruct>(1); | |||
_Groups.Add(group); | |||
#if DEBUG && !PROFILE_SVELTO | |||
#if DEBUG | |||
var name = | |||
$"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name}-{typeof(G4).Name} ID {(uint)group.id}"; | |||
$"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name}-{typeof(G4).Name} ID {(uint)group.id}"; | |||
GroupNamesMap.idToName[group] = name; | |||
#endif | |||
//The hashname is independent from the actual group ID. this is fundamental because it is want | |||
//guarantees the hash to be the same across different machines | |||
GroupHashMap.RegisterGroup(group, typeof(GroupCompound<G1, G2, G3, G4>).FullName); | |||
//ToArrayFast is theoretically not correct, but since multiple 0s are ignored and we don't care if we | |||
//add one, we avoid an allocation | |||
_GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _)); | |||
GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value = true; | |||
@@ -127,12 +130,14 @@ namespace Svelto.ECS | |||
} | |||
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => | |||
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]); | |||
public static bool Includes(ExclusiveGroupStruct group) | |||
{ | |||
DBC.ECS.Check.Require(group != ExclusiveGroupStruct.Invalid, "invalid group passed"); | |||
return _GroupsHashSet.Contains(group); | |||
} | |||
@@ -149,29 +154,33 @@ namespace Svelto.ECS | |||
} | |||
static readonly FasterList<ExclusiveGroupStruct> _Groups; | |||
static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet; | |||
static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet; | |||
//we are changing this with Interlocked, so it cannot be readonly | |||
static int isInitializing; | |||
protected internal static ExclusiveGroupBitmask bitmask; | |||
} | |||
public abstract class GroupCompound<G1, G2, G3> : ITouchedByReflection where G1 : GroupTag<G1> | |||
where G2 : GroupTag<G2> | |||
where G3 : GroupTag<G3> | |||
public abstract class GroupCompound<G1, G2, G3>: ITouchedByReflection | |||
where G1 : GroupTag<G1> | |||
where G2 : GroupTag<G2> | |||
where G3 : GroupTag<G3> | |||
{ | |||
static GroupCompound() | |||
{ | |||
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 && | |||
GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value == false) | |||
{ | |||
var | |||
group = | |||
new ExclusiveGroup(); //todo: it's a bit of a waste to create a class here even if this is a static constructor | |||
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor( | |||
typeof(GroupCompound<G1, G2, G3>).TypeHandle); | |||
var group = new ExclusiveGroup( | |||
GroupTag<G1>.bitmask | GroupTag<G2>.bitmask | GroupTag<G3>.bitmask | bitmask); | |||
_Groups = new FasterList<ExclusiveGroupStruct>(1); | |||
_Groups.Add(group); | |||
#if DEBUG && !PROFILE_SVELTO | |||
#if DEBUG | |||
var name = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name} ID {(uint)group.id}"; | |||
GroupNamesMap.idToName[group] = name; | |||
#endif | |||
@@ -212,12 +221,14 @@ namespace Svelto.ECS | |||
} | |||
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => | |||
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]); | |||
public static bool Includes(ExclusiveGroupStruct group) | |||
{ | |||
DBC.ECS.Check.Require(group != ExclusiveGroupStruct.Invalid, "invalid group passed"); | |||
return _GroupsHashSet.Contains(group); | |||
} | |||
@@ -234,27 +245,31 @@ namespace Svelto.ECS | |||
} | |||
static readonly FasterList<ExclusiveGroupStruct> _Groups; | |||
static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet; | |||
static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet; | |||
//we are changing this with Interlocked, so it cannot be readonly | |||
static int isInitializing; | |||
protected internal static ExclusiveGroupBitmask bitmask; | |||
} | |||
public abstract class GroupCompound<G1, G2> : ITouchedByReflection where G1 : GroupTag<G1> where G2 : GroupTag<G2> | |||
public abstract class GroupCompound<G1, G2>: ITouchedByReflection | |||
where G1 : GroupTag<G1> | |||
where G2 : GroupTag<G2> | |||
{ | |||
static GroupCompound() | |||
{ | |||
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 && | |||
GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value == false) | |||
{ | |||
var | |||
group = | |||
new ExclusiveGroup(); //todo: it's a bit of a waste to create a class here even if this is a static constructor | |||
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor( | |||
typeof(GroupCompound<G1, G2>).TypeHandle); | |||
var group = new ExclusiveGroup(GroupTag<G1>.bitmask | GroupTag<G2>.bitmask | bitmask); | |||
_Groups = new FasterList<ExclusiveGroupStruct>(1); | |||
_Groups.Add(group); | |||
#if DEBUG && !PROFILE_SVELTO | |||
#if DEBUG | |||
GroupNamesMap.idToName[group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name} ID {group.id}"; | |||
#endif | |||
//The hashname is independent from the actual group ID. this is fundamental because it is want | |||
@@ -264,9 +279,10 @@ namespace Svelto.ECS | |||
_GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _)); | |||
GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = true; | |||
GroupCompound<G2, G1>._Groups = _Groups; | |||
GroupCompound<G2, G1>._Groups = _Groups; | |||
GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = false; | |||
GroupCompound<G2, G1>._GroupsHashSet = _GroupsHashSet; | |||
GroupCompound<G2, G1>._GroupsHashSet = _GroupsHashSet; | |||
//every abstract group preemptively adds this group, it may or may not be empty in future | |||
GroupTag<G1>.Add(group); | |||
@@ -275,12 +291,15 @@ namespace Svelto.ECS | |||
} | |||
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => | |||
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]); | |||
//TODO there is an overlap between this method and ExclusiveGroupExtensions FoundIn | |||
public static bool Includes(ExclusiveGroupStruct group) | |||
{ | |||
DBC.ECS.Check.Require(group != ExclusiveGroupStruct.Invalid, "invalid group passed"); | |||
return _GroupsHashSet.Contains(group); | |||
} | |||
@@ -297,9 +316,10 @@ namespace Svelto.ECS | |||
} | |||
static readonly FasterList<ExclusiveGroupStruct> _Groups; | |||
static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet; | |||
static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet; | |||
static int isInitializing; | |||
protected internal static ExclusiveGroupBitmask bitmask; | |||
} | |||
/// <summary> | |||
@@ -309,23 +329,35 @@ namespace Svelto.ECS | |||
/// groups with the same adjective, a group tag needs to hold all the groups sharing it. | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
public abstract class GroupTag<T> : ITouchedByReflection where T : GroupTag<T> | |||
public abstract class GroupTag<T>: ITouchedByReflection | |||
where T : GroupTag<T> | |||
{ | |||
static GroupTag() | |||
{ | |||
if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0) | |||
{ | |||
var group = new ExclusiveGroup(); | |||
//Allow to call GroupTag static constructors like | |||
// public class Dead: GroupTag<Dead> | |||
// { | |||
// static Dead() | |||
// { | |||
// bitmask = ExclusiveGroupBitmask.DISABLED_BIT; | |||
// } | |||
// }; | |||
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); | |||
var group = new ExclusiveGroup(bitmask); | |||
_Groups.Add(group); | |||
#if DEBUG && !PROFILE_SVELTO | |||
#if DEBUG | |||
var typeInfo = typeof(T); | |||
var name = $"Compound: {typeInfo.Name} ID {(uint)group.id}"; | |||
var name = $"Compound: {typeInfo.Name} ID {(uint)group.id}"; | |||
#if !PROFILE_SVELTO | |||
var typeInfoBaseType = typeInfo.BaseType; | |||
if (typeInfoBaseType.GenericTypeArguments[0] != | |||
typeInfo) //todo: this should shield from using a pattern different than public class GROUP_NAME : GroupTag<GROUP_NAME> {} however I am not sure it's working | |||
throw new ECSException("Invalid Group Tag declared"); | |||
#endif | |||
GroupNamesMap.idToName[group] = name; | |||
#endif | |||
@@ -338,12 +370,14 @@ namespace Svelto.ECS | |||
} | |||
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => | |||
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups); | |||
public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]); | |||
public static bool Includes(ExclusiveGroupStruct group) | |||
{ | |||
DBC.ECS.Check.Require(group != ExclusiveGroupStruct.Invalid, "invalid group passed"); | |||
return _GroupsHashSet.Contains(group); | |||
} | |||
@@ -361,9 +395,10 @@ namespace Svelto.ECS | |||
} | |||
static readonly FasterList<ExclusiveGroupStruct> _Groups = new FasterList<ExclusiveGroupStruct>(1); | |||
static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet; | |||
static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet; | |||
//we are changing this with Interlocked, so it cannot be readonly | |||
static int isInitializing; | |||
protected internal static ExclusiveGroupBitmask bitmask; | |||
} | |||
} |
@@ -18,9 +18,9 @@ namespace Svelto.ECS | |||
{ | |||
try | |||
{ | |||
var typeOfExclusiveGroup = typeof(ExclusiveGroup); | |||
var typeOfExclusiveGroup = typeof(ExclusiveGroup); | |||
var typeOfExclusiveGroupStruct = typeof(ExclusiveGroupStruct); | |||
var typeOfExclusiveBuildGroup = typeof(ExclusiveBuildGroup); | |||
var typeOfExclusiveBuildGroup = typeof(ExclusiveBuildGroup); | |||
foreach (Type type in AssemblyUtility.GetTypesSafe(assembly)) | |||
{ | |||
@@ -40,36 +40,37 @@ namespace Svelto.ECS | |||
foreach (var field in fields) | |||
{ | |||
if (field.IsStatic | |||
&& (typeOfExclusiveGroup.IsAssignableFrom(field.FieldType) | |||
|| typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType) | |||
if (field.IsStatic | |||
&& (typeOfExclusiveGroup.IsAssignableFrom(field.FieldType) | |||
|| typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType) | |||
|| typeOfExclusiveBuildGroup.IsAssignableFrom(field.FieldType))) | |||
{ | |||
uint groupID; | |||
uint groupIDAndBitMask; | |||
if (typeOfExclusiveGroup.IsAssignableFrom(field.FieldType)) | |||
{ | |||
var group = (ExclusiveGroup)field.GetValue(null); | |||
groupID = ((ExclusiveGroupStruct)@group).id; | |||
groupIDAndBitMask = ((ExclusiveGroupStruct)@group).ToIDAndBitmask(); | |||
} | |||
else | |||
if (typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType)) | |||
else if (typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType)) | |||
{ | |||
var group = (ExclusiveGroupStruct)field.GetValue(null); | |||
groupID = @group.id; | |||
groupIDAndBitMask = @group.ToIDAndBitmask(); | |||
} | |||
else | |||
{ | |||
var group = (ExclusiveBuildGroup)field.GetValue(null); | |||
groupID = ((ExclusiveGroupStruct)@group).id; | |||
groupIDAndBitMask = ((ExclusiveGroupStruct)@group).ToIDAndBitmask(); | |||
} | |||
{ | |||
ExclusiveGroupStruct group = new ExclusiveGroupStruct(groupID); | |||
var bitMask = (byte)(groupIDAndBitMask >> 24); | |||
var groupID = groupIDAndBitMask & 0xFFFFFF; | |||
ExclusiveGroupStruct group = new ExclusiveGroupStruct(groupID, bitMask); | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (GroupNamesMap.idToName.ContainsKey(@group) == false) | |||
GroupNamesMap.idToName[@group] = | |||
$"{type.FullName}.{field.Name} {@group.id})"; | |||
$"{type.FullName}.{field.Name} {@group.id})"; | |||
#endif | |||
//The hashname is independent from the actual group ID. this is fundamental because it is want | |||
//guarantees the hash to be the same across different machines | |||
@@ -3,12 +3,12 @@ using Svelto.ECS; | |||
static class GroupNamesMap | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
#if DEBUG | |||
static GroupNamesMap() { idToName = new Dictionary<ExclusiveGroupStruct, string>(); } | |||
internal static readonly Dictionary<ExclusiveGroupStruct, string> idToName; | |||
#endif | |||
#if DEBUG && !PROFILE_SVELTO | |||
#if DEBUG | |||
public static string ToName(this in ExclusiveGroupStruct group) | |||
{ | |||
Dictionary<ExclusiveGroupStruct, string> idToName = GroupNamesMap.idToName; | |||
@@ -2,6 +2,7 @@ using System.Collections.Generic; | |||
using System.Runtime.CompilerServices; | |||
using System.Threading; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS.Experimental | |||
{ | |||
@@ -49,7 +50,7 @@ namespace Svelto.ECS.Experimental | |||
public FasterList<ExclusiveGroupStruct> Evaluate() | |||
{ | |||
_groups.FastClear(); | |||
_groups.Clear(); | |||
foreach (var item in _sets) _groups.Add(item); | |||
@@ -210,7 +211,7 @@ namespace Svelto.ECS.Experimental | |||
readonly FasterReadOnlyList<ExclusiveGroupStruct> _group; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public int Count<T>(EntitiesDB entitiesDB) where T : struct, IBaseEntityComponent | |||
public int Count<T>(EntitiesDB entitiesDB) where T : struct, _IInternalEntityComponent | |||
{ | |||
var count = 0; | |||
@@ -220,7 +221,7 @@ namespace Svelto.ECS.Experimental | |||
return count; | |||
} | |||
public int Max<T>(EntitiesDB entitiesDB) where T : struct, IBaseEntityComponent | |||
public int Max<T>(EntitiesDB entitiesDB) where T : struct, _IInternalEntityComponent | |||
{ | |||
var max = 0; | |||
@@ -1,8 +1,12 @@ | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS.Hybrid | |||
{ | |||
public interface IManagedComponent:IBaseEntityComponent | |||
///IManagedComponents are pure struct components stored in managed memory | |||
public interface IManagedComponent:_IInternalEntityComponent | |||
{} | |||
/// IEntityViewComponents are components that leverage on the implementers pattern (not recommended in most cases) | |||
public interface IEntityViewComponent:IManagedComponent | |||
#if SLOW_SVELTO_SUBMISSION | |||
,INeedEGID | |||
@@ -1,11 +0,0 @@ | |||
namespace Svelto.ECS | |||
{ | |||
///<summary>Entity Components MUST implement IEntityComponent</summary> | |||
public interface IBaseEntityComponent | |||
{ | |||
} | |||
public interface IEntityComponent:IBaseEntityComponent | |||
{ | |||
} | |||
} |
@@ -1,3 +1,4 @@ | |||
using System; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS.Internal | |||
@@ -65,14 +66,15 @@ namespace Svelto.ECS | |||
/// Interface to mark an Engine as reacting on entities added | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
public interface IReactOnAdd<T> : IReactOnAdd where T : IBaseEntityComponent | |||
[Obsolete] | |||
public interface IReactOnAdd<T> : IReactOnAdd where T : _IInternalEntityComponent | |||
{ | |||
void Add(ref T entityComponent, EGID egid); | |||
} | |||
public interface IReactOnAddEx<T> : IReactOnAddEx where T : struct, IBaseEntityComponent | |||
public interface IReactOnAddEx<T> : IReactOnAddEx where T : struct, _IInternalEntityComponent | |||
{ | |||
void Add((uint start, uint end) rangeOfEntities, in EntityCollection<T> collection, | |||
void Add((uint start, uint end) rangeOfEntities, in EntityCollection<T> entities, | |||
ExclusiveGroupStruct groupID); | |||
} | |||
@@ -80,22 +82,24 @@ namespace Svelto.ECS | |||
/// Interface to mark an Engine as reacting on entities removed | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
public interface IReactOnRemove<T> : IReactOnRemove where T : IBaseEntityComponent | |||
[Obsolete] | |||
public interface IReactOnRemove<T> : IReactOnRemove where T : _IInternalEntityComponent | |||
{ | |||
void Remove(ref T entityComponent, EGID egid); | |||
} | |||
public interface IReactOnAddAndRemoveEx<T> : IReactOnAddEx<T>, IReactOnRemoveEx<T> where T : struct, IBaseEntityComponent | |||
public interface IReactOnAddAndRemoveEx<T> : IReactOnAddEx<T>, IReactOnRemoveEx<T> where T : struct, _IInternalEntityComponent | |||
{ | |||
} | |||
public interface IReactOnRemoveEx<T> : IReactOnRemoveEx where T : struct, IBaseEntityComponent | |||
public interface IReactOnRemoveEx<T> : IReactOnRemoveEx where T : struct, _IInternalEntityComponent | |||
{ | |||
void Remove((uint start, uint end) rangeOfEntities, in EntityCollection<T> collection, | |||
void Remove((uint start, uint end) rangeOfEntities, in EntityCollection<T> entities, | |||
ExclusiveGroupStruct groupID); | |||
} | |||
public interface IReactOnAddAndRemove<T> : IReactOnAdd<T>, IReactOnRemove<T> where T : IBaseEntityComponent | |||
[Obsolete] | |||
public interface IReactOnAddAndRemove<T> : IReactOnAdd<T>, IReactOnRemove<T> where T : _IInternalEntityComponent | |||
{ | |||
} | |||
@@ -104,7 +108,7 @@ namespace Svelto.ECS | |||
/// It can work together with IReactOnRemove which normally is not called on enginesroot disposed | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
public interface IReactOnDispose<T> : IReactOnDispose where T : IBaseEntityComponent | |||
public interface IReactOnDispose<T> : IReactOnDispose where T : _IInternalEntityComponent | |||
{ | |||
void Remove(ref T entityComponent, EGID egid); | |||
} | |||
@@ -113,14 +117,19 @@ namespace Svelto.ECS | |||
/// Interface to mark an Engine as reacting to entities swapping group | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
public interface IReactOnSwap<T> : IReactOnSwap where T : IBaseEntityComponent | |||
[Obsolete] | |||
public interface IReactOnSwap<T> : IReactOnSwap where T : _IInternalEntityComponent | |||
{ | |||
void MovedTo(ref T entityComponent, ExclusiveGroupStruct previousGroup, EGID egid); | |||
} | |||
public interface IReactOnSwapEx<T> : IReactOnSwapEx where T : struct, IBaseEntityComponent | |||
/// <summary> | |||
/// All the entities have been already submitted in the database (swapped) when this callback is triggered | |||
/// </summary> | |||
/// <typeparam name="T"></typeparam> | |||
public interface IReactOnSwapEx<T> : IReactOnSwapEx where T : struct, _IInternalEntityComponent | |||
{ | |||
void MovedTo((uint start, uint end) rangeOfEntities, in EntityCollection<T> collection, | |||
void MovedTo((uint start, uint end) rangeOfEntities, in EntityCollection<T> entities, | |||
ExclusiveGroupStruct fromGroup, ExclusiveGroupStruct toGroup); | |||
} | |||
@@ -131,4 +140,9 @@ namespace Svelto.ECS | |||
{ | |||
void EntitiesSubmitted(); | |||
} | |||
public interface IReactOnSubmissionStarted : IReactEngine | |||
{ | |||
void EntitiesSubmissionStarting(); | |||
} | |||
} |
@@ -0,0 +1,9 @@ | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
///IEntityComponents are unmanaged struct components stored in native memory. If they are not unmanaged they won't be recognised as IEntityComponent! | |||
public interface IEntityComponent:_IInternalEntityComponent | |||
{ | |||
} | |||
} |
@@ -1,10 +1,10 @@ | |||
#if SLOW_SVELTO_SUBMISSION | |||
namespace Svelto.ECS.Internal | |||
{ | |||
delegate void SetEGIDWithoutBoxingActionCast<T>(ref T target, EGID egid) where T : struct, IBaseEntityComponent; | |||
delegate void SetReferenceWithoutBoxingActionCast<T>(ref T target, EntityReference egid) where T : struct, IBaseEntityComponent; | |||
delegate void SetEGIDWithoutBoxingActionCast<T>(ref T target, EGID egid) where T : struct, _IInternalEntityComponent; | |||
delegate void SetReferenceWithoutBoxingActionCast<T>(ref T target, EntityReference egid) where T : struct, _IInternalEntityComponent; | |||
static class SetEGIDWithoutBoxing<T> where T : struct, IBaseEntityComponent | |||
static class SetEGIDWithoutBoxing<T> where T : struct, _IInternalEntityComponent | |||
{ | |||
public static readonly SetEGIDWithoutBoxingActionCast<T> SetIDWithoutBoxing = MakeSetter(); | |||
public static readonly SetReferenceWithoutBoxingActionCast<T> SetRefWithoutBoxing = MakeSetterReference(); | |||
@@ -1,8 +1,9 @@ | |||
using System; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly ref struct DoubleEntitiesEnumerator<T1> where T1 : struct, IBaseEntityComponent | |||
public readonly ref struct DoubleEntitiesEnumerator<T1> where T1 : struct, _IInternalEntityComponent | |||
{ | |||
public DoubleEntitiesEnumerator(GroupsEnumerable<T1> groupsEnumerable) { _groupsEnumerable = groupsEnumerable; } | |||
@@ -90,12 +91,12 @@ namespace Svelto.ECS | |||
int _indexB; | |||
} | |||
public ref struct ValueRef | |||
public readonly ref struct ValueRef | |||
{ | |||
public readonly GroupsEnumerable<T1>.RefCurrent _current; | |||
public readonly int _indexA; | |||
public readonly GroupsEnumerable<T1>.RefCurrent _refCurrent; | |||
public readonly int _indexB; | |||
readonly GroupsEnumerable<T1>.RefCurrent _current; | |||
readonly int _indexA; | |||
readonly GroupsEnumerable<T1>.RefCurrent _refCurrent; | |||
readonly int _indexB; | |||
public ValueRef | |||
(GroupsEnumerable<T1>.RefCurrent current, int indexA, GroupsEnumerable<T1>.RefCurrent refCurrent | |||
@@ -130,8 +131,8 @@ namespace Svelto.ECS | |||
} | |||
} | |||
public readonly ref struct DoubleIterationEnumerator<T1, T2> where T1 : struct, IBaseEntityComponent | |||
where T2 : struct, IBaseEntityComponent | |||
public readonly ref struct DoubleIterationEnumerator<T1, T2> where T1 : struct, _IInternalEntityComponent | |||
where T2 : struct, _IInternalEntityComponent | |||
{ | |||
public DoubleIterationEnumerator(GroupsEnumerable<T1, T2> groupsEnumerable) | |||
{ | |||
@@ -222,7 +223,7 @@ namespace Svelto.ECS | |||
int _indexB; | |||
} | |||
public ref struct ValueRef | |||
public readonly ref struct ValueRef | |||
{ | |||
public readonly GroupsEnumerable<T1, T2>.RefCurrent _current; | |||
public readonly int _indexA; | |||
@@ -268,9 +269,9 @@ namespace Svelto.ECS | |||
/// <typeparam name="T1"></typeparam> | |||
/// <typeparam name="T2"></typeparam> | |||
/// <typeparam name="T3"></typeparam> | |||
public readonly ref struct DoubleEntitiesEnumerator<T1, T2, T3> where T1 : struct, IBaseEntityComponent | |||
where T2 : struct, IBaseEntityComponent | |||
where T3 : struct, IBaseEntityComponent | |||
public readonly ref struct DoubleEntitiesEnumerator<T1, T2, T3> where T1 : struct, _IInternalEntityComponent | |||
where T2 : struct, _IInternalEntityComponent | |||
where T3 : struct, _IInternalEntityComponent | |||
{ | |||
public DoubleEntitiesEnumerator(GroupsEnumerable<T1, T2, T3> groupsEnumerable) | |||
{ | |||
@@ -361,12 +362,12 @@ namespace Svelto.ECS | |||
int _indexB; | |||
} | |||
public ref struct ValueRef | |||
public readonly ref struct ValueRef | |||
{ | |||
public readonly GroupsEnumerable<T1, T2, T3>.RefCurrent _current; | |||
public readonly int _indexA; | |||
public readonly GroupsEnumerable<T1, T2, T3>.RefCurrent _refCurrent; | |||
public readonly int _indexB; | |||
readonly GroupsEnumerable<T1, T2, T3>.RefCurrent _current; | |||
readonly int _indexA; | |||
readonly GroupsEnumerable<T1, T2, T3>.RefCurrent _refCurrent; | |||
readonly int _indexB; | |||
public ValueRef | |||
(GroupsEnumerable<T1, T2, T3>.RefCurrent current, int indexA | |||
@@ -409,10 +410,10 @@ namespace Svelto.ECS | |||
/// <typeparam name="T2"></typeparam> | |||
/// <typeparam name="T3"></typeparam> | |||
/// <typeparam name="T4"></typeparam> | |||
public readonly ref struct DoubleEntitiesEnumerator<T1, T2, T3, T4> where T1 : struct, IBaseEntityComponent | |||
where T2 : struct, IBaseEntityComponent | |||
where T3 : struct, IBaseEntityComponent | |||
where T4 : struct, IBaseEntityComponent | |||
public readonly ref struct DoubleEntitiesEnumerator<T1, T2, T3, T4> where T1 : struct, _IInternalEntityComponent | |||
where T2 : struct, _IInternalEntityComponent | |||
where T3 : struct, _IInternalEntityComponent | |||
where T4 : struct, _IInternalEntityComponent | |||
{ | |||
public DoubleEntitiesEnumerator(GroupsEnumerable<T1, T2, T3, T4> groupsEnumerable) | |||
{ | |||
@@ -1,10 +1,11 @@ | |||
using System; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public struct Consumer<T> : IDisposable where T : unmanaged, IBaseEntityComponent | |||
public struct Consumer<T> : IDisposable where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
internal Consumer(string name, uint capacity) : this() | |||
{ | |||
@@ -20,10 +21,10 @@ namespace Svelto.ECS | |||
string.Empty | |||
#endif | |||
); | |||
mustBeDisposed = MemoryUtilities.Alloc<bool>(1, Allocator.Persistent); | |||
mustBeDisposed = MemoryUtilities.NativeAlloc<bool>(1, Allocator.Persistent); | |||
*(bool*) mustBeDisposed = false; | |||
isActive = MemoryUtilities.Alloc<bool>(1, Allocator.Persistent); | |||
isActive = MemoryUtilities.NativeAlloc<bool>(1, Allocator.Persistent); | |||
*(bool*) isActive = true; | |||
} | |||
} | |||
@@ -79,8 +80,8 @@ namespace Svelto.ECS | |||
public void Free() | |||
{ | |||
MemoryUtilities.Free(mustBeDisposed, Allocator.Persistent); | |||
MemoryUtilities.Free(isActive, Allocator.Persistent); | |||
MemoryUtilities.NativeFree(mustBeDisposed, Allocator.Persistent); | |||
MemoryUtilities.NativeFree(isActive, Allocator.Persistent); | |||
} | |||
public void Pause() | |||
@@ -1,18 +1,19 @@ | |||
using System.Runtime.CompilerServices; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
public partial class EnginesRoot | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IBaseEntityComponent | |||
internal Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
return _entityStreams.GenerateConsumer<T>(name, capacity); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal Consumer<T> GenerateConsumer<T>(ExclusiveGroupStruct group, string name, uint capacity) | |||
where T : unmanaged, IBaseEntityComponent | |||
where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
return _entityStreams.GenerateConsumer<T>(@group, name, capacity); | |||
} | |||
@@ -1,5 +1,6 @@ | |||
using System; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -15,7 +16,7 @@ namespace Svelto.ECS | |||
struct EntitiesStreams : IDisposable | |||
{ | |||
internal Consumer<T> GenerateConsumer<T>(string name, uint capacity) | |||
where T : unmanaged, IBaseEntityComponent | |||
where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
if (_streams.ContainsKey(TypeRefWrapper<T>.wrapper) == false) | |||
_streams[TypeRefWrapper<T>.wrapper] = new EntityStream<T>(); | |||
@@ -24,7 +25,7 @@ namespace Svelto.ECS | |||
} | |||
public Consumer<T> GenerateConsumer<T>(ExclusiveGroupStruct group, string name, uint capacity) | |||
where T : unmanaged, IBaseEntityComponent | |||
where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
if (_streams.ContainsKey(TypeRefWrapper<T>.wrapper) == false) | |||
_streams[TypeRefWrapper<T>.wrapper] = new EntityStream<T>(); | |||
@@ -33,12 +34,12 @@ namespace Svelto.ECS | |||
return typeSafeStream.GenerateConsumer(group, name, capacity); | |||
} | |||
internal void PublishEntity<T>(ref T entity, EGID egid) where T : unmanaged, IBaseEntityComponent | |||
internal void PublishEntity<T>(ref T entity, EGID egid) where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
if (_streams.TryGetValue(TypeRefWrapper<T>.wrapper, out var typeSafeStream)) | |||
(typeSafeStream as EntityStream<T>).PublishEntity(ref entity, egid); | |||
else | |||
Console.LogDebug("No Consumers are waiting for this entity to change ", typeof(T)); | |||
Console.LogDebug($"No Consumers are waiting for this entity to change {typeof(T)}"); | |||
} | |||
public void Dispose() | |||
@@ -1,4 +1,5 @@ | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -7,7 +8,7 @@ namespace Svelto.ECS | |||
void Dispose(); | |||
} | |||
public class EntityStream<T> : ITypeSafeStream where T : unmanaged, IBaseEntityComponent | |||
public class EntityStream<T> : ITypeSafeStream where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
readonly ThreadSafeFasterList<Consumer<T>> _consumers; | |||
@@ -1,5 +1,6 @@ | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -12,14 +13,14 @@ namespace Svelto.ECS | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public Consumer<T> GenerateConsumer<T>(string name, uint capacity) | |||
where T : unmanaged, IBaseEntityComponent | |||
where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
return _enginesRoot.Target.GenerateConsumer<T>(name, capacity); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public Consumer<T> GenerateConsumer<T>(ExclusiveGroupStruct @group, string name, uint capacity) | |||
where T : unmanaged, IBaseEntityComponent | |||
where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
return _enginesRoot.Target.GenerateConsumer<T>(group, name, capacity); | |||
} | |||
@@ -31,9 +32,9 @@ namespace Svelto.ECS | |||
public interface IEntityStreamConsumerFactory | |||
{ | |||
Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IBaseEntityComponent; | |||
Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, _IInternalEntityComponent; | |||
Consumer<T> GenerateConsumer<T>(ExclusiveGroupStruct @group, string name, uint capacity) | |||
where T : unmanaged, IBaseEntityComponent; | |||
where T : unmanaged, _IInternalEntityComponent; | |||
} | |||
} |
@@ -4,19 +4,11 @@ using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
static class TypeSafeDictionaryFactory<T> where T : struct, IBaseEntityComponent | |||
static class TypeSafeDictionaryFactory<T> where T : struct, _IInternalEntityComponent | |||
{ | |||
static readonly bool isUnmanaged = typeof(T).IsUnmanagedEx() | |||
static readonly bool isUnmanaged = TypeCache<T>.isUnmanaged | |||
&& typeof(IEntityViewComponent).IsAssignableFrom(typeof(T)) == false; | |||
public static ITypeSafeDictionary Create() | |||
{ | |||
if (isUnmanaged) | |||
return new UnmanagedTypeSafeDictionary<T>(1); | |||
return new ManagedTypeSafeDictionary<T>(1); | |||
} | |||
public static ITypeSafeDictionary Create(uint size) | |||
{ | |||
if (isUnmanaged) | |||
@@ -0,0 +1,10 @@ | |||
namespace Svelto.ECS | |||
{ | |||
namespace Internal | |||
{ | |||
///<summary>This interfaces shouldn't be used outside the svelto assembly, use interfaces that inherit from this</summary> | |||
public interface _IInternalEntityComponent | |||
{ | |||
} | |||
} | |||
} |
@@ -4,7 +4,7 @@ using Svelto.DataStructures; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
public interface ITypeSafeDictionary<TValue> : ITypeSafeDictionary where TValue : IBaseEntityComponent | |||
public interface ITypeSafeDictionary<TValue> : ITypeSafeDictionary where TValue : _IInternalEntityComponent | |||
{ | |||
void Add(uint egidEntityId, in TValue entityComponent); | |||
@@ -3,18 +3,12 @@ using System.Runtime.CompilerServices; | |||
using System.Threading; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Hybrid; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
public sealed class ManagedTypeSafeDictionary<TValue> : ITypeSafeDictionary<TValue> | |||
where TValue : struct, IBaseEntityComponent | |||
sealed class ManagedTypeSafeDictionary<TValue> : ITypeSafeDictionary<TValue> | |||
where TValue : struct, _IInternalEntityComponent | |||
{ | |||
static readonly Type _type = typeof(TValue); | |||
#if SLOW_SVELTO_SUBMISSION | |||
static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type); | |||
static readonly bool _hasReference = typeof(INeedEntityReference).IsAssignableFrom(_type); | |||
#endif | |||
static readonly ThreadLocal<IEntityIDs> cachedEntityIDM = | |||
new ThreadLocal<IEntityIDs>(() => new ManagedEntityIDs()); | |||
@@ -24,7 +18,7 @@ namespace Svelto.ECS.Internal | |||
new SveltoDictionary<uint, TValue, ManagedStrategy<SveltoDictionaryNode<uint>>, ManagedStrategy<TValue>, | |||
ManagedStrategy<int>>(size, Allocator.Managed); | |||
} | |||
public IEntityIDs entityIDs | |||
{ | |||
get | |||
@@ -99,7 +93,7 @@ namespace Svelto.ECS.Internal | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ITypeSafeDictionary Create() | |||
{ | |||
return TypeSafeDictionaryFactory<TValue>.Create(1); | |||
return new ManagedTypeSafeDictionary<TValue>(1); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -289,7 +283,7 @@ namespace Svelto.ECS.Internal | |||
} | |||
/// <summary> | |||
/// Execute all the engine IReactOnDispose for eahc component registered in the DB when it's disposed of | |||
/// Execute all the engine IReactOnDispose for each component registered in the DB when it's disposed of | |||
/// </summary> | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void ExecuteEnginesDisposeCallbacks_Group | |||
@@ -18,7 +18,7 @@ namespace Svelto.ECS.Internal | |||
, ExclusiveGroupStruct toGroupID) where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>> | |||
where Strategy2 : struct, IBufferStrategy<TValue> | |||
where Strategy3 : struct, IBufferStrategy<int> | |||
where TValue : struct, IBaseEntityComponent | |||
where TValue : struct, _IInternalEntityComponent | |||
{ | |||
foreach (var tuple in fromDictionary) | |||
{ | |||
@@ -57,7 +57,7 @@ namespace Svelto.ECS.Internal | |||
, in PlatformProfiler sampler) where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>> | |||
where Strategy2 : struct, IBufferStrategy<TValue> | |||
where Strategy3 : struct, IBufferStrategy<int> | |||
where TValue : struct, IBaseEntityComponent | |||
where TValue : struct, _IInternalEntityComponent | |||
{ | |||
if (entitycomponentenginesdb.TryGetValue(new RefWrapperType(TypeCache<TValue>.type) | |||
, out var entityComponentsEngines)) | |||
@@ -78,7 +78,9 @@ namespace Svelto.ECS.Internal | |||
for (var j = 0; j < entityComponentsEngines.count; j++) | |||
using (sampler.Sample(entityComponentsEngines[j].name)) | |||
{ | |||
#pragma warning disable CS0612 | |||
((IReactOnAdd<TValue>)entityComponentsEngines[j].engine).Add(ref entity, egid); | |||
#pragma warning restore CS0612 | |||
} | |||
} | |||
catch (Exception e) | |||
@@ -99,7 +101,7 @@ namespace Svelto.ECS.Internal | |||
where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>> | |||
where Strategy2 : struct, IBufferStrategy<TValue> | |||
where Strategy3 : struct, IBufferStrategy<int> | |||
where TValue : struct, IBaseEntityComponent | |||
where TValue : struct, _IInternalEntityComponent | |||
{ | |||
if (allEngines.TryGetValue(new RefWrapperType(TypeCache<TValue>.type), out var entityComponentsEngines) | |||
== false) | |||
@@ -137,7 +139,7 @@ namespace Svelto.ECS.Internal | |||
where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>> | |||
where Strategy2 : struct, IBufferStrategy<TValue> | |||
where Strategy3 : struct, IBufferStrategy<int> | |||
where TValue : struct, IBaseEntityComponent | |||
where TValue : struct, _IInternalEntityComponent | |||
{ | |||
if (reactiveenginesremove.TryGetValue(new RefWrapperType(TypeCache<TValue>.type) | |||
, out var entityComponentsEngines)) | |||
@@ -158,7 +160,9 @@ namespace Svelto.ECS.Internal | |||
for (var j = 0; j < entityComponentsEngines.count; j++) | |||
using (profiler.Sample(entityComponentsEngines[j].name)) | |||
{ | |||
#pragma warning disable CS0612 | |||
((IReactOnRemove<TValue>)entityComponentsEngines[j].engine).Remove(ref entity, egid); | |||
#pragma warning restore CS0612 | |||
} | |||
} | |||
catch | |||
@@ -184,7 +188,7 @@ namespace Svelto.ECS.Internal | |||
where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>> | |||
where Strategy2 : struct, IBufferStrategy<TValue> | |||
where Strategy3 : struct, IBufferStrategy<int> | |||
where TValue : struct, IBaseEntityComponent | |||
where TValue : struct, _IInternalEntityComponent | |||
{ | |||
if (reactiveenginesremove.TryGetValue(new RefWrapperType(TypeCache<TValue>.type) | |||
, out var reactiveEnginesRemovePerType)) | |||
@@ -201,7 +205,9 @@ namespace Svelto.ECS.Internal | |||
using (sampler.Sample(reactiveEnginesRemovePerType[i].name)) | |||
{ | |||
#pragma warning disable CS0612 | |||
((IReactOnRemove<TValue>)reactiveEnginesRemovePerType[i].engine).Remove( | |||
#pragma warning restore CS0612 | |||
ref entity, egid); | |||
} | |||
} | |||
@@ -250,7 +256,7 @@ namespace Svelto.ECS.Internal | |||
where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>> | |||
where Strategy2 : struct, IBufferStrategy<TValue> | |||
where Strategy3 : struct, IBufferStrategy<int> | |||
where TValue : struct, IBaseEntityComponent | |||
where TValue : struct, _IInternalEntityComponent | |||
{ | |||
if (reactiveenginesswap.count == 0) | |||
return; | |||
@@ -268,7 +274,9 @@ namespace Svelto.ECS.Internal | |||
for (var j = 0; j < reactiveenginesswap.count; j++) | |||
using (sampler.Sample(reactiveenginesswap[j].name)) | |||
{ | |||
#pragma warning disable CS0612 | |||
((IReactOnSwap<TValue>)reactiveenginesswap[j].engine).MovedTo( | |||
#pragma warning restore CS0612 | |||
ref entityComponent, fromgroup, newEgid); | |||
} | |||
} | |||
@@ -295,7 +303,7 @@ namespace Svelto.ECS.Internal | |||
where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>> | |||
where Strategy2 : struct, IBufferStrategy<TValue> | |||
where Strategy3 : struct, IBufferStrategy<int> | |||
where TValue : struct, IBaseEntityComponent | |||
where TValue : struct, _IInternalEntityComponent | |||
{ | |||
//get all the engines linked to TValue | |||
if (!reactiveenginesswap.TryGetValue(new RefWrapperType(TypeCache<TValue>.type) | |||
@@ -314,7 +322,9 @@ namespace Svelto.ECS.Internal | |||
using (sampler.Sample(reactiveEnginesSwapPerType[i].name)) | |||
{ | |||
#pragma warning disable CS0612 | |||
((IReactOnSwap<TValue>)reactiveEnginesSwapPerType[i].engine).MovedTo( | |||
#pragma warning restore CS0612 | |||
ref entityComponent, fromgroup, newEgid); | |||
} | |||
} | |||
@@ -361,7 +371,7 @@ namespace Svelto.ECS.Internal | |||
where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>> | |||
where Strategy2 : struct, IBufferStrategy<TValue> | |||
where Strategy3 : struct, IBufferStrategy<int> | |||
where TValue : struct, IBaseEntityComponent | |||
where TValue : struct, _IInternalEntityComponent | |||
{ | |||
var iterations = infostoprocess.count; | |||
@@ -409,7 +419,7 @@ namespace Svelto.ECS.Internal | |||
where Strategy1 : struct, IBufferStrategy<SveltoDictionaryNode<uint>> | |||
where Strategy2 : struct, IBufferStrategy<TValue> | |||
where Strategy3 : struct, IBufferStrategy<int> | |||
where TValue : struct, IBaseEntityComponent | |||
where TValue : struct, _IInternalEntityComponent | |||
{ | |||
var iterations = infostoprocess.count; | |||
@@ -455,9 +465,9 @@ namespace Svelto.ECS.Internal | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void ExecuteEnginesAddEntityCallbacksFast<TValue> | |||
(FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnAddEx>>> fasterDictionary | |||
, ExclusiveGroupStruct groupId, (uint, uint) valueTuple, IEntityIDs entityids | |||
, ExclusiveGroupStruct groupId, (uint, uint) rangeTuple, IEntityIDs entityids | |||
, ITypeSafeDictionary<TValue> typeSafeDictionary, PlatformProfiler profiler) | |||
where TValue : struct, IBaseEntityComponent | |||
where TValue : struct, _IInternalEntityComponent | |||
{ | |||
//get all the engines linked to TValue | |||
if (!fasterDictionary.TryGetValue(new RefWrapperType(TypeCache<TValue>.type) | |||
@@ -470,7 +480,7 @@ namespace Svelto.ECS.Internal | |||
using (profiler.Sample(entityComponentsEngines[i].name)) | |||
{ | |||
((IReactOnAddEx<TValue>)entityComponentsEngines[i].engine).Add( | |||
valueTuple | |||
rangeTuple | |||
, new EntityCollection<TValue>(typeSafeDictionary.GetValues(out var count), entityids, count) | |||
, groupId); | |||
} | |||
@@ -489,7 +499,7 @@ namespace Svelto.ECS.Internal | |||
(FasterList<ReactEngineContainer<IReactOnSwapEx>> fasterList, ExclusiveGroupStruct fromGroup | |||
, ExclusiveGroupStruct toGroup, IEntityIDs entityids, ITypeSafeDictionary<TValue> typeSafeDictionary | |||
, (uint, uint) rangeofsubmittedentitiesindicies, PlatformProfiler sampler) | |||
where TValue : struct, IBaseEntityComponent | |||
where TValue : struct, _IInternalEntityComponent | |||
{ | |||
for (var i = 0; i < fasterList.count; i++) | |||
try | |||
@@ -515,7 +525,7 @@ namespace Svelto.ECS.Internal | |||
public static void ExecuteEnginesRemoveCallbacksFast<TValue> | |||
(FasterList<ReactEngineContainer<IReactOnRemoveEx>> fasterList, ExclusiveGroupStruct exclusiveGroupStruct | |||
, (uint, uint) valueTuple, IEntityIDs entityids, ITypeSafeDictionary<TValue> typeSafeDictionary | |||
, PlatformProfiler sampler) where TValue : struct, IBaseEntityComponent | |||
, PlatformProfiler sampler) where TValue : struct, _IInternalEntityComponent | |||
{ | |||
for (var i = 0; i < fasterList.count; i++) | |||
try | |||
@@ -3,7 +3,7 @@ namespace Svelto.ECS.Internal | |||
static class TypeSafeDictionaryUtilities | |||
{ | |||
internal static EGIDMapper<T> ToEGIDMapper<T>(this ITypeSafeDictionary<T> dic, | |||
ExclusiveGroupStruct groupStructId) where T:struct, IBaseEntityComponent | |||
ExclusiveGroupStruct groupStructId) where T:struct, _IInternalEntityComponent | |||
{ | |||
var mapper = new EGIDMapper<T>(groupStructId, dic); | |||
@@ -1,87 +0,0 @@ | |||
#if UNITY_NATIVE //because of the thread count, ATM this is only for unity | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Unity.Jobs.LowLevel.Unsafe; | |||
using Allocator = Svelto.Common.Allocator; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
public unsafe struct AtomicNativeBags:IDisposable | |||
{ | |||
public uint count => _threadsCount; | |||
public AtomicNativeBags(Allocator allocator) | |||
{ | |||
_allocator = allocator; | |||
_threadsCount = JobsUtility.MaxJobThreadCount + 1; | |||
var bufferSize = MemoryUtilities.SizeOf<NativeBag>(); | |||
var bufferCount = _threadsCount; | |||
var allocationSize = bufferSize * bufferCount; | |||
var ptr = (byte*)MemoryUtilities.Alloc((uint) allocationSize, allocator); | |||
// MemoryUtilities.MemClear((IntPtr) ptr, (uint) allocationSize); | |||
for (int i = 0; i < bufferCount; i++) | |||
{ | |||
var bufferPtr = (NativeBag*)(ptr + bufferSize * i); | |||
var buffer = new NativeBag(allocator); | |||
MemoryUtilities.CopyStructureToPtr(ref buffer, (IntPtr) bufferPtr); | |||
} | |||
_data = (NativeBag*)ptr; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public readonly ref NativeBag GetBuffer(int index) | |||
{ | |||
#if DEBUG | |||
if (_data == null) | |||
throw new Exception("using invalid AtomicNativeBags"); | |||
#endif | |||
return ref MemoryUtilities.ArrayElementAsRef<NativeBag>((IntPtr) _data, index); | |||
} | |||
public void Dispose() | |||
{ | |||
#if DEBUG | |||
if (_data == null) | |||
throw new Exception("using invalid AtomicNativeBags"); | |||
#endif | |||
for (int i = 0; i < _threadsCount; i++) | |||
{ | |||
GetBuffer(i).Dispose(); | |||
} | |||
MemoryUtilities.Free((IntPtr) _data, _allocator); | |||
_data = null; | |||
} | |||
public void Clear() | |||
{ | |||
#if DEBUG | |||
if (_data == null) | |||
throw new Exception("using invalid AtomicNativeBags"); | |||
#endif | |||
for (int i = 0; i < _threadsCount; i++) | |||
{ | |||
GetBuffer(i).Clear(); | |||
} | |||
} | |||
readonly Allocator _allocator; | |||
readonly uint _threadsCount; | |||
#if UNITY_COLLECTIONS || UNITY_JOBS || UNITY_BURST | |||
#if UNITY_BURST | |||
[Unity.Burst.NoAlias] | |||
#endif | |||
[Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
NativeBag* _data; | |||
} | |||
} | |||
#endif |
@@ -1,204 +0,0 @@ | |||
#if DEBUG && !PROFILE_SVELTO | |||
#define ENABLE_DEBUG_CHECKS | |||
#endif | |||
using System; | |||
using System.Diagnostics; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.Common.DataStructures; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
/// <summary> | |||
/// Burst friendly RingBuffer on steroid: | |||
/// it can: Enqueue/Dequeue, it wraps around if there is enough space after dequeuing | |||
/// It resizes if there isn't enough space left. | |||
/// It's a "bag", you can queue and dequeue any type and mix them. Just be sure that you dequeue what you queue! No check on type | |||
/// is done. | |||
/// You can reserve a position in the queue to update it later. | |||
/// The datastructure is a struct and it's "copiable" | |||
/// I eventually decided to call it NativeBag and not NativeBag because it can also be used as | |||
/// a preallocated memory pool where any kind of T can be stored as long as T is unmanaged | |||
/// </summary> | |||
public struct NativeBag : IDisposable | |||
{ | |||
public uint count | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get | |||
{ | |||
unsafe | |||
{ | |||
BasicTests(); | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
return _queue->size; | |||
} | |||
} | |||
} | |||
} | |||
public uint capacity | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get | |||
{ | |||
unsafe | |||
{ | |||
BasicTests(); | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
return _queue->capacity; | |||
} | |||
} | |||
} | |||
} | |||
public NativeBag(Allocator allocator):this() | |||
{ | |||
unsafe | |||
{ | |||
var listData = (UnsafeBlob*)MemoryUtilities.Alloc<UnsafeBlob>((uint)1, allocator); | |||
listData->allocator = allocator; | |||
_queue = listData; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public bool IsEmpty() | |||
{ | |||
unsafe | |||
{ | |||
BasicTests(); | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
if (_queue == null || _queue->ptr == null) | |||
return true; | |||
} | |||
} | |||
return count == 0; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public unsafe void Dispose() | |||
{ | |||
if (_queue != null) | |||
{ | |||
BasicTests(); | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
_queue->Dispose(); | |||
MemoryUtilities.Free((IntPtr)_queue, _queue->allocator); | |||
_queue = null; | |||
} | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T ReserveEnqueue<T> | |||
(out UnsafeArrayIndex index) | |||
where T : struct //should be unmanaged, but it's not due to Svelto.ECS constraints. | |||
{ | |||
unsafe | |||
{ | |||
BasicTests(); | |||
var sizeOf = MemoryUtilities.SizeOf<T>(); | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
if (_queue->availableSpace - sizeOf < 0) | |||
{ | |||
_queue->Grow<T>(); | |||
} | |||
return ref _queue->Reserve<T>(out index); | |||
} | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Enqueue<T> | |||
(in T item) where T : struct //should be unmanaged, but it's not due to Svelto.ECS constraints. | |||
{ | |||
unsafe | |||
{ | |||
BasicTests(); | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
var sizeOf = MemoryUtilities.SizeOf<T>(); | |||
if (_queue->availableSpace - sizeOf < 0) | |||
{ | |||
_queue->Grow<T>(); | |||
} | |||
_queue->Enqueue(item); | |||
} | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Clear() | |||
{ | |||
unsafe | |||
{ | |||
BasicTests(); | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
_queue->Clear(); | |||
} | |||
} | |||
} | |||
public T Dequeue<T>() where T : struct //should be unmanaged, but it's not due to Svelto.ECS constraints. | |||
{ | |||
unsafe | |||
{ | |||
BasicTests(); | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
return _queue->Dequeue<T>(); | |||
} | |||
} | |||
} | |||
public ref T AccessReserved<T>(UnsafeArrayIndex reservedIndex) where T : struct //should be unmanaged, but it's not due to Svelto.ECS constraints. | |||
{ | |||
unsafe | |||
{ | |||
BasicTests(); | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
return ref _queue->AccessReserved<T>(reservedIndex); | |||
} | |||
} | |||
} | |||
[Conditional("ENABLE_DEBUG_CHECKS")] | |||
unsafe void BasicTests() | |||
{ | |||
if (_queue == null) | |||
throw new Exception("SimpleNativeArray: null-access"); | |||
} | |||
readonly Sentinel _threadSentinel; | |||
#if UNITY_COLLECTIONS || UNITY_JOBS || UNITY_BURST | |||
#if UNITY_BURST | |||
[Unity.Burst.NoAlias] | |||
#endif | |||
[Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe UnsafeBlob* _queue; | |||
} | |||
} |
@@ -1,516 +0,0 @@ | |||
#if DEBUG && !PROFILE_SVELTO | |||
#define ENABLE_DEBUG_CHECKS | |||
#endif | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.Common.DataStructures; | |||
using Allocator = Svelto.Common.Allocator; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
public struct NativeDynamicArray : IDisposable | |||
{ | |||
public bool isValid | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get | |||
{ | |||
unsafe | |||
{ | |||
return _list != null; | |||
} | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public int Count<T>() where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception($"NativeDynamicArray: not expected type used"); | |||
#endif | |||
return (_list->count / MemoryUtilities.SizeOf<T>()); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public int SizeInBytes() | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
#endif | |||
return (_list->count); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public int Capacity<T>() where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
return (_list->capacity / MemoryUtilities.SizeOf<T>()); | |||
} | |||
} | |||
public static NativeDynamicArray Alloc<T>(uint newLength = 0) where T : struct | |||
{ | |||
return Alloc<T>(Allocator.Persistent, newLength); | |||
} | |||
public static NativeDynamicArray Alloc<T>(Allocator allocator, uint newLength = 0) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
var rtnStruc = new NativeDynamicArray | |||
{ | |||
_hashType = TypeHash<T>.hash, | |||
}; | |||
#else | |||
NativeDynamicArray rtnStruc = default; | |||
#endif | |||
UnsafeArray* listData = (UnsafeArray*)MemoryUtilities.Alloc<UnsafeArray>(1, allocator); | |||
//clear to nullify the pointers | |||
//MemoryUtilities.MemClear((IntPtr) listData, structSize); | |||
rtnStruc._allocator = allocator; | |||
listData->Realloc<T>(newLength, allocator); | |||
rtnStruc._list = listData; | |||
return rtnStruc; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T Get<T>(uint index) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
if (index >= Count<T>()) | |||
throw new Exception($"NativeDynamicArray: out of bound access, index {index} count {Count<T>()}"); | |||
#endif | |||
#if ENABLE_DEBUG_CHECKS | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
#endif | |||
return ref _list->Get<T>(index); | |||
#if ENABLE_DEBUG_CHECKS | |||
} | |||
#endif | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T Get<T>(int index) where T : struct | |||
{ | |||
return ref Get<T>((uint)index); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Set<T>(uint index, in T value) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
if (index >= Capacity<T>()) | |||
throw new Exception( | |||
$"NativeDynamicArray: out of bound access, index {index} capacity {Capacity<T>()}"); | |||
#endif | |||
#if ENABLE_DEBUG_CHECKS | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
#endif | |||
_list->Set(index, value); | |||
#if ENABLE_DEBUG_CHECKS | |||
} | |||
#endif | |||
} | |||
} | |||
public unsafe void Dispose() | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
#endif | |||
#if ENABLE_DEBUG_CHECKS | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
#endif | |||
_list->Dispose(_allocator); | |||
MemoryUtilities.Free((IntPtr)_list, _allocator); | |||
#if ENABLE_DEBUG_CHECKS | |||
} | |||
#endif | |||
_list = null; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Add<T>(in T item) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
#if ENABLE_DEBUG_CHECKS | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
#endif | |||
if (Count<T>() == Capacity<T>()) | |||
{ | |||
_list->Realloc<T>((uint)((Capacity<T>() + 1) * 1.5f), _allocator); | |||
} | |||
_list->Add(item); | |||
#if ENABLE_DEBUG_CHECKS | |||
} | |||
#endif | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T AddAt<T>(uint index) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
var structSize = (uint)MemoryUtilities.SizeOf<T>(); | |||
#if ENABLE_DEBUG_CHECKS | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
#endif | |||
if (index >= Capacity<T>()) | |||
_list->Realloc<T>((uint)((index + 1) * 1.5f), _allocator); | |||
var writeIndex = (index + 1) * structSize; | |||
if (_list->count < writeIndex) | |||
_list->SetCountTo(writeIndex); | |||
return ref _list->Get<T>(index); | |||
#if ENABLE_DEBUG_CHECKS | |||
} | |||
#endif | |||
} | |||
} | |||
public void Resize<T>(uint newCapacity) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
#if ENABLE_DEBUG_CHECKS | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
#endif | |||
_list->Realloc<T>((uint)newCapacity, _allocator); | |||
#if ENABLE_DEBUG_CHECKS | |||
} | |||
#endif | |||
} | |||
} | |||
public void SetCount<T>(uint count) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
uint structSize = (uint)MemoryUtilities.SizeOf<T>(); | |||
uint size = (uint)(count * structSize); | |||
#if ENABLE_DEBUG_CHECKS | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
#endif | |||
_list->SetCountTo((uint)size); | |||
#if ENABLE_DEBUG_CHECKS | |||
} | |||
#endif | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void AddWithoutGrow<T>(in T item) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
var structSize = (uint)MemoryUtilities.SizeOf<T>(); | |||
if (_list->space - (int)structSize < 0) | |||
throw new Exception("NativeDynamicArray: no writing authorized"); | |||
#endif | |||
#if ENABLE_DEBUG_CHECKS | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
#endif | |||
_list->Add(item); | |||
#if ENABLE_DEBUG_CHECKS | |||
} | |||
#endif | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void UnorderedRemoveAt<T>(uint index) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
if (Count<T>() == 0) | |||
throw new Exception("NativeDynamicArray: empty array invalid operation"); | |||
#endif | |||
#if ENABLE_DEBUG_CHECKS | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
#endif | |||
var indexToMove = Count<T>() - 1; | |||
if (index < indexToMove) | |||
{ | |||
Set<T>(index, Get<T>((uint)indexToMove)); | |||
} | |||
_list->Pop<T>(); | |||
#if ENABLE_DEBUG_CHECKS | |||
} | |||
#endif | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void FastClear() | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
#endif | |||
#if ENABLE_DEBUG_CHECKS | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
#endif | |||
_list->Clear(); | |||
#if ENABLE_DEBUG_CHECKS | |||
} | |||
#endif | |||
} | |||
} | |||
public unsafe T* ToPTR<T>() where T : unmanaged | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
return (T*)_list->ptr; | |||
} | |||
public IntPtr ToIntPTR<T>() where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
return (IntPtr)_list->ptr; | |||
} | |||
} | |||
public T[] ToManagedArray<T>() where T : unmanaged | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
var count = Count<T>(); | |||
var ret = new T[count]; | |||
var lengthToCopyInBytes = count * MemoryUtilities.SizeOf<T>(); | |||
#if ENABLE_DEBUG_CHECKS | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
#endif | |||
fixed (void* handle = ret) | |||
{ | |||
Unsafe.CopyBlock(handle, _list->ptr, (uint)lengthToCopyInBytes); | |||
} | |||
return ret; | |||
#if ENABLE_DEBUG_CHECKS | |||
} | |||
#endif | |||
} | |||
} | |||
public T[] ToManagedArrayUntrimmed<T>() where T : unmanaged | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
var capacity = Capacity<T>(); | |||
var ret = new T[capacity]; | |||
#if ENABLE_DEBUG_CHECKS | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
#endif | |||
fixed (void* handle = ret) | |||
{ | |||
MemoryUtilities.MemCpy<T>((IntPtr)_list->ptr, 0, (IntPtr)handle, 0, (uint)capacity); | |||
} | |||
#if ENABLE_DEBUG_CHECKS | |||
} | |||
#endif | |||
return ret; | |||
} | |||
} | |||
public void RemoveAt<T>(uint index) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
if (_list == null) | |||
throw new Exception("NativeDynamicArray: null-access"); | |||
if (_hashType != TypeHash<T>.hash) | |||
throw new Exception("NativeDynamicArray: not expected type used"); | |||
#endif | |||
#if ENABLE_DEBUG_CHECKS | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
#endif | |||
MemoryUtilities.MemMove<T>((IntPtr)_list->ptr, index + 1, index, (uint)(Count<T>() - (index + 1))); | |||
_list->Pop<T>(); | |||
#if ENABLE_DEBUG_CHECKS | |||
} | |||
#endif | |||
} | |||
} | |||
public void MemClear() | |||
{ | |||
unsafe | |||
{ | |||
#if ENABLE_DEBUG_CHECKS | |||
using (_threadSentinel.TestThreadSafety()) | |||
{ | |||
#endif | |||
MemoryUtilities.MemClear((IntPtr)_list->ptr, (uint)_list->capacity); | |||
#if ENABLE_DEBUG_CHECKS | |||
} | |||
#endif | |||
} | |||
} | |||
#if UNITY_COLLECTIONS || UNITY_JOBS || UNITY_BURST | |||
#if UNITY_BURST | |||
[Unity.Burst.NoAlias] | |||
#endif | |||
[Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe UnsafeArray* _list; | |||
#if DEBUG && !PROFILE_SVELTO | |||
int _hashType; | |||
#endif | |||
Sentinel _threadSentinel; | |||
Allocator _allocator; | |||
} | |||
} |
@@ -1,82 +0,0 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
public struct NativeDynamicArrayCast<T>:IDisposable where T : struct | |||
{ | |||
public NativeDynamicArrayCast(uint size, Allocator allocator) | |||
{ | |||
_array = NativeDynamicArray.Alloc<T>(allocator, size); | |||
} | |||
public NativeDynamicArrayCast(NativeDynamicArray array) : this() { _array = array; } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public int Count() => _array.Count<T>(); | |||
public int count | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array.Count<T>(); | |||
} | |||
public int capacity | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _array.Capacity<T>(); | |||
} | |||
public ref T this[int index] | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref _array.Get<T>((uint) index); | |||
} | |||
public ref T this[uint index] | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref _array.Get<T>(index); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Add(in T id) { _array.Add(id); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void UnorderedRemoveAt(uint index) { _array.UnorderedRemoveAt<T>(index); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveAt(uint index) { _array.RemoveAt<T>(index); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Clear() { _array.FastClear(); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Dispose() { _array.Dispose(); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T AddAt(uint lastIndex) { return ref _array.AddAt<T>(lastIndex); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Resize(uint newSize) { _array.Resize<T>(newSize); } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public NativeDynamicArray ToNativeArray() { return _array; } | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Set(uint index, in T value) | |||
{ | |||
_array.Set(index, value); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Set(int index, in T value) | |||
{ | |||
_array.Set((uint)index, value); | |||
} | |||
public bool isValid => _array.isValid; | |||
NativeDynamicArray _array; | |||
} | |||
} |
@@ -1,23 +0,0 @@ | |||
#if UNITY_COLLECTIONS | |||
using Unity.Collections; | |||
using Unity.Collections.LowLevel.Unsafe; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
public static class NativeDynamicArrayUnityExtension | |||
{ | |||
public static NativeArray<T> ToNativeArray<T>(this NativeDynamicArray array) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
var nativeArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<T>( | |||
(void*) array.ToIntPTR<T>(), (int) array.Count<T>(), Allocator.None); | |||
#if ENABLE_UNITY_COLLECTIONS_CHECKS | |||
NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref nativeArray, AtomicSafetyHandle.Create()); | |||
#endif | |||
return nativeArray; | |||
} | |||
} | |||
} | |||
} | |||
#endif |
@@ -1,47 +0,0 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
public struct SharedDisposableNative<T> : IDisposable where T : unmanaged, IDisposable | |||
{ | |||
#if UNITY_COLLECTIONS || (UNITY_JOBS || UNITY_BURST) | |||
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe IntPtr ptr; | |||
public SharedDisposableNative(in T value) | |||
{ | |||
unsafe | |||
{ | |||
ptr = MemoryUtilities.Alloc<T>(1, Allocator.Persistent); | |||
Unsafe.Write((void*)ptr, value); | |||
} | |||
} | |||
public void Dispose() | |||
{ | |||
unsafe | |||
{ | |||
Unsafe.AsRef<T>((void*)ptr).Dispose(); | |||
MemoryUtilities.Free((IntPtr)ptr, Allocator.Persistent); | |||
ptr = IntPtr.Zero; | |||
} | |||
} | |||
public ref T value | |||
{ | |||
get | |||
{ | |||
unsafe | |||
{ | |||
DBC.ECS.Check.Require(ptr != null, "SharedNative has not been initialized"); | |||
return ref Unsafe.AsRef<T>((void*)ptr); | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -1,127 +0,0 @@ | |||
using System; | |||
using System.Threading; | |||
using Svelto.Common; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
public struct SharedNativeInt: IDisposable | |||
{ | |||
#if UNITY_COLLECTIONS || (UNITY_JOBS || UNITY_BURST) | |||
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe int* data; | |||
Allocator _allocator; | |||
public SharedNativeInt(Allocator allocator) | |||
{ | |||
unsafe | |||
{ | |||
_allocator = allocator; | |||
data = (int*) MemoryUtilities.Alloc(sizeof(int), allocator); | |||
} | |||
} | |||
public static SharedNativeInt Create(int t, Allocator allocator) | |||
{ | |||
unsafe | |||
{ | |||
var current = new SharedNativeInt(); | |||
current._allocator = allocator; | |||
current.data = (int*) MemoryUtilities.Alloc(sizeof(int), allocator); | |||
*current.data = t; | |||
return current; | |||
} | |||
} | |||
public static implicit operator int(SharedNativeInt t) | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (t.data == null) | |||
throw new Exception("using disposed SharedInt"); | |||
#endif | |||
return *t.data; | |||
} | |||
} | |||
public void Dispose() | |||
{ | |||
unsafe | |||
{ | |||
if (data != null) | |||
{ | |||
MemoryUtilities.Free((IntPtr) data, _allocator); | |||
data = null; | |||
} | |||
} | |||
} | |||
public int Decrement() | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (data == null) | |||
throw new Exception("null-access"); | |||
#endif | |||
return Interlocked.Decrement(ref *data); | |||
} | |||
} | |||
public int Increment() | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (data == null) | |||
throw new Exception("null-access"); | |||
#endif | |||
return Interlocked.Increment(ref *data); | |||
} | |||
} | |||
public int Add(int val) | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (data == null) | |||
throw new Exception("null-access"); | |||
#endif | |||
return Interlocked.Add(ref *data, val); | |||
} | |||
} | |||
public int CompareExchange(int value, int compare) | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (data == null) | |||
throw new Exception("null-access"); | |||
#endif | |||
return Interlocked.CompareExchange(ref *data, value, compare); | |||
} | |||
} | |||
public void Set(int val) | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (data == null) | |||
throw new Exception("null-access"); | |||
#endif | |||
Volatile.Write(ref *data, val); | |||
} | |||
} | |||
} | |||
} |
@@ -1,147 +0,0 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
struct UnsafeArray | |||
{ | |||
internal unsafe byte* ptr => _ptr; | |||
//expressed in bytes | |||
internal int capacity => (int) _capacity; | |||
//expressed in bytes | |||
internal int count => (int) _writeIndex; | |||
//expressed in bytes | |||
internal int space => capacity - count; | |||
#if DEBUG && !PROFILE_SVELTO | |||
#pragma warning disable 649 | |||
internal uint id; | |||
#pragma warning restore 649 | |||
#endif | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T Get<T>(uint index) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
uint sizeOf = (uint) MemoryUtilities.SizeOf<T>(); | |||
if (index + sizeOf > _writeIndex) | |||
throw new Exception("no reading authorized"); | |||
#endif | |||
return ref Unsafe.AsRef<T>(Unsafe.Add<T>(ptr, (int) index)); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Set<T>(uint index, in T value) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
uint sizeOf = (uint) MemoryUtilities.SizeOf<T>(); | |||
uint writeIndex = (uint) (index * sizeOf); | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_capacity < writeIndex + sizeOf) | |||
throw new Exception("no writing authorized"); | |||
#endif | |||
Unsafe.AsRef<T>(Unsafe.Add<T>(_ptr, (int) index)) = value; | |||
if (_writeIndex < writeIndex + sizeOf) | |||
_writeIndex = (uint) (writeIndex + sizeOf); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Add<T>(in T value) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
var structSize = MemoryUtilities.SizeOf<T>(); | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (space - structSize < 0) | |||
throw new Exception("no writing authorized"); | |||
#endif | |||
Unsafe.Write(ptr + _writeIndex, value); | |||
_writeIndex += (uint)structSize; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T Pop<T>() where T : struct | |||
{ | |||
unsafe | |||
{ | |||
var structSize = MemoryUtilities.SizeOf<T>(); | |||
_writeIndex -= (uint)structSize; | |||
return ref Unsafe.AsRef<T>(ptr + _writeIndex); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal void Realloc<T>(uint newCapacity, Allocator allocator) where T : struct | |||
{ | |||
unsafe | |||
{ | |||
var structSize = (uint) MemoryUtilities.SizeOf<T>(); | |||
uint newCapacityInBytes = structSize * newCapacity; | |||
if (_ptr == null) | |||
_ptr = (byte*) MemoryUtilities.Alloc(newCapacityInBytes, allocator); | |||
else | |||
_ptr = (byte*) MemoryUtilities.Realloc((IntPtr) _ptr, newCapacityInBytes, allocator, (uint) count); | |||
_capacity = newCapacityInBytes; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Dispose(Allocator allocator) | |||
{ | |||
unsafe | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (ptr == null) | |||
throw new Exception("UnsafeArray: try to dispose an already disposed array"); | |||
#endif | |||
MemoryUtilities.Free((IntPtr) ptr, allocator); | |||
_ptr = null; | |||
_writeIndex = 0; | |||
_capacity = 0; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Clear() | |||
{ | |||
_writeIndex = 0; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SetCountTo(uint count) | |||
{ | |||
_writeIndex = count; | |||
} | |||
#if UNITY_COLLECTIONS || UNITY_JOBS || UNITY_BURST | |||
#if UNITY_BURST | |||
[Unity.Burst.NoAlias] | |||
#endif | |||
[Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] | |||
#endif | |||
unsafe byte* _ptr; | |||
uint _writeIndex; | |||
uint _capacity; | |||
} | |||
} |
@@ -1,272 +0,0 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
namespace Svelto.ECS.DataStructures | |||
{ | |||
//Necessary to be sure that the user won't pass random values | |||
public struct UnsafeArrayIndex | |||
{ | |||
internal uint index; | |||
} | |||
/// <summary> | |||
/// Note: this must work inside burst, so it must follow burst restrictions | |||
/// It's a typeless native queue based on a ring-buffer model. This means that the writing head and the | |||
/// reading head always advance independently. If there is enough space left by dequeued elements, | |||
/// the writing head will wrap around. The writing head cannot ever surpass the reading head. | |||
/// | |||
/// </summary> | |||
struct UnsafeBlob : IDisposable | |||
{ | |||
internal unsafe byte* ptr { get; set; } | |||
//expressed in bytes | |||
internal uint capacity { get; private set; } | |||
//expressed in bytes | |||
internal uint size | |||
{ | |||
get | |||
{ | |||
var currentSize = (uint) _writeIndex - _readIndex; | |||
#if DEBUG && !PROFILE_SVELTO | |||
if ((currentSize & (4 - 1)) != 0) | |||
throw new Exception("size is expected to be a multiple of 4"); | |||
#endif | |||
return currentSize; | |||
} | |||
} | |||
//expressed in bytes | |||
internal uint availableSpace => capacity - size; | |||
/// <summary> | |||
/// </summary> | |||
internal Allocator allocator; | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal void Enqueue<T>(in T item) where T : struct //should be unmanaged, but it's not due to Svelto.ECS constraints. | |||
{ | |||
unsafe | |||
{ | |||
var structSize = (uint) MemoryUtilities.SizeOf<T>(); | |||
var writeHead = _writeIndex % capacity; | |||
#if DEBUG && !PROFILE_SVELTO | |||
var size = _writeIndex - _readIndex; | |||
var spaceAvailable = capacity - size; | |||
if (spaceAvailable - (int) structSize < 0) | |||
throw new Exception("no writing authorized"); | |||
if ((writeHead & (4 - 1)) != 0) | |||
throw new Exception("write head is expected to be a multiple of 4"); | |||
#endif | |||
if (writeHead + structSize <= capacity) | |||
{ | |||
Unsafe.Write(ptr + writeHead, item); | |||
} | |||
else //copy with wrap, will start to copy and wrap for the remainder | |||
{ | |||
var byteCountToEnd = capacity - writeHead; | |||
var localCopyToAvoidGcIssues = item; | |||
//read and copy the first portion of Item until the end of the stream | |||
Unsafe.CopyBlock(ptr + writeHead, Unsafe.AsPointer(ref localCopyToAvoidGcIssues) | |||
, (uint) byteCountToEnd); | |||
var restCount = structSize - byteCountToEnd; | |||
//read and copy the remainder | |||
Unsafe.CopyBlock(ptr, (byte*) Unsafe.AsPointer(ref localCopyToAvoidGcIssues) + byteCountToEnd | |||
, (uint) restCount); | |||
} | |||
//this is may seems a waste if you are going to use an unsafeBlob just for bytes, but it's necessary for mixed types. | |||
//it's still possible to use WriteUnaligned though | |||
uint paddedStructSize = (uint) (structSize + (int) MemoryUtilities.Pad4(structSize)); | |||
_writeIndex += paddedStructSize; //we want _writeIndex to be always aligned by 4 | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
//The index returned is the index of the unwrapped ring. It must be wrapped again before to be used | |||
internal ref T Reserve<T>(out UnsafeArrayIndex index) where T : struct //should be unmanaged, but it's not due to Svelto.ECS constraints. | |||
{ | |||
unsafe | |||
{ | |||
var structSize = (uint) MemoryUtilities.SizeOf<T>(); | |||
var wrappedIndex = _writeIndex % capacity; | |||
#if DEBUG && !PROFILE_SVELTO | |||
var size = _writeIndex - _readIndex; | |||
var spaceAvailable = capacity - size; | |||
if (spaceAvailable - (int) structSize < 0) | |||
throw new Exception("no writing authorized"); | |||
if ((wrappedIndex & (4 - 1)) != 0) | |||
throw new Exception("write head is expected to be a multiple of 4"); | |||
#endif | |||
ref var buffer = ref Unsafe.AsRef<T>(ptr + wrappedIndex); | |||
index.index = _writeIndex; | |||
_writeIndex += structSize + MemoryUtilities.Pad4(structSize); | |||
return ref buffer; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal ref T AccessReserved<T>(UnsafeArrayIndex index) where T : struct //should be unmanaged, but it's not due to Svelto.ECS constraints. | |||
{ | |||
unsafe | |||
{ | |||
var wrappedIndex = index.index % capacity; | |||
#if DEBUG && !PROFILE_SVELTO | |||
if ((index.index & 3) != 0) | |||
throw new Exception($"invalid index detected"); | |||
#endif | |||
return ref Unsafe.AsRef<T>(ptr + wrappedIndex); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal T Dequeue<T>() where T : struct //should be unmanaged, but it's not due to Svelto.ECS constraints. | |||
{ | |||
unsafe | |||
{ | |||
var structSize = (uint) MemoryUtilities.SizeOf<T>(); | |||
var readHead = _readIndex % capacity; | |||
#if DEBUG && !PROFILE_SVELTO | |||
var size = _writeIndex - _readIndex; | |||
if (size < structSize) //are there enough bytes to read? | |||
throw new Exception("dequeuing empty queue or unexpected type dequeued"); | |||
if (_readIndex > _writeIndex) | |||
throw new Exception("unexpected read"); | |||
if ((readHead & (4 - 1)) != 0) | |||
throw new Exception("read head is expected to be a multiple of 4"); | |||
#endif | |||
var paddedStructSize = structSize + MemoryUtilities.Pad4(structSize); | |||
_readIndex += paddedStructSize; | |||
if (_readIndex == _writeIndex) | |||
{ | |||
//resetting the Indices has the benefit to let the Reserve work in more occasions and | |||
//the rapping happening less often. If the _readIndex reached the _writeIndex, it means | |||
//that there is no data left to read, so we can start to write again from the begin of the memory | |||
_writeIndex = 0; | |||
_readIndex = 0; | |||
} | |||
if (readHead + paddedStructSize <= capacity) | |||
return Unsafe.Read<T>(ptr + readHead); | |||
//handle the case the structure wraps around so it must be reconstructed from the part at the | |||
//end of the stream and the part starting from the begin. | |||
T item = default; | |||
var byteCountToEnd = capacity - readHead; | |||
Unsafe.CopyBlock(Unsafe.AsPointer(ref item), ptr + readHead, byteCountToEnd); | |||
var restCount = structSize - byteCountToEnd; | |||
Unsafe.CopyBlock((byte*) Unsafe.AsPointer(ref item) + byteCountToEnd, ptr, restCount); | |||
return item; | |||
} | |||
} | |||
/// <summary> | |||
/// This code unwraps the queue and resizes the array, but doesn't change the unwrapped index of existing elements. | |||
/// In this way the previously reserved indices will remain valid | |||
/// </summary> | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal void Grow<T>() where T : struct //should be unmanaged, but it's not due to Svelto.ECS constraints. | |||
{ | |||
unsafe | |||
{ | |||
var sizeOf = MemoryUtilities.SizeOf<T>(); | |||
var oldCapacity = capacity; | |||
uint newCapacity = (uint) ((oldCapacity + sizeOf) << 1); | |||
//be sure it's multiple of 4. Assuming that what we write is aligned to 4, then we will always have aligned wrapped heads | |||
//the reading and writing head always increment in multiple of 4 | |||
newCapacity += MemoryUtilities.Pad4(newCapacity); | |||
byte* newPointer = null; | |||
newPointer = (byte*) MemoryUtilities.Alloc(newCapacity, allocator); | |||
//copy wrapped content if there is any | |||
var currentSize = _writeIndex - _readIndex; | |||
if (currentSize > 0) | |||
{ | |||
var oldReaderHead = _readIndex % oldCapacity; | |||
var oldWriterHead = _writeIndex % oldCapacity; | |||
//Remembering that the unwrapped reader cannot ever surpass the unwrapped writer, if the reader is behind the writer | |||
//it means that the writer didn't wrap. It's the natural position so the data can be copied with | |||
//a single memcpy | |||
if (oldReaderHead < oldWriterHead) | |||
{ | |||
var newReaderHead = _readIndex % newCapacity; | |||
Unsafe.CopyBlock(newPointer + newReaderHead, ptr + oldReaderHead, (uint) currentSize); | |||
} | |||
else | |||
{ | |||
//if the wrapped writer is behind the wrapped reader, it means the writer wrapped. Therefore | |||
//I need to copy the data from the current wrapped reader to the end and then from the | |||
//begin of the array to the current wrapped writer. | |||
var byteCountToEnd = oldCapacity - oldReaderHead; //bytes to copy from the reader to the end | |||
var newReaderHead = _readIndex % newCapacity; | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (newReaderHead + byteCountToEnd + oldWriterHead > newCapacity) //basically the test is the old size must be less than the new capacity. | |||
throw new Exception("something is wrong with my previous assumptions"); | |||
#endif | |||
//I am leaving on purpose gap at the begin of the new array if there is any, it will be | |||
//anyway used once it's time to wrap. | |||
Unsafe.CopyBlock(newPointer + newReaderHead, ptr + oldReaderHead, byteCountToEnd); //from the old reader head to the end of the old array | |||
Unsafe.CopyBlock(newPointer + newReaderHead + byteCountToEnd, ptr + 0, (uint) oldWriterHead); //from the begin of the old array to the old writer head (rember the writerHead wrapped) | |||
} | |||
} | |||
if (ptr != null) | |||
MemoryUtilities.Free((IntPtr) ptr, allocator); | |||
ptr = newPointer; | |||
capacity = newCapacity; | |||
//_readIndex = 0; the idea is that the old readIndex should remain unchanged. Remember this is the unwrapped index. | |||
_writeIndex = _readIndex + currentSize; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Dispose() | |||
{ | |||
unsafe | |||
{ | |||
if (ptr != null) | |||
MemoryUtilities.Free((IntPtr) ptr, allocator); | |||
ptr = null; | |||
_writeIndex = 0; | |||
capacity = 0; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Clear() | |||
{ | |||
_writeIndex = 0; | |||
_readIndex = 0; | |||
} | |||
uint _writeIndex; | |||
uint _readIndex; | |||
} | |||
} |
@@ -8,7 +8,6 @@ using System.Threading; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Native; | |||
using Svelto.ECS.DataStructures; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
@@ -20,8 +19,8 @@ namespace Svelto.ECS.Internal | |||
} | |||
#endif | |||
public sealed class UnmanagedTypeSafeDictionary<TValue> : ITypeSafeDictionary<TValue> | |||
where TValue : struct, IBaseEntityComponent | |||
sealed class UnmanagedTypeSafeDictionary<TValue> : ITypeSafeDictionary<TValue> | |||
where TValue : struct, _IInternalEntityComponent | |||
{ | |||
static readonly ThreadLocal<IEntityIDs> cachedEntityIDN = | |||
new ThreadLocal<IEntityIDs>(() => new NativeEntityIDs()); | |||
@@ -106,13 +105,13 @@ namespace Svelto.ECS.Internal | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ITypeSafeDictionary Create() | |||
{ | |||
return TypeSafeDictionaryFactory<TValue>.Create(1); | |||
return new UnmanagedTypeSafeDictionary<TValue>(1); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Clear() | |||
{ | |||
implUnmgd.dictionary.FastClear(); | |||
implUnmgd.dictionary.Clear(); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -0,0 +1,49 @@ | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Experimental; | |||
using Svelto.DataStructures.Native; | |||
namespace Svelto.ECS.ResourceManager | |||
{ | |||
/// <summary> | |||
/// Inherit this class to have the base functionalities to implement a custom ECS compatible resource manager | |||
/// </summary> | |||
public class ECSResourceManager<T> where T : class | |||
{ | |||
protected ECSResourceManager() | |||
{ | |||
_sparse = new ValueContainer<T, ManagedStrategy<T>, NativeStrategy<SparseIndex>>(16); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ValueIndex Add(in T resource) | |||
{ | |||
return _sparse.Add(resource); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Remove(ValueIndex index) | |||
{ | |||
_sparse.Remove(index); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Clear() | |||
{ | |||
_sparse.Clear(); | |||
} | |||
public T this[ValueIndex index] | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _sparse[index]; | |||
} | |||
~ECSResourceManager() | |||
{ | |||
_sparse.Dispose(); | |||
} | |||
ValueContainer<T, ManagedStrategy<T>, NativeStrategy<SparseIndex>> _sparse; | |||
} | |||
} |
@@ -1,14 +1,7 @@ | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS.Experimental | |||
namespace Svelto.ECS.ResourceManager | |||
{ | |||
// struct ECSResources<T> | |||
// { | |||
// internal uint id; | |||
// | |||
// public static implicit operator T(ECSResources<T> ecsString) { return ResourcesECSDB<T>.FromECS(ecsString.id); } | |||
// } | |||
/// <summary> | |||
/// To do. Or we reuse the ID or we need to clear this | |||
/// </summary> | |||
@@ -1,7 +1,7 @@ | |||
using System; | |||
using System.Runtime.InteropServices; | |||
namespace Svelto.ECS.Experimental | |||
namespace Svelto.ECS.ResourceManager | |||
{ | |||
/// <summary> | |||
/// Todo: the entityDB should be aware of the ECSString and recycle it on entity removal | |||
@@ -1,24 +0,0 @@ | |||
using System; | |||
namespace Svelto.ECS | |||
{ | |||
[AllowMultiple] | |||
public class DisposeDisposablesEngine : IEngine, IDisposable | |||
{ | |||
public DisposeDisposablesEngine(IDisposable[] disposable) | |||
{ | |||
_disposable = disposable; | |||
} | |||
public void Dispose() | |||
{ | |||
foreach (var d in _disposable) | |||
{ | |||
d.Dispose(); | |||
} | |||
} | |||
IDisposable[] _disposable; | |||
} | |||
} |
@@ -3,7 +3,6 @@ using System; | |||
using DBC.ECS; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.DataStructures; | |||
using Svelto.ECS.Internal; | |||
using Svelto.ECS.Native; | |||
@@ -17,8 +16,10 @@ namespace Svelto.ECS | |||
//todo: remove operation array and store entity descriptor hash in the return value | |||
//todo I maybe able to provide a _nativeSwap.SwapEntity<entityDescriptor> | |||
//todo make this work with base descriptors too | |||
var descriptorComponentsToRemove = EntityDescriptorTemplate<T>.descriptor.componentsToBuild; | |||
_nativeRemoveOperations.Add(new NativeOperationRemove( | |||
EntityDescriptorTemplate<T>.descriptor.componentsToBuild, TypeCache<T>.type | |||
descriptorComponentsToRemove, TypeCache<T>.type | |||
, memberName)); | |||
return new NativeEntityRemove(_nativeRemoveOperationQueue, _nativeRemoveOperations.count - 1); | |||
@@ -54,7 +55,7 @@ namespace Svelto.ECS | |||
//todo, I don't like that this scans all the queues even if they are empty | |||
for (int i = 0; i < removeBuffersCount; i++) | |||
{ | |||
ref var buffer = ref _nativeRemoveOperationQueue.GetBuffer(i); | |||
ref var buffer = ref _nativeRemoveOperationQueue.GetBag(i); | |||
while (buffer.IsEmpty() == false) | |||
{ | |||
@@ -78,7 +79,7 @@ namespace Svelto.ECS | |||
var swapBuffersCount = _nativeSwapOperationQueue.count; | |||
for (int i = 0; i < swapBuffersCount; i++) | |||
{ | |||
ref var buffer = ref _nativeSwapOperationQueue.GetBuffer(i); | |||
ref var buffer = ref _nativeSwapOperationQueue.GetBag(i); | |||
while (buffer.IsEmpty() == false) | |||
{ | |||
@@ -105,7 +106,7 @@ namespace Svelto.ECS | |||
var addBuffersCount = _nativeAddOperationQueue.count; | |||
for (int i = 0; i < addBuffersCount; i++) | |||
{ | |||
ref var buffer = ref _nativeAddOperationQueue.GetBuffer(i); | |||
ref var buffer = ref _nativeAddOperationQueue.GetBag(i); | |||
//todo: I don't like to iterate a constant number of buffer and skip the empty ones | |||
while (buffer.IsEmpty() == false) | |||
{ | |||
@@ -162,7 +163,6 @@ namespace Svelto.ECS | |||
FasterList<NativeOperationSwap> _nativeSwapOperations; | |||
FasterList<NativeOperationBuild> _nativeAddOperations; | |||
//todo: I very likely don't need to create one for each native entity factory, the same can be reused | |||
readonly AtomicNativeBags _nativeAddOperationQueue; | |||
readonly AtomicNativeBags _nativeRemoveOperationQueue; | |||
readonly AtomicNativeBags _nativeSwapOperationQueue; | |||
@@ -104,6 +104,9 @@ namespace Svelto.ECS | |||
return ref entitiesDb.QueryEntity<T>(new EGID(id, group)); | |||
} | |||
/// <summary> | |||
/// Expects that only one entity of type T exists in the group | |||
/// </summary> | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static ref T QueryUniqueEntity<T> | |||
(this EntitiesDB entitiesDb, ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent | |||
@@ -147,5 +150,12 @@ namespace Svelto.ECS | |||
array = default; | |||
return false; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static AllGroupsEnumerable<T1> QueryEntities<T1>(this EntitiesDB db) | |||
where T1 :unmanaged, IEntityComponent | |||
{ | |||
return new AllGroupsEnumerable<T1>(db); | |||
} | |||
} | |||
} |
@@ -3,7 +3,7 @@ using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Native; | |||
using Svelto.ECS.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS.Native | |||
{ | |||
@@ -13,7 +13,7 @@ namespace Svelto.ECS.Native | |||
/// that a job can use it as long as nothing else is modifying the entities database and the NativeEGIDMapper | |||
/// is disposed right after the use. | |||
/// </summary> | |||
public readonly struct NativeEGIDMapper<T> : IEGIDMapper where T : unmanaged, IBaseEntityComponent | |||
public readonly struct NativeEGIDMapper<T> : IEGIDMapper where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
public static readonly NativeEGIDMapper<T> empty = new NativeEGIDMapper<T> | |||
(default, new SharedSveltoDictionaryNative<uint, T>(0, Allocator.Persistent)); | |||
@@ -1,7 +1,6 @@ | |||
using System; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Native; | |||
using Svelto.ECS.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS.Native | |||
{ | |||
@@ -14,7 +13,7 @@ namespace Svelto.ECS.Native | |||
///WARNING: REMEMBER THIS MUST BE DISPOSED OF, AS IT USES NATIVE MEMORY. IT WILL LEAK MEMORY OTHERWISE | |||
/// | |||
/// </summary> | |||
public struct NativeEGIDMultiMapper<T> : IDisposable where T : unmanaged, IBaseEntityComponent | |||
public struct NativeEGIDMultiMapper<T> : IDisposable where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
public NativeEGIDMultiMapper(in SveltoDictionaryNative<ExclusiveGroupStruct, SharedSveltoDictionaryNative<uint, T>> dictionary) | |||
{ | |||
@@ -1,27 +1,42 @@ | |||
#if UNITY_NATIVE | |||
using Svelto.ECS.DataStructures; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS.Native | |||
{ | |||
public readonly struct NativeEntityFactory | |||
{ | |||
internal NativeEntityFactory(AtomicNativeBags addOperationQueue, int index, EnginesRoot.EntityReferenceMap entityLocator) | |||
internal NativeEntityFactory(AtomicNativeBags addOperationQueue, int operationIndex, EnginesRoot.EntityReferenceMap entityLocator) | |||
{ | |||
_index = index; | |||
_operationIndex = operationIndex; | |||
_addOperationQueue = addOperationQueue; | |||
_entityLocator = entityLocator; | |||
_entityLocator = entityLocator; | |||
} | |||
public NativeEntityInitializer BuildEntity | |||
(uint eindex, ExclusiveBuildGroup exclusiveBuildGroup, int threadIndex) | |||
/// <summary> | |||
/// TODO is this still true?: | |||
/// | |||
/// var entity1Init = nativeFactory.BuildEntity(new EGID(1, Group.TestGroupA), threadIndex); | |||
/// var entity2Init = nativeFactory.BuildEntity(new EGID(2, Group.TestGroupA), threadIndex); | |||
/// and expect that entity1Init is still valid and I have to invalidate it | |||
/// I think I fixed it, but needs more test | |||
/// However we should remove atomicBags and use svelto dictionar | |||
/// </summary> | |||
/// <param name="eindex"></param> | |||
/// <param name="exclusiveBuildGroup"></param> | |||
/// <param name="threadIndex"></param> | |||
/// <returns></returns> | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public NativeEntityInitializer BuildEntity(uint eindex, ExclusiveBuildGroup exclusiveBuildGroup, int threadIndex) | |||
{ | |||
EntityReference reference = _entityLocator.ClaimReference(); | |||
NativeBag bagPerEntityPerThread = _addOperationQueue.GetBuffer(threadIndex + 1); | |||
NativeBag bagPerEntityPerThread = _addOperationQueue.GetBag(threadIndex + 1); | |||
bagPerEntityPerThread.Enqueue(_index); //each native ECS native operation is stored in an array, each request to perform a native operation in a queue. _index is the index of the operation in the array that will be dequeued later | |||
bagPerEntityPerThread.Enqueue( | |||
_operationIndex); //each native operation is stored in an array, each request to perform a native operation in a queue. _index is the index of the operation in the array that will be dequeued later | |||
bagPerEntityPerThread.Enqueue(new EGID(eindex, exclusiveBuildGroup)); | |||
bagPerEntityPerThread.Enqueue(reference); | |||
//NativeEntityInitializer is quite a complex beast. It holds the starting values of the component set by the user. These components must be later dequeued and in order to know how many components | |||
//must be dequeued, a count must be used. The space to hold the count is then reserved in the queue and index will be used access the count later on through NativeEntityInitializer so it can increment it. | |||
//index is not the number of components of the entity, it's just the number of components that the user decide to initialise | |||
@@ -30,14 +45,15 @@ namespace Svelto.ECS.Native | |||
return new NativeEntityInitializer(bagPerEntityPerThread, index, reference); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public NativeEntityInitializer BuildEntity(EGID egid, int threadIndex) | |||
{ | |||
return BuildEntity(egid.entityID, egid.groupID, threadIndex); | |||
} | |||
readonly EnginesRoot.EntityReferenceMap _entityLocator; | |||
readonly AtomicNativeBags _addOperationQueue; | |||
readonly int _index; | |||
readonly EnginesRoot.EntityReferenceMap _entityLocator; | |||
readonly AtomicNativeBags _addOperationQueue; | |||
readonly int _operationIndex; | |||
} | |||
} | |||
#endif |
@@ -1,31 +1,35 @@ | |||
#if UNITY_NATIVE //at the moment I am still considering NativeOperations useful only for Unity | |||
using Svelto.ECS.DataStructures; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS.Native | |||
{ | |||
public readonly ref struct NativeEntityInitializer | |||
{ | |||
readonly NativeBag _unsafeBuffer; | |||
readonly UnsafeArrayIndex _index; | |||
readonly EntityReference _reference; | |||
readonly NativeBag _unsafeBuffer; | |||
readonly UnsafeArrayIndex _componentsToInitializeCounterRef; | |||
readonly EntityReference _reference; | |||
public NativeEntityInitializer(in NativeBag unsafeBuffer, UnsafeArrayIndex index, EntityReference reference) | |||
public NativeEntityInitializer(in NativeBag unsafeBuffer, UnsafeArrayIndex componentsToInitializeCounterRef, EntityReference reference) | |||
{ | |||
_unsafeBuffer = unsafeBuffer; | |||
_index = index; | |||
_reference = reference; | |||
_componentsToInitializeCounterRef = componentsToInitializeCounterRef; | |||
_reference = reference; | |||
} | |||
public void Init<T>(in T component) where T : unmanaged, IEntityComponent | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref T Init<T>(in T component) where T : unmanaged, IEntityComponent | |||
{ | |||
uint id = EntityComponentID<T>.ID.Data; | |||
uint componentID = EntityComponentID<T>.ID.Data; | |||
_unsafeBuffer.AccessReserved<uint>(_index)++; //number of components added so far | |||
_unsafeBuffer.AccessReserved<uint>(_componentsToInitializeCounterRef)++; //increase the number of components that have been initialised by the user | |||
//Since NativeEntityInitializer is a ref struct, it guarantees that I am enqueueing components of the | |||
//last entity built | |||
_unsafeBuffer.Enqueue(id); | |||
_unsafeBuffer.Enqueue(component); | |||
_unsafeBuffer.Enqueue(componentID); //to know what component it's being stored | |||
_unsafeBuffer.ReserveEnqueue<T>(out var index) = component; | |||
return ref _unsafeBuffer.AccessReserved<T>(index); | |||
} | |||
public EntityReference reference => _reference; | |||
@@ -1,5 +1,5 @@ | |||
#if UNITY_NATIVE | |||
using Svelto.ECS.DataStructures; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS.Native | |||
{ | |||
@@ -16,7 +16,7 @@ namespace Svelto.ECS.Native | |||
public void RemoveEntity(EGID egid, int threadIndex) | |||
{ | |||
var simpleNativeBag = _removeQueue.GetBuffer(threadIndex); | |||
var simpleNativeBag = _removeQueue.GetBag(threadIndex); | |||
simpleNativeBag.Enqueue(_indexRemove); | |||
simpleNativeBag.Enqueue(egid); | |||
@@ -1,5 +1,5 @@ | |||
#if UNITY_NATIVE | |||
using Svelto.ECS.DataStructures; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS.Native | |||
{ | |||
@@ -16,7 +16,7 @@ namespace Svelto.ECS.Native | |||
public void SwapEntity(EGID from, EGID to, int threadIndex) | |||
{ | |||
var simpleNativeBag = _swapQueue.GetBuffer(threadIndex); | |||
var simpleNativeBag = _swapQueue.GetBag(threadIndex); | |||
simpleNativeBag.Enqueue(_indexSwap); | |||
simpleNativeBag.Enqueue(new DoubleEGID(from, to)); | |||
@@ -24,7 +24,7 @@ namespace Svelto.ECS.Native | |||
public void SwapEntity(EGID from, ExclusiveBuildGroup to, int threadIndex) | |||
{ | |||
var simpleNativeBag = _swapQueue.GetBuffer(threadIndex); | |||
var simpleNativeBag = _swapQueue.GetBag(threadIndex); | |||
simpleNativeBag.Enqueue(_indexSwap); | |||
simpleNativeBag.Enqueue(new DoubleEGID(from, new EGID(from.entityID, to))); | |||
} | |||
@@ -2,7 +2,6 @@ using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Native; | |||
using Svelto.ECS.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS.Native | |||
@@ -49,7 +48,7 @@ namespace Svelto.ECS.Native | |||
/// must be unit tested! | |||
public static NativeEGIDMultiMapper<T> QueryNativeMappedEntities<T>(this EntitiesDB entitiesDb, | |||
LocalFasterReadOnlyList<ExclusiveGroupStruct> groups, Allocator allocator) | |||
where T : unmanaged, IBaseEntityComponent | |||
where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
var dictionary = new SveltoDictionaryNative<ExclusiveGroupStruct, SharedSveltoDictionaryNative<uint, T>> | |||
((uint) groups.count, allocator); | |||
@@ -10,8 +10,8 @@ namespace Svelto.ECS | |||
{ | |||
var iterationsPerBatch = totalIterations / processorCount; | |||
if (iterationsPerBatch < 32) | |||
return 32; | |||
if (iterationsPerBatch < 64) | |||
return 64; | |||
return (int) iterationsPerBatch; | |||
} | |||
@@ -9,12 +9,18 @@ namespace Svelto.ECS | |||
/// that can be burstifiable | |||
/// </summary> | |||
/// <typeparam name="T1"></typeparam> | |||
public readonly struct AllGroupsEnumerable<T1> where T1 : struct, IBaseEntityComponent | |||
public readonly ref struct AllGroupsEnumerable<T1> where T1 : struct, _IInternalEntityComponent | |||
{ | |||
public ref struct GroupCollection | |||
public readonly ref struct GroupCollection | |||
{ | |||
internal EntityCollection<T1> collection; | |||
internal ExclusiveGroupStruct group; | |||
readonly EntityCollection<T1> collection; | |||
readonly ExclusiveGroupStruct group; | |||
public GroupCollection(EntityCollection<T1> entityCollection, ExclusiveGroupStruct groupKey) | |||
{ | |||
collection = entityCollection; | |||
group = groupKey; | |||
} | |||
public void Deconstruct(out EntityCollection<T1> collection, out ExclusiveGroupStruct group) | |||
{ | |||
@@ -45,9 +51,11 @@ namespace Svelto.ECS | |||
if (typeSafeDictionary.count == 0) | |||
continue; | |||
_array.collection = new EntityCollection<T1>(typeSafeDictionary.GetValues(out var count), | |||
typeSafeDictionary.entityIDs, count); | |||
_array.@group = group.key; | |||
_array = new GroupCollection( | |||
new EntityCollection<T1>( | |||
typeSafeDictionary.GetValues(out var count), | |||
typeSafeDictionary.entityIDs, count), group.key); | |||
return true; | |||
} | |||
@@ -4,12 +4,13 @@ using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Native; | |||
using Svelto.ECS.Hybrid; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
namespace Native | |||
{ | |||
public struct EGIDMultiMapper<T> where T : unmanaged, IBaseEntityComponent | |||
public struct EGIDMultiMapper<T> where T : unmanaged, _IInternalEntityComponent | |||
{ | |||
public EGIDMultiMapper | |||
(SveltoDictionary<ExclusiveGroupStruct, | |||
@@ -9,169 +9,179 @@ namespace Svelto.ECS | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1>(in this EntityCollection<T1> ec, out NB<T1> buffer, out int count) | |||
where T1 : unmanaged, IEntityComponent | |||
where T1 : unmanaged, IEntityComponent | |||
{ | |||
if (ec._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer = default; | |||
count = 0; | |||
count = 0; | |||
return; | |||
} | |||
buffer = (NB<T1>)ec._buffer; | |||
count = (int)ec.count; | |||
count = (int)ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1>(in this EntityCollection<T1> ec, out NB<T1> buffer, | |||
out NativeEntityIDs entityIDs, out int count) where T1 : unmanaged, IEntityComponent | |||
out NativeEntityIDs entityIDs, out int count) | |||
where T1 : unmanaged, IEntityComponent | |||
{ | |||
if (ec._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer = default; | |||
count = 0; | |||
buffer = default; | |||
count = 0; | |||
entityIDs = default; | |||
return; | |||
} | |||
buffer = (NB<T1>)ec._buffer; | |||
count = (int)ec.count; | |||
buffer = (NB<T1>)ec._buffer; | |||
count = (int)ec.count; | |||
entityIDs = (NativeEntityIDs)ec._entityIDs; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2>(in this EntityCollection<T1, T2> ec, out NB<T1> buffer1, | |||
out NB<T2> buffer2, out NativeEntityIDs entityIDs, out int count) where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
out NB<T2> buffer2, out NativeEntityIDs entityIDs, out int count) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
{ | |||
if (ec.buffer1._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
if (ec.buffer1._buffer | |||
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer1 = default; | |||
buffer2 = default; | |||
count = 0; | |||
buffer1 = default; | |||
buffer2 = default; | |||
count = 0; | |||
entityIDs = default; | |||
return; | |||
} | |||
buffer1 = (NB<T1>)ec.buffer1._buffer; | |||
buffer2 = (NB<T2>)ec.buffer2._buffer; | |||
count = ec.count; | |||
buffer1 = (NB<T1>)ec.buffer1._buffer; | |||
buffer2 = (NB<T2>)ec.buffer2._buffer; | |||
count = ec.count; | |||
entityIDs = (NativeEntityIDs)ec.buffer1._entityIDs; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2>(in this EntityCollection<T1, T2> ec, out NB<T1> buffer1, | |||
out NB<T2> buffer2, out int count) where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
out NB<T2> buffer2, out int count) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
{ | |||
if (ec.buffer1._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
if (ec.buffer1._buffer | |||
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer1 = default; | |||
buffer2 = default; | |||
count = 0; | |||
buffer1 = default; | |||
buffer2 = default; | |||
count = 0; | |||
return; | |||
} | |||
buffer1 = (NB<T1>)ec.buffer1._buffer; | |||
buffer2 = (NB<T2>)ec.buffer2._buffer; | |||
count = (int)ec.count; | |||
count = (int)ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2, T3>(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, | |||
out NB<T2> buffer2, out NB<T3> buffer3, out int count) where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
out NB<T2> buffer2, out NB<T3> buffer3, out int count) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
{ | |||
if (ec.buffer1._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
if (ec.buffer1._buffer | |||
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer1 = default; | |||
buffer2 = default; | |||
buffer3 = default; | |||
count = 0; | |||
count = 0; | |||
return; | |||
} | |||
buffer1 = (NB<T1>)ec.buffer1._buffer; | |||
buffer2 = (NB<T2>)ec.buffer2._buffer; | |||
buffer3 = (NB<T3>)ec.buffer3._buffer; | |||
count = (int)ec.count; | |||
count = (int)ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2, T3>(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, | |||
out NB<T2> buffer2, out NB<T3> buffer3, out NativeEntityIDs entityIDs, out int count) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
{ | |||
if (ec.buffer1._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
if (ec.buffer1._buffer | |||
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer1 = default; | |||
buffer2 = default; | |||
buffer3 = default; | |||
count = 0; | |||
buffer1 = default; | |||
buffer2 = default; | |||
buffer3 = default; | |||
count = 0; | |||
entityIDs = default; | |||
return; | |||
} | |||
buffer1 = (NB<T1>)ec.buffer1._buffer; | |||
buffer2 = (NB<T2>)ec.buffer2._buffer; | |||
buffer3 = (NB<T3>)ec.buffer3._buffer; | |||
count = (int)ec.count; | |||
buffer1 = (NB<T1>)ec.buffer1._buffer; | |||
buffer2 = (NB<T2>)ec.buffer2._buffer; | |||
buffer3 = (NB<T3>)ec.buffer3._buffer; | |||
count = (int)ec.count; | |||
entityIDs = (NativeEntityIDs)ec.buffer1._entityIDs; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2, T3, T4>(in this EntityCollection<T1, T2, T3, T4> ec, out NB<T1> buffer1, | |||
out NB<T2> buffer2, out NB<T3> buffer3, out NB<T4> buffer4, out int count) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
where T4 : unmanaged, IEntityComponent | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
where T4 : unmanaged, IEntityComponent | |||
{ | |||
if (ec.buffer1._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
if (ec.buffer1._buffer | |||
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer1 = default; | |||
buffer2 = default; | |||
buffer3 = default; | |||
buffer4 = default; | |||
count = 0; | |||
count = 0; | |||
return; | |||
} | |||
buffer1 = (NB<T1>)ec.buffer1._buffer; | |||
buffer2 = (NB<T2>)ec.buffer2._buffer; | |||
buffer3 = (NB<T3>)ec.buffer3._buffer; | |||
buffer4 = (NB<T4>)ec.buffer4._buffer; | |||
count = (int)ec.count; | |||
count = (int)ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2, T3, T4>(in this EntityCollection<T1, T2, T3, T4> ec, out NB<T1> buffer1, | |||
out NB<T2> buffer2, out NB<T3> buffer3, out NB<T4> buffer4, out NativeEntityIDs entityIDs, out int count) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
where T4 : unmanaged, IEntityComponent | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
where T4 : unmanaged, IEntityComponent | |||
{ | |||
if (ec.buffer1._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
if (ec.buffer1._buffer | |||
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer1 = default; | |||
buffer2 = default; | |||
buffer3 = default; | |||
buffer4 = default; | |||
count = 0; | |||
buffer1 = default; | |||
buffer2 = default; | |||
buffer3 = default; | |||
buffer4 = default; | |||
count = 0; | |||
entityIDs = default; | |||
return; | |||
} | |||
buffer1 = (NB<T1>)ec.buffer1._buffer; | |||
buffer2 = (NB<T2>)ec.buffer2._buffer; | |||
buffer3 = (NB<T3>)ec.buffer3._buffer; | |||
buffer4 = (NB<T4>)ec.buffer4._buffer; | |||
buffer1 = (NB<T1>)ec.buffer1._buffer; | |||
buffer2 = (NB<T2>)ec.buffer2._buffer; | |||
buffer3 = (NB<T3>)ec.buffer3._buffer; | |||
buffer4 = (NB<T4>)ec.buffer4._buffer; | |||
entityIDs = (NativeEntityIDs)ec.buffer1._entityIDs; | |||
count = (int)ec.count; | |||
count = (int)ec.count; | |||
} | |||
} | |||
@@ -179,115 +189,124 @@ namespace Svelto.ECS | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1>(in this EntityCollection<T1> ec, out MB<T1> buffer, out int count) | |||
where T1 : struct, IEntityViewComponent | |||
where T1 : struct, IEntityViewComponent | |||
{ | |||
if (ec._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer = default; | |||
count = 0; | |||
count = 0; | |||
return; | |||
} | |||
buffer = (MB<T1>)ec._buffer; | |||
count = (int)ec.count; | |||
count = (int)ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1>(in this EntityCollection<T1> ec, out MB<T1> buffer, | |||
out ManagedEntityIDs entityIDs, out int count) where T1 : struct, IEntityViewComponent | |||
out ManagedEntityIDs entityIDs, out int count) | |||
where T1 : struct, IEntityViewComponent | |||
{ | |||
if (ec._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer = default; | |||
count = 0; | |||
buffer = default; | |||
count = 0; | |||
entityIDs = default; | |||
return; | |||
} | |||
buffer = (MB<T1>)ec._buffer; | |||
count = (int)ec.count; | |||
buffer = (MB<T1>)ec._buffer; | |||
count = (int)ec.count; | |||
entityIDs = (ManagedEntityIDs)ec._entityIDs; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2>(in this EntityCollection<T1, T2> ec, out MB<T1> buffer1, | |||
out MB<T2> buffer2, out int count) where T1 : struct, IEntityViewComponent | |||
where T2 : struct, IEntityViewComponent | |||
out MB<T2> buffer2, out int count) | |||
where T1 : struct, IEntityViewComponent | |||
where T2 : struct, IEntityViewComponent | |||
{ | |||
if (ec.buffer1._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
if (ec.buffer1._buffer | |||
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer1 = default; | |||
buffer2 = default; | |||
count = 0; | |||
count = 0; | |||
return; | |||
} | |||
buffer1 = (MB<T1>)ec.buffer1._buffer; | |||
buffer2 = (MB<T2>)ec.buffer2._buffer; | |||
count = (int)ec.count; | |||
count = (int)ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2>(in this EntityCollection<T1, T2> ec, out MB<T1> buffer1, | |||
out MB<T2> buffer2, out ManagedEntityIDs entityIDs, out int count) where T1 : struct, IEntityViewComponent | |||
where T2 : struct, IEntityViewComponent | |||
out MB<T2> buffer2, out ManagedEntityIDs entityIDs, out int count) | |||
where T1 : struct, IEntityViewComponent | |||
where T2 : struct, IEntityViewComponent | |||
{ | |||
if (ec.buffer1._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
if (ec.buffer1._buffer | |||
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer1 = default; | |||
buffer2 = default; | |||
count = 0; | |||
buffer1 = default; | |||
buffer2 = default; | |||
count = 0; | |||
entityIDs = default; | |||
return; | |||
} | |||
buffer1 = (MB<T1>)ec.buffer1._buffer; | |||
buffer2 = (MB<T2>)ec.buffer2._buffer; | |||
count = (int)ec.count; | |||
buffer1 = (MB<T1>)ec.buffer1._buffer; | |||
buffer2 = (MB<T2>)ec.buffer2._buffer; | |||
count = (int)ec.count; | |||
entityIDs = (ManagedEntityIDs)ec.buffer1._entityIDs; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2, T3>(in this EntityCollection<T1, T2, T3> ec, out MB<T1> buffer1, | |||
out MB<T2> buffer2, out MB<T3> buffer3, out int count) where T1 : struct, IEntityViewComponent | |||
where T2 : struct, IEntityViewComponent | |||
where T3 : struct, IEntityViewComponent | |||
out MB<T2> buffer2, out MB<T3> buffer3, out int count) | |||
where T1 : struct, IEntityViewComponent | |||
where T2 : struct, IEntityViewComponent | |||
where T3 : struct, IEntityViewComponent | |||
{ | |||
if (ec.buffer1._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
if (ec.buffer1._buffer | |||
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer1 = default; | |||
buffer2 = default; | |||
buffer3 = default; | |||
count = 0; | |||
count = 0; | |||
return; | |||
} | |||
buffer1 = (MB<T1>)ec.buffer1._buffer; | |||
buffer2 = (MB<T2>)ec.buffer2._buffer; | |||
buffer3 = (MB<T3>)ec.buffer3._buffer; | |||
count = (int)ec.count; | |||
count = (int)ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2, T3>(in this EntityCollection<T1, T2, T3> ec, out MB<T1> buffer1, | |||
out MB<T2> buffer2, out MB<T3> buffer3, out ManagedEntityIDs entityIDs, out int count) | |||
where T1 : struct, IEntityViewComponent | |||
where T2 : struct, IEntityViewComponent | |||
where T3 : struct, IEntityViewComponent | |||
where T1 : struct, IEntityViewComponent | |||
where T2 : struct, IEntityViewComponent | |||
where T3 : struct, IEntityViewComponent | |||
{ | |||
if (ec.buffer1._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
if (ec.buffer1._buffer | |||
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer1 = default; | |||
buffer2 = default; | |||
buffer3 = default; | |||
buffer1 = default; | |||
buffer2 = default; | |||
buffer3 = default; | |||
entityIDs = default; | |||
count = 0; | |||
count = 0; | |||
return; | |||
} | |||
buffer1 = (MB<T1>)ec.buffer1._buffer; | |||
buffer2 = (MB<T2>)ec.buffer2._buffer; | |||
buffer3 = (MB<T3>)ec.buffer3._buffer; | |||
count = (int)ec.count; | |||
buffer1 = (MB<T1>)ec.buffer1._buffer; | |||
buffer2 = (MB<T2>)ec.buffer2._buffer; | |||
buffer3 = (MB<T3>)ec.buffer3._buffer; | |||
count = (int)ec.count; | |||
entityIDs = (ManagedEntityIDs)ec.buffer1._entityIDs; | |||
} | |||
} | |||
@@ -296,66 +315,92 @@ namespace Svelto.ECS | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2>(in this EntityCollection<T1, T2> ec, out NB<T1> buffer1, | |||
out MB<T2> buffer2, out int count) where T1 : unmanaged, IEntityComponent | |||
where T2 : struct, IEntityViewComponent | |||
out MB<T2> buffer2, out ManagedEntityIDs entityIDs, out int count) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : struct, IEntityViewComponent | |||
{ | |||
if (ec.buffer1._buffer | |||
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer1 = default; | |||
buffer2 = default; | |||
count = 0; | |||
return; | |||
} | |||
buffer1 = (NB<T1>)ec.buffer1._buffer; | |||
buffer2 = (MB<T2>)ec.buffer2._buffer; | |||
count = (int)ec.count; | |||
entityIDs = (ManagedEntityIDs)ec.buffer2._entityIDs; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2>(in this EntityCollection<T1, T2> ec, out NB<T1> buffer1, | |||
out MB<T2> buffer2, out int count) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : struct, IEntityViewComponent | |||
{ | |||
if (ec.buffer1._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
if (ec.buffer1._buffer | |||
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer1 = default; | |||
buffer2 = default; | |||
count = 0; | |||
count = 0; | |||
return; | |||
} | |||
buffer1 = (NB<T1>)ec.buffer1._buffer; | |||
buffer2 = (MB<T2>)ec.buffer2._buffer; | |||
count = (int)ec.count; | |||
count = (int)ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2, T3>(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, | |||
out MB<T2> buffer2, out MB<T3> buffer3, out int count) where T1 : unmanaged, IEntityComponent | |||
where T2 : struct, IEntityViewComponent | |||
where T3 : struct, IEntityViewComponent | |||
out MB<T2> buffer2, out MB<T3> buffer3, out int count) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : struct, IEntityViewComponent | |||
where T3 : struct, IEntityViewComponent | |||
{ | |||
if (ec.buffer1._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
if (ec.buffer1._buffer | |||
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer1 = default; | |||
buffer2 = default; | |||
buffer3 = default; | |||
count = 0; | |||
count = 0; | |||
return; | |||
} | |||
buffer1 = (NB<T1>)ec.buffer1._buffer; | |||
buffer2 = (MB<T2>)ec.buffer2._buffer; | |||
buffer3 = (MB<T3>)ec.buffer3._buffer; | |||
count = (int)ec.count; | |||
count = (int)ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2, T3, T4>(in this EntityCollection<T1, T2, T3, T4> ec, out NB<T1> buffer1, | |||
out NB<T2> buffer2, out NB<T3> buffer3, out MB<T4> buffer4, out int count) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
where T4 : struct, IEntityViewComponent | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : unmanaged, IEntityComponent | |||
where T4 : struct, IEntityViewComponent | |||
{ | |||
if (ec.buffer1._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
if (ec.buffer1._buffer | |||
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer1 = default; | |||
buffer2 = default; | |||
buffer3 = default; | |||
buffer4 = default; | |||
count = 0; | |||
count = 0; | |||
return; | |||
} | |||
buffer1 = (NB<T1>)ec.buffer1._buffer; | |||
buffer2 = (NB<T2>)ec.buffer2._buffer; | |||
buffer3 = (NB<T3>)ec.buffer3._buffer; | |||
buffer4 = (MB<T4>)ec.buffer4._buffer; | |||
count = (int)ec.count; | |||
count = (int)ec.count; | |||
} | |||
} | |||
@@ -363,48 +408,51 @@ namespace Svelto.ECS | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2, T3>(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, | |||
out NB<T2> buffer2, out MB<T3> buffer3, out int count) where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : struct, IEntityViewComponent | |||
out NB<T2> buffer2, out MB<T3> buffer3, out int count) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : struct, IEntityViewComponent | |||
{ | |||
if (ec.buffer1._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
if (ec.buffer1._buffer | |||
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer1 = default; | |||
buffer2 = default; | |||
buffer3 = default; | |||
count = 0; | |||
count = 0; | |||
return; | |||
} | |||
buffer1 = (NB<T1>)ec.buffer1._buffer; | |||
buffer2 = (NB<T2>)ec.buffer2._buffer; | |||
buffer3 = (MB<T3>)ec.buffer3._buffer; | |||
count = (int)ec.count; | |||
count = (int)ec.count; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void Deconstruct<T1, T2, T3, T4>(in this EntityCollection<T1, T2, T3, T4> ec, out NB<T1> buffer1, | |||
out NB<T2> buffer2, out MB<T3> buffer3, out MB<T4> buffer4, out int count) | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : struct, IEntityViewComponent | |||
where T4 : struct, IEntityViewComponent | |||
where T1 : unmanaged, IEntityComponent | |||
where T2 : unmanaged, IEntityComponent | |||
where T3 : struct, IEntityViewComponent | |||
where T4 : struct, IEntityViewComponent | |||
{ | |||
if (ec.buffer1._buffer == null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
if (ec.buffer1._buffer | |||
== null) //I cannot test 0, as buffer can be valid and processed by removeEx with count 0 | |||
{ | |||
buffer1 = default; | |||
buffer2 = default; | |||
buffer3 = default; | |||
buffer4 = default; | |||
count = 0; | |||
count = 0; | |||
return; | |||
} | |||
buffer1 = (NB<T1>)ec.buffer1._buffer; | |||
buffer2 = (NB<T2>)ec.buffer2._buffer; | |||
buffer3 = (MB<T3>)ec.buffer3._buffer; | |||
buffer4 = (MB<T4>)ec.buffer4._buffer; | |||
count = (int)ec.count; | |||
count = (int)ec.count; | |||
} | |||
} | |||
} |
@@ -73,6 +73,9 @@ namespace Svelto.ECS | |||
return ref entitiesDb.QueryEntity<T>(new EGID(id, group)); | |||
} | |||
/// <summary> | |||
/// Expects that only one entity of type T exists in the group | |||
/// </summary> | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static ref T QueryUniqueEntity<T> | |||
(this EntitiesDB entitiesDb, ExclusiveGroupStruct group) where T : struct, IEntityViewComponent | |||
@@ -119,7 +122,7 @@ namespace Svelto.ECS | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static AllGroupsEnumerable<T1> QueryEntities<T1>(this EntitiesDB db) | |||
where T1 :struct, IBaseEntityComponent | |||
where T1 :struct, IEntityViewComponent | |||
{ | |||
return new AllGroupsEnumerable<T1>(db); | |||
} | |||
@@ -3,6 +3,7 @@ using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
//TODO there is an overlap between these methods and Group Compound Includes | |||
public static class ExclusiveGroupExtensions | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -1,20 +1,20 @@ | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// NOTE THESE ENUMERABLES EXIST TO AVOID BOILERPLATE CODE AS THEY SKIP 0 SIZED GROUPS | |||
/// However if the normal pattern with the double foreach is used, this is not necessary | |||
/// Note: atm cannot be ref structs because they are returned in a valuetuple | |||
/// </summary> | |||
/// <typeparam name="T1"></typeparam> | |||
/// <typeparam name="T2"></typeparam> | |||
/// <typeparam name="T3"></typeparam> | |||
/// <typeparam name="T4"></typeparam> | |||
public readonly ref struct GroupsEnumerable<T1, T2, T3, T4> where T1 : struct, IBaseEntityComponent | |||
where T2 : struct, IBaseEntityComponent | |||
where T3 : struct, IBaseEntityComponent | |||
where T4 : struct, IBaseEntityComponent | |||
public readonly ref struct GroupsEnumerable<T1, T2, T3, T4> where T1 : struct, _IInternalEntityComponent | |||
where T2 : struct, _IInternalEntityComponent | |||
where T3 : struct, _IInternalEntityComponent | |||
where T4 : struct, _IInternalEntityComponent | |||
{ | |||
public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
{ | |||
@@ -92,9 +92,9 @@ namespace Svelto.ECS | |||
} | |||
} | |||
public readonly ref struct GroupsEnumerable<T1, T2, T3> where T1 : struct, IBaseEntityComponent | |||
where T2 : struct, IBaseEntityComponent | |||
where T3 : struct, IBaseEntityComponent | |||
public readonly ref struct GroupsEnumerable<T1, T2, T3> where T1 : struct, _IInternalEntityComponent | |||
where T2 : struct, _IInternalEntityComponent | |||
where T3 : struct, _IInternalEntityComponent | |||
{ | |||
public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
{ | |||
@@ -175,7 +175,7 @@ namespace Svelto.ECS | |||
} | |||
public readonly ref struct GroupsEnumerable<T1, T2> | |||
where T1 : struct, IBaseEntityComponent where T2 : struct, IBaseEntityComponent | |||
where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent | |||
{ | |||
public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
{ | |||
@@ -253,7 +253,7 @@ namespace Svelto.ECS | |||
} | |||
} | |||
public readonly ref struct GroupsEnumerable<T1> where T1 : struct, IBaseEntityComponent | |||
public readonly ref struct GroupsEnumerable<T1> where T1 : struct, _IInternalEntityComponent | |||
{ | |||
public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) | |||
{ | |||
@@ -1,4 +1,5 @@ | |||
using Svelto.DataStructures; | |||
#if SVELTO_LEGACY_FILTERS | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Native; | |||
namespace Svelto.ECS | |||
@@ -13,4 +14,5 @@ namespace Svelto.ECS | |||
return filter.Add(egid.entityID, mapper); | |||
} | |||
} | |||
} | |||
} | |||
#endif |
@@ -1,4 +1,5 @@ | |||
using Svelto.ECS.Native; | |||
#if SVELTO_LEGACY_FILTERS | |||
using Svelto.ECS.Native; | |||
namespace Svelto.ECS | |||
{ | |||
@@ -16,4 +17,5 @@ namespace Svelto.ECS | |||
} | |||
} | |||
} | |||
} | |||
#endif |
@@ -3,6 +3,7 @@ using System; | |||
using Svelto.ECS.SveltoOnDOTS; | |||
using Unity.Jobs; | |||
//note can't change namespace, too late for old projects | |||
namespace Svelto.ECS | |||
{ | |||
public static class UnityJobExtensions | |||
@@ -44,7 +45,7 @@ namespace Svelto.ECS | |||
<JOB>(this JOB job, int iterations, JobHandle inputDeps, JobHandle combinedDeps) where JOB: struct, IJobParallelFor | |||
{ | |||
if (iterations == 0) | |||
return inputDeps; | |||
return combinedDeps; | |||
var innerloopBatchCount = ProcessorCount.BatchSize((uint)iterations); | |||
var jobDeps = job.Schedule(iterations, innerloopBatchCount, inputDeps); | |||
@@ -62,6 +63,9 @@ namespace Svelto.ECS | |||
public static JobHandle ScheduleAndCombine | |||
<JOB>(this JOB job, int arrayLength, JobHandle inputDeps, JobHandle combinedDeps) where JOB : struct, IJobFor | |||
{ | |||
if (arrayLength == 0) | |||
return combinedDeps; | |||
var jobDeps = job.Schedule(arrayLength, inputDeps); | |||
return JobHandle.CombineDependencies(combinedDeps, jobDeps); | |||
} | |||
@@ -0,0 +1,257 @@ | |||
#if UNITY_ECS | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Native; | |||
using Svelto.ECS.Internal; | |||
using Unity.Burst; | |||
using Unity.Collections; | |||
using Unity.Collections.LowLevel.Unsafe; | |||
using Unity.Entities; | |||
using Unity.Jobs; | |||
namespace Svelto.ECS.SveltoOnDOTS | |||
{ | |||
public readonly struct DOTSOperationsForSvelto | |||
{ | |||
internal unsafe DOTSOperationsForSvelto(EntityManager manager, JobHandle* jobHandle) | |||
{ | |||
_EManager = manager; | |||
_jobHandle = jobHandle; | |||
} | |||
public EntityArchetype CreateArchetype(params ComponentType[] types) | |||
{ | |||
return _EManager.CreateArchetype(types); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SetComponent<T>(Entity e, in T component) | |||
where T : unmanaged, IComponentData | |||
{ | |||
_EManager.SetComponentData(e, component); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SetSharedComponent<T>(Entity e, in T component) | |||
where T : unmanaged, ISharedComponentData | |||
{ | |||
_EManager.SetSharedComponent(e, component); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal Entity CreateDOTSEntityFromSvelto(Entity prefabEntity, ExclusiveGroupStruct groupID, EntityReference reference) | |||
{ | |||
Entity dotsEntity = _EManager.Instantiate(prefabEntity); | |||
//SharedComponentData can be used to group the DOTS ECS entities exactly like the Svelto ones | |||
_EManager.AddSharedComponent(dotsEntity, new DOTSSveltoGroupID(groupID)); | |||
_EManager.AddComponent<DOTSSveltoReference>(dotsEntity); | |||
_EManager.SetComponentData(dotsEntity, new DOTSSveltoReference(reference)); | |||
return dotsEntity; | |||
} | |||
/// <summary> | |||
/// This method assumes that the Svelto entity with EGID egid has also dotsEntityComponent | |||
/// among the descriptors | |||
/// </summary> | |||
/// <param name="archetype"></param> | |||
/// <param name="egid"></param> | |||
/// <returns></returns> | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal Entity CreateDOTSEntityFromSvelto(EntityArchetype archetype, ExclusiveGroupStruct groupID, EntityReference reference) | |||
{ | |||
Entity dotsEntity = _EManager.CreateEntity(archetype); | |||
//SharedComponentData can be used to group the DOTS ECS entities exactly like the Svelto ones | |||
_EManager.AddSharedComponent(dotsEntity, new DOTSSveltoGroupID(groupID)); | |||
_EManager.AddComponent<DOTSSveltoReference>(dotsEntity); | |||
_EManager.SetComponentData(dotsEntity, new DOTSSveltoReference(reference)); | |||
return dotsEntity; | |||
} | |||
/// <summary> | |||
/// in this case the user decided to create a DOTS entity that is self managed and not managed | |||
/// by the framework | |||
/// </summary> | |||
/// <param name="archetype"></param> | |||
/// <param name="wireEgid"></param> | |||
/// <returns></returns> | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal Entity CreateDOTSEntity(EntityArchetype archetype) | |||
{ | |||
return _EManager.CreateEntity(archetype); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void DestroyEntity(Entity e) | |||
{ | |||
_EManager.DestroyEntity(e); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveComponent<T>(Entity dotsEntity) | |||
{ | |||
_EManager.RemoveComponent<T>(dotsEntity); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void AddComponent<T>(Entity dotsEntity) | |||
where T : unmanaged, IComponentData | |||
{ | |||
_EManager.AddComponent<T>(dotsEntity); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void AddComponent<T>(Entity dotsEntity, in T component) | |||
where T : unmanaged, IComponentData | |||
{ | |||
_EManager.AddComponent<T>(dotsEntity); | |||
_EManager.SetComponentData(dotsEntity, component); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void AddSharedComponent<T>(Entity dotsEntity, in T component) | |||
where T : unmanaged, ISharedComponentData | |||
{ | |||
_EManager.AddSharedComponent(dotsEntity, component); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void AddBuffer<T>(Entity dotsEntity) | |||
where T : unmanaged, IBufferElementData | |||
{ | |||
_EManager.AddBuffer<T>(dotsEntity); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SetSharedComponentBatched<SharedComponentData>(NativeArray<Entity> nativeArray, SharedComponentData SCD) | |||
where SharedComponentData : unmanaged, ISharedComponentData | |||
{ | |||
_EManager.SetSharedComponent(nativeArray, SCD); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void AddComponentBatched<T>(NativeArray<Entity> DOTSEntities) | |||
{ | |||
_EManager.AddComponent<T>(DOTSEntities); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public NativeArray<Entity> CreateDOTSEntityFromSveltoBatched(Entity prefab, (uint rangeStart, uint rangeEnd) range, | |||
ExclusiveGroupStruct groupID, NB<DOTSEntityComponent> DOSTEntityComponents) | |||
{ | |||
unsafe | |||
{ | |||
_jobHandle->Complete(); | |||
var count = (int)(range.rangeEnd - range.rangeStart); | |||
var nativeArray = _EManager.Instantiate(prefab, count, _EManager.World.UpdateAllocator.ToAllocator); | |||
_EManager.AddSharedComponent(nativeArray, new DOTSSveltoGroupID(groupID)); | |||
var setDOTSEntityComponentsJob = new SetDOTSEntityComponents | |||
{ | |||
sveltoStartIndex = range.rangeStart, | |||
createdEntities = nativeArray, | |||
DOSTEntityComponents = DOSTEntityComponents | |||
}; | |||
*_jobHandle = JobHandle.CombineDependencies(*_jobHandle, setDOTSEntityComponentsJob.ScheduleParallel(count, default)); | |||
return nativeArray; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public NativeArray<Entity> CreateDOTSEntityFromSveltoBatched(Entity prefab, (uint rangeStart, uint rangeEnd) range, | |||
ExclusiveGroupStruct groupID, NB<DOTSEntityComponent> DOSTEntityComponents, | |||
SharedSveltoDictionaryNative<uint, EntityReference> referenceMap, NativeEntityIDs sveltoIds, out JobHandle creationJob) | |||
{ | |||
var nativeArray = CreateDOTSEntityFromSveltoBatched(prefab, range, groupID, DOSTEntityComponents); | |||
unsafe | |||
{ | |||
var count = (int)(range.rangeEnd - range.rangeStart); | |||
_EManager.AddComponent<DOTSSveltoReference>(nativeArray); | |||
var SetDOTSSveltoReferenceJob = new SetDOTSSveltoReference | |||
{ | |||
sveltoStartIndex = range.rangeStart, | |||
createdEntities = nativeArray, | |||
entityManager = _EManager, | |||
ids = sveltoIds, | |||
entityReferenceMap = referenceMap, | |||
}; | |||
creationJob = *_jobHandle = JobHandle.CombineDependencies(*_jobHandle, SetDOTSSveltoReferenceJob.ScheduleParallel(count, default)); | |||
return nativeArray; | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void AddJobToComplete(JobHandle jobHandle) | |||
{ | |||
unsafe | |||
{ | |||
*_jobHandle = JobHandle.CombineDependencies(*_jobHandle, jobHandle); | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void DestroyEntitiesBatched(NativeArray<Entity> nativeArray) | |||
{ | |||
_EManager.DestroyEntity(nativeArray); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void Complete() | |||
{ | |||
unsafe | |||
{ | |||
_jobHandle->Complete(); | |||
} | |||
} | |||
[BurstCompile] | |||
struct SetDOTSEntityComponents: IJobParallelFor | |||
{ | |||
public uint sveltoStartIndex; | |||
[ReadOnly] public NativeArray<Entity> createdEntities; | |||
public NB<DOTSEntityComponent> DOSTEntityComponents; | |||
public void Execute(int currentIndex) | |||
{ | |||
int index = (int)(sveltoStartIndex + currentIndex); | |||
var dotsEntity = createdEntities[currentIndex]; | |||
DOSTEntityComponents[index].dotsEntity = dotsEntity; | |||
} | |||
} | |||
[BurstCompile] | |||
public struct SetDOTSSveltoReference: IJobParallelFor | |||
{ | |||
public uint sveltoStartIndex; | |||
[ReadOnly] public NativeArray<Entity> createdEntities; | |||
[NativeDisableParallelForRestriction] public EntityManager entityManager; | |||
public NativeEntityIDs ids; | |||
public SharedSveltoDictionaryNative<uint, EntityReference> entityReferenceMap; | |||
public void Execute(int currentIndex) | |||
{ | |||
int index = (int)(sveltoStartIndex + currentIndex); | |||
var dotsEntity = createdEntities[currentIndex]; | |||
entityManager.SetComponentData( | |||
dotsEntity, new DOTSSveltoReference | |||
{ | |||
entityReference = entityReferenceMap[ids[index]] | |||
}); | |||
} | |||
} | |||
readonly EntityManager _EManager; | |||
[NativeDisableUnsafePtrRestriction] readonly unsafe JobHandle* _jobHandle; | |||
} | |||
} | |||
#endif |
@@ -1,55 +0,0 @@ | |||
#if UNITY_ECS | |||
using Unity.Entities; | |||
namespace Svelto.ECS.SveltoOnDOTS | |||
{ | |||
/// <summary> | |||
/// DOTS component to keep track of the associated Svelto.ECS entity | |||
/// </summary> | |||
public struct DOTSSveltoEGID : IComponentData | |||
{ | |||
public EGID egid; | |||
public DOTSSveltoEGID(EGID egid) { this.egid = egid; } | |||
} | |||
/// <summary> | |||
/// DOTS component to be able to query all the DOTS entities found in a Svelto.ECS group | |||
/// </summary> | |||
public readonly struct DOTSSveltoGroupID : ISharedComponentData | |||
{ | |||
readonly ExclusiveGroupStruct group; | |||
public DOTSSveltoGroupID(ExclusiveGroupStruct exclusiveGroup) | |||
{ | |||
@group = exclusiveGroup; | |||
} | |||
public static implicit operator ExclusiveGroupStruct(DOTSSveltoGroupID group) | |||
{ | |||
return group.@group; | |||
} | |||
} | |||
struct DOTSEntityToSetup : ISharedComponentData | |||
{ | |||
internal readonly ExclusiveGroupStruct group; | |||
public DOTSEntityToSetup(ExclusiveGroupStruct exclusiveGroup) | |||
{ | |||
@group = exclusiveGroup; | |||
} | |||
} | |||
public interface IEntityComponentForDOTS: IEntityComponent | |||
{ | |||
public Entity dotsEntity { get; set; } | |||
} | |||
public struct DOTSEntityComponent:IEntityComponentForDOTS | |||
{ | |||
public Entity dotsEntity { get; set; } | |||
} | |||
} | |||
#endif |
@@ -1,215 +0,0 @@ | |||
#if UNITY_ECS | |||
//#if !UNITY_ECS_050 | |||
#define SLOW_SVELTO_ECB //Using EntityManager directly is much faster than using ECB because of the shared components | |||
//#endif | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
using Unity.Entities; | |||
namespace Svelto.ECS.SveltoOnDOTS | |||
{ | |||
public readonly struct EntityCommandBufferForSvelto | |||
{ | |||
internal EntityCommandBufferForSvelto(EntityCommandBuffer value, EntityManager manager) | |||
{ | |||
_ECB = value; | |||
_EManager = manager; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public Entity CreatePureDOTSEntity(EntityArchetype jointArchetype) | |||
{ | |||
#if SLOW_SVELTO_ECB | |||
return _EManager.CreateEntity(jointArchetype); | |||
#else | |||
return _ECB.CreateEntity(jointArchetype); | |||
#endif | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SetComponent<T>(Entity e, in T component) where T : struct, IComponentData | |||
{ | |||
#if SLOW_SVELTO_ECB | |||
_EManager.SetComponentData<T>(e, component); | |||
#else | |||
_ECB.SetComponent(e, component); | |||
#endif | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void SetSharedComponent<T>(Entity e, in T component) where T : struct, ISharedComponentData | |||
{ | |||
#if SLOW_SVELTO_ECB | |||
_EManager.SetSharedComponentData<T>(e, component); | |||
#else | |||
_ECB.SetSharedComponent(e, component); | |||
#endif | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
///Not ready for prime time with BURST yet, maybe with DOTS 1.0 | |||
public static Entity CreateDOTSEntityOnSvelto(int sortKey, EntityCommandBuffer.ParallelWriter writer, | |||
Entity entityComponentPrefabEntity, EGID egid, bool mustHandleDOTSComponent) | |||
{ | |||
#if !SLOW_SVELTO_ECB | |||
Entity dotsEntity = writer.Instantiate(sortKey, entityComponentPrefabEntity); | |||
//SharedComponentData can be used to group the DOTS ECS entities exactly like the Svelto ones | |||
writer.AddSharedComponent(sortKey, dotsEntity, new DOTSSveltoGroupID(egid.groupID)); | |||
writer.AddComponent(sortKey, dotsEntity, new DOTSSveltoEGID(egid)); | |||
if (mustHandleDOTSComponent) | |||
writer.AddSharedComponent(sortKey, dotsEntity, new DOTSEntityToSetup(egid.groupID)); | |||
return dotsEntity; | |||
#endif | |||
throw new NotSupportedException(); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal Entity CreateDOTSEntityOnSvelto(Entity entityComponentPrefabEntity, EGID egid, | |||
bool mustHandleDOTSComponent) | |||
{ | |||
#if SLOW_SVELTO_ECB | |||
Entity dotsEntity = _EManager.Instantiate(entityComponentPrefabEntity); | |||
//SharedComponentData can be used to group the DOTS ECS entities exactly like the Svelto ones | |||
_EManager.AddSharedComponentData(dotsEntity, new DOTSSveltoGroupID(egid.groupID)); | |||
_EManager.AddComponentData(dotsEntity, new DOTSSveltoEGID(egid)); | |||
if (mustHandleDOTSComponent) | |||
_EManager.AddSharedComponentData(dotsEntity, new DOTSEntityToSetup(egid.groupID)); | |||
#else | |||
Entity dotsEntity = _ECB.Instantiate(entityComponentPrefabEntity); | |||
//SharedComponentData can be used to group the DOTS ECS entities exactly like the Svelto ones | |||
_ECB.AddSharedComponent(dotsEntity, new DOTSSveltoGroupID(egid.groupID)); | |||
_ECB.AddComponent(dotsEntity, new DOTSSveltoEGID(egid)); | |||
if (mustHandleDOTSComponent) | |||
_ECB.AddSharedComponent(dotsEntity, new DOTSEntityToSetup(egid.groupID)); | |||
#endif | |||
return dotsEntity; | |||
} | |||
/// <summary> | |||
/// This method assumes that the Svelto entity with EGID egid has also dotsEntityComponent | |||
/// among the descriptors | |||
/// </summary> | |||
/// <param name="archetype"></param> | |||
/// <param name="egid"></param> | |||
/// <returns></returns> | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal Entity CreateDOTSEntityOnSvelto(EntityArchetype archetype, EGID egid, bool mustHandleDOTSComponent) | |||
{ | |||
#if SLOW_SVELTO_ECB | |||
Entity dotsEntity = _EManager.CreateEntity(archetype); | |||
//SharedComponentData can be used to group the DOTS ECS entities exactly like the Svelto ones | |||
_EManager.AddSharedComponentData(dotsEntity, new DOTSSveltoGroupID(egid.groupID)); | |||
_EManager.AddComponentData(dotsEntity, new DOTSSveltoEGID(egid)); | |||
if (mustHandleDOTSComponent) | |||
_EManager.AddSharedComponentData(dotsEntity, new DOTSEntityToSetup(egid.groupID)); | |||
#else | |||
Entity dotsEntity = _ECB.CreateEntity(archetype); | |||
//SharedComponentData can be used to group the DOTS ECS entities exactly like the Svelto ones | |||
_ECB.AddSharedComponent(dotsEntity, new DOTSSveltoGroupID(egid.groupID)); | |||
_ECB.AddComponent(dotsEntity, new DOTSSveltoEGID(egid)); | |||
if (mustHandleDOTSComponent) | |||
_ECB.AddSharedComponent(dotsEntity, new DOTSEntityToSetup(egid.groupID)); | |||
#endif | |||
return dotsEntity; | |||
} | |||
/// <summary> | |||
/// in this case the user decided to create a DOTS entity that is self managed and not managed | |||
/// by the framework | |||
/// </summary> | |||
/// <param name="archetype"></param> | |||
/// <param name="wireEgid"></param> | |||
/// <returns></returns> | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal Entity CreateDOTSEntityUnmanaged(EntityArchetype archetype) | |||
{ | |||
#if SLOW_SVELTO_ECB | |||
return _EManager.CreateEntity(archetype); | |||
#else | |||
return _ECB.CreateEntity(archetype); | |||
#endif | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void DestroyEntity(Entity e) | |||
{ | |||
#if SLOW_SVELTO_ECB | |||
_EManager.DestroyEntity(e); | |||
#else | |||
_ECB.DestroyEntity(e); | |||
#endif | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void RemoveComponent<T>(Entity dotsEntity) | |||
{ | |||
#if SLOW_SVELTO_ECB | |||
_EManager.RemoveComponent<T>(dotsEntity); | |||
#else | |||
_ECB.RemoveComponent<T>(dotsEntity); | |||
#endif | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void AddComponent<T>(Entity dotsEntity) where T : struct, IComponentData | |||
{ | |||
#if SLOW_SVELTO_ECB | |||
_EManager.AddComponent<T>(dotsEntity); | |||
#else | |||
_ECB.AddComponent<T>(dotsEntity); | |||
#endif | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void AddComponent<T>(Entity dotsEntity, in T component) where T : struct, IComponentData | |||
{ | |||
#if SLOW_SVELTO_ECB | |||
_EManager.AddComponentData(dotsEntity, component); | |||
#else | |||
_ECB.AddComponent(dotsEntity, component); | |||
#endif | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void AddSharedComponent<T>(Entity dotsEntity, in T component) where T : struct, ISharedComponentData | |||
{ | |||
#if SLOW_SVELTO_ECB | |||
_EManager.AddSharedComponentData(dotsEntity, component); | |||
#else | |||
_ECB.AddSharedComponent(dotsEntity, component); | |||
#endif | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public void AddBuffer<T>(Entity dotsEntity) where T : struct, IBufferElementData | |||
{ | |||
#if SLOW_SVELTO_ECB | |||
_EManager.AddBuffer<T>(dotsEntity); | |||
#else | |||
_ECB.AddBuffer<T>(dotsEntity); | |||
#endif | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public EntityCommandBuffer.ParallelWriter AsParallelWriter() | |||
{ | |||
#if SLOW_SVELTO_ECB | |||
throw new System.Exception(); | |||
#else | |||
return _ECB.AsParallelWriter(); | |||
#endif | |||
} | |||
readonly EntityCommandBuffer _ECB; | |||
readonly EntityManager _EManager; | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,61 @@ | |||
#if UNITY_ECS | |||
using Unity.Entities; | |||
namespace Svelto.ECS.SveltoOnDOTS | |||
{ | |||
/// <summary> | |||
/// If for some reason the user needs the DOTS entities to be grouped like the Svelto Entities, then this descriptor can be extended | |||
/// which will automatically enable the SveltoOnDOTSHandleLifeTimeEngine synchronization. | |||
/// This will also handle entities destruction. | |||
/// </summary> | |||
public class SveltoOnDotsSynchedEntityDescriptor: GenericEntityDescriptor<DOTSEntityComponent> { } | |||
public interface IEntityComponentForDOTS: IEntityComponent | |||
{ | |||
public Entity dotsEntity { get; set; } | |||
} | |||
public struct DOTSEntityComponent:IEntityComponentForDOTS | |||
{ | |||
public DOTSEntityComponent(Entity entity) | |||
{ | |||
dotsEntity = entity; | |||
} | |||
public Entity dotsEntity { get; set; } | |||
} | |||
//DOTS COMPONENTS: | |||
/// <summary> | |||
/// DOTS component to keep track of the associated Svelto.ECS entity | |||
/// </summary> | |||
public struct DOTSSveltoReference: IComponentData | |||
{ | |||
public EntityReference entityReference; | |||
public DOTSSveltoReference(EntityReference eEntityReference) | |||
{ | |||
entityReference = eEntityReference; | |||
} | |||
} | |||
/// <summary> | |||
/// DOTS component to be able to query all the DOTS entities found in a Svelto.ECS group | |||
/// </summary> | |||
public readonly struct DOTSSveltoGroupID: ISharedComponentData | |||
{ | |||
readonly ExclusiveGroupStruct group; | |||
public DOTSSveltoGroupID(ExclusiveGroupStruct exclusiveGroup) | |||
{ | |||
@group = exclusiveGroup; | |||
} | |||
public static implicit operator ExclusiveGroupStruct(DOTSSveltoGroupID group) | |||
{ | |||
return group.@group; | |||
} | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,17 @@ | |||
#if UNITY_ECS | |||
namespace Svelto.ECS.SveltoOnDOTS | |||
{ | |||
/// <summary> | |||
/// SubmissionEngine is a dedicated DOTS ECS Svelto.ECS engine that allows using the DOTS ECS | |||
/// EntityCommandBuffer for fast creation of DOTS entities | |||
/// </summary> | |||
public interface ISveltoOnDOTSStructuralEngine | |||
{ | |||
DOTSOperationsForSvelto DOTSOperations { get; set; } | |||
string name { get; } | |||
void OnPostSubmission(); | |||
} | |||
} | |||
#endif |
@@ -8,8 +8,7 @@ namespace Svelto.ECS.SveltoOnDOTS | |||
{ | |||
/// <summary> | |||
/// This is a high level class to abstract the complexity of creating a Svelto ECS application that interacts | |||
/// with DOTS ECS. However this is designed to make it work almost out of the box, but it should be eventually | |||
/// substituted by project customized code. | |||
/// with DOTS ECS. | |||
/// This is a JobifiedEngine and as such it expect to be ticked. Normally it must be executed in a | |||
/// SortedEnginesGroup as step that happens after the Svelto jobified engines run. | |||
/// | |||
@@ -20,21 +19,25 @@ namespace Svelto.ECS.SveltoOnDOTS | |||
/// Synchronizations engines to be executed (Svelto to DOTS ECS) | |||
/// Submission of Entities to be executed | |||
/// Svelto Add/Remove callbacks to be called | |||
/// ISubmissionEngines to be executed | |||
/// ISveltoOnDOTSStructuralEngine to be executed | |||
/// DOTS ECS engines to executed | |||
/// Synchronizations engines to be executed (DOTS ECS To Svelto) | |||
/// </summary> | |||
[Sequenced(nameof(JobifiedSveltoEngines.SveltoOnDOTS))] | |||
public class SveltoOnDOTSEnginesGroup : IJobifiedEngine | |||
public class SveltoOnDOTSEnginesGroup: IJobifiedEngine | |||
{ | |||
public SveltoOnDOTSEnginesGroup(EnginesRoot enginesRoot) | |||
{ | |||
DBC.ECS.Check.Require(enginesRoot.scheduler is SimpleEntitiesSubmissionScheduler | |||
, "The Engines root must use a EntitiesSubmissionScheduler scheduler implementation"); | |||
DBC.ECS.Check.Require( | |||
enginesRoot.scheduler is SimpleEntitiesSubmissionScheduler | |||
, "The Engines root must use a EntitiesSubmissionScheduler scheduler implementation"); | |||
CreateUnityECSWorldForSvelto(enginesRoot.scheduler as SimpleEntitiesSubmissionScheduler, enginesRoot); | |||
} | |||
/// <summary> | |||
/// for the user to add pure DOTS ECS SystemBase/ISystem systems to the DOTS ECS world | |||
/// </summary> | |||
public World world { get; private set; } | |||
public JobHandle Execute(JobHandle inputDeps) | |||
@@ -56,28 +59,29 @@ namespace Svelto.ECS.SveltoOnDOTS | |||
public string name => nameof(SveltoOnDOTSEnginesGroup); | |||
public void AddSveltoToDOTSEngine(SyncSveltoToDOTSEngine engine) | |||
public void AddSveltoToDOTSSyncEngine(SyncSveltoToDOTSEngine engine) | |||
{ | |||
//it's a Svelto Engine/DOTS ECS SystemBase so it must be added in the DOTS ECS world AND svelto enginesRoot | |||
world.AddSystem(engine); | |||
world.AddSystemManaged(engine); | |||
_enginesRoot.AddEngine(engine); | |||
_syncSveltoToDotsGroup.Add(engine); | |||
} | |||
public void AddDOTSToSveltoEngine(SyncDOTSToSveltoEngine engine) | |||
public void AddDOTSToSveltoSyncEngine(SyncDOTSToSveltoEngine engine) | |||
{ | |||
//it's a Svelto Engine/DOTS ECS SystemBase so it must be added in the DOTS ECS world AND svelto enginesRoot | |||
world.AddSystem(engine); | |||
world.AddSystemManaged(engine); | |||
_enginesRoot.AddEngine(engine); | |||
_syncDotsToSveltoGroup.Add(engine); | |||
} | |||
public void AddDOTSSubmissionEngine(SveltoOnDOTSHandleCreationEngine submissionEngine) | |||
public void AddSveltoOnDOTSSubmissionEngine(ISveltoOnDOTSStructuralEngine submissionEngine) | |||
{ | |||
_sveltoDotsEntitiesSubmissionGroup.Add(submissionEngine); | |||
if (submissionEngine is IEngine enginesRootEngine) | |||
_enginesRoot.AddEngine(enginesRootEngine); | |||
} | |||
@@ -96,17 +100,24 @@ namespace Svelto.ECS.SveltoOnDOTS | |||
World.DefaultGameObjectInjectionWorld = world; | |||
//This is the DOTS ECS group that takes care of all the DOTS ECS systems that creates entities | |||
//it also submits Svelto entities | |||
_sveltoDotsEntitiesSubmissionGroup = new SveltoOnDOTSEntitiesSubmissionGroup(scheduler, enginesRoot); | |||
//This is the group that handles the DOTS ECS sync systems that copy the svelto entities values to DOTS ECS entities | |||
//it also submits Svelto entities through the scheduler | |||
var defaultSveltoOnDotsHandleLifeTimeEngine = new SveltoOnDOTSHandleLifeTimeEngine<DOTSEntityComponent>(); | |||
_sveltoDotsEntitiesSubmissionGroup = new SveltoOnDOTSEntitiesSubmissionGroup(scheduler); | |||
enginesRoot.AddEngine(defaultSveltoOnDotsHandleLifeTimeEngine); | |||
_sveltoDotsEntitiesSubmissionGroup.Add(defaultSveltoOnDotsHandleLifeTimeEngine); | |||
enginesRoot.AddEngine(_sveltoDotsEntitiesSubmissionGroup); | |||
world.AddSystem(_sveltoDotsEntitiesSubmissionGroup); | |||
world.AddSystemManaged(_sveltoDotsEntitiesSubmissionGroup); | |||
//This is the group that handles the DOTS ECS sync systems that copy the svelto entities values to DOTS ECS entities | |||
_syncSveltoToDotsGroup = new SyncSveltoToDOTSGroup(); | |||
enginesRoot.AddEngine(_syncSveltoToDotsGroup); | |||
//This is the group that handles the DOTS ECS sync systems that copy the DOTS ECS entities values to svelto entities | |||
_syncDotsToSveltoGroup = new SyncDOTSToSveltoGroup(); | |||
enginesRoot.AddEngine(_syncDotsToSveltoGroup); | |||
//This is the group that handles the DOTS ECS sync systems that copy the DOTS ECS entities values to svelto entities | |||
//enginesRoot.AddEngine(new SveltoDOTS ECSEntitiesSubmissionGroup(scheduler, world)); | |||
enginesRoot.AddEngine(this); | |||
_enginesRoot = enginesRoot; | |||
@@ -115,8 +126,8 @@ namespace Svelto.ECS.SveltoOnDOTS | |||
EnginesRoot _enginesRoot; | |||
SveltoOnDOTSEntitiesSubmissionGroup _sveltoDotsEntitiesSubmissionGroup; | |||
SyncSveltoToDOTSGroup _syncSveltoToDotsGroup; | |||
SyncDOTSToSveltoGroup _syncDotsToSveltoGroup; | |||
SyncSveltoToDOTSGroup _syncSveltoToDotsGroup; | |||
SyncDOTSToSveltoGroup _syncDotsToSveltoGroup; | |||
} | |||
} | |||
#endif |
@@ -1,196 +1,104 @@ | |||
#if UNITY_ECS | |||
#if !UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP_RUNTIME_WORLD | |||
#error SveltoOnDOTS required the user to take over the DOTS world control and explicitly create it. UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP must be defined | |||
#endif | |||
using System; | |||
using System.Collections.Generic; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Native; | |||
using Svelto.ECS.Schedulers; | |||
using Unity.Collections.LowLevel.Unsafe; | |||
using Unity.Entities; | |||
using Unity.Jobs; | |||
using Allocator = Unity.Collections.Allocator; | |||
namespace Svelto.ECS.SveltoOnDOTS | |||
{ | |||
/// <summary> | |||
/// SveltoDOTS ECSEntitiesSubmissionGroup expand the _submissionScheduler responsibility to integrate the | |||
/// submission of Svelto entities with the submission of DOTS ECS entities using EntityCommandBuffer. | |||
/// SveltoDOTS ECSEntitiesSubmissionGroup extends the _submissionScheduler responsibility to integrate the | |||
/// submission of Svelto entities with the submission of DOTS ECS entities using DOTSOperationsForSvelto. | |||
/// As there is just one submissionScheduler per enginesRoot, there should be only one SveltoDOTS | |||
/// ECSEntitiesSubmissionGroup | |||
/// per engines group. It's expected use is showed in the class SveltoOnDOTS ECSEnginesGroup which should be used | |||
/// instead of using this class directly. | |||
/// Groups DOTS ECS/Svelto SystemBase engines that creates DOTS ECS entities. | |||
/// ECSEntitiesSubmissionGroup. | |||
/// initialise DOTS ECS/Svelto systems/engines that handles DOTS ECS entities structural changes. | |||
/// Flow: | |||
/// Complete all the jobs used as input dependencies (this is a sync point) | |||
/// Create the new frame Command Buffer to use | |||
/// Svelto entities are submitted | |||
/// Svelto Add and remove callback are called | |||
/// ECB is injected in all the registered engines | |||
/// all the OnUpdate of the registered engines/systems are called | |||
/// the DOTS ECS command buffer is flushed | |||
/// all the DOTS ECS entities created that need Svelto information will be processed | |||
/// ISveltoOnDOTSStructuralEngine can use DOTSOperationsForSvelto in their add/remove/moove callbacks | |||
/// </summary> | |||
[DisableAutoCreation] | |||
public sealed partial class SveltoOnDOTSEntitiesSubmissionGroup : SystemBase, IQueryingEntitiesEngine, | |||
ISveltoOnDOTSSubmission | |||
public sealed partial class SveltoOnDOTSEntitiesSubmissionGroup: SystemBase, IQueryingEntitiesEngine, ISveltoOnDOTSSubmission | |||
{ | |||
public SveltoOnDOTSEntitiesSubmissionGroup(SimpleEntitiesSubmissionScheduler submissionScheduler, | |||
EnginesRoot enginesRoot) | |||
public SveltoOnDOTSEntitiesSubmissionGroup(SimpleEntitiesSubmissionScheduler submissionScheduler) | |||
{ | |||
_submissionScheduler = submissionScheduler; | |||
_submissionEngines = new FasterList<SveltoOnDOTSHandleCreationEngine>(); | |||
_cachedList = new List<DOTSEntityToSetup>(); | |||
_sveltoOnDotsHandleLifeTimeEngines = new FasterList<ISveltoOnDOTSHandleLifeTimeEngine>(); | |||
var defaultSveltoOnDotsHandleLifeTimeEngine = new SveltoOnDOTSHandleLifeTimeEngine<DOTSEntityComponent>(); | |||
enginesRoot.AddEngine(defaultSveltoOnDotsHandleLifeTimeEngine); | |||
_sveltoOnDotsHandleLifeTimeEngines.Add(defaultSveltoOnDotsHandleLifeTimeEngine); | |||
_submissionScheduler = submissionScheduler; | |||
_submissionEngines = new FasterList<ISveltoOnDOTSStructuralEngine>(); | |||
} | |||
public EntitiesDB entitiesDB { get; set; } | |||
public void Ready() { } | |||
//Right, when you record a command outside of a job using the regular ECB, you don't pass it a sort key. | |||
//We instead use a constant for the main thread that is actually set to Int32.MaxValue. Where as the commands | |||
//that are recording from jobs with the ParallelWriter, get a lower value sort key from the job. Because we | |||
//playback the commands in order based on this sort key, the ParallelWriter commands end up happening before | |||
//the main thread commands. This is where your error is coming from because the Instantiate command happens at | |||
//the end because it's sort key is Int32.MaxValue. | |||
//We don't recommend mixing the main thread and ParallelWriter commands in a single ECB for this reason. | |||
public void SubmitEntities(JobHandle jobHandle) | |||
{ | |||
if (_submissionScheduler.paused == true) | |||
if (_submissionScheduler.paused == true || World.EntityManager == default) | |||
return; | |||
using (var profiler = new PlatformProfiler("SveltoDOTSEntitiesSubmissionGroup")) | |||
{ | |||
using (profiler.Sample("PreSubmissionPhase")) | |||
using (profiler.Sample("Complete All Pending Jobs")) | |||
{ | |||
PreSubmissionPhase(ref jobHandle, profiler); | |||
jobHandle.Complete(); //sync-point | |||
EntityManager.CompleteAllTrackedJobs(); | |||
} | |||
//Submit Svelto Entities, calls Add/Remove/MoveTo that can be used by the IDOTS ECSSubmissionEngines | |||
//Submit Svelto Entities, calls Add/Remove/MoveTo that can be used by the DOTS ECSSubmissionEngines | |||
_submissionScheduler.SubmitEntities(); | |||
using (profiler.Sample("AfterSubmissionPhase")) | |||
{ | |||
AfterSubmissionPhase(profiler); | |||
} | |||
foreach (var engine in _submissionEngines) | |||
engine.OnPostSubmission(); | |||
_dotsOperationsForSvelto.Complete(); | |||
} | |||
} | |||
public void Add(SveltoOnDOTSHandleCreationEngine engine) | |||
public void Add(ISveltoOnDOTSStructuralEngine engine) | |||
{ | |||
// Console.LogDebug($"Add Submission Engine {engine} to the DOTS world {_ECBSystem.World.Name}"); | |||
//this is temporary enabled because of engines that needs EntityManagers for the wrong reasons. | |||
_submissionEngines.Add(engine); | |||
engine.entityManager = EntityManager; | |||
engine.OnCreate(); | |||
if (World != null) | |||
engine.DOTSOperations = _dotsOperationsForSvelto; | |||
} | |||
public void Add(ISveltoOnDOTSHandleLifeTimeEngine engine) | |||
{ | |||
// Console.LogDebug($"Add Submission Engine {engine} to the DOTS world {_ECBSystem.World.Name}"); | |||
_sveltoOnDotsHandleLifeTimeEngines.Add(engine); | |||
} | |||
void PreSubmissionPhase(ref JobHandle jobHandle, PlatformProfiler profiler) | |||
{ | |||
using (profiler.Sample("Complete All Pending Jobs")) jobHandle.Complete(); //sync-point | |||
_entityCommandBuffer = new EntityCommandBuffer((Allocator)Common.Allocator.TempJob); | |||
foreach (var system in _submissionEngines) | |||
system.entityCommandBuffer = | |||
new EntityCommandBufferForSvelto(_entityCommandBuffer, World.EntityManager); | |||
foreach (var system in _sveltoOnDotsHandleLifeTimeEngines) | |||
system.entityCommandBuffer = | |||
new EntityCommandBufferForSvelto(_entityCommandBuffer, World.EntityManager); | |||
} | |||
void AfterSubmissionPhase(PlatformProfiler profiler) | |||
protected override void OnCreate() | |||
{ | |||
JobHandle combinedHandle = default; | |||
for (var i = 0; i < _submissionEngines.count; i++) | |||
unsafe | |||
{ | |||
try | |||
{ | |||
combinedHandle = JobHandle.CombineDependencies(combinedHandle, _submissionEngines[i].OnUpdate()); | |||
} | |||
catch (Exception e) | |||
{ | |||
Console.LogException(e, _submissionEngines[i].name); | |||
throw; | |||
} | |||
} | |||
using (profiler.Sample("Playback Command Buffer")) | |||
{ | |||
_entityCommandBuffer.Playback(EntityManager); | |||
_entityCommandBuffer.Dispose(); | |||
_jobHandle = (JobHandle*) MemoryUtilities.NativeAlloc((uint)MemoryUtilities.SizeOf<JobHandle>(), Allocator.Persistent); | |||
_dotsOperationsForSvelto = new DOTSOperationsForSvelto(World.EntityManager, _jobHandle); | |||
//initialise engines field while world was null | |||
foreach (var engine in _submissionEngines) | |||
engine.DOTSOperations = _dotsOperationsForSvelto; | |||
} | |||
using (profiler.Sample("ConvertPendingEntities")) | |||
ConvertPendingEntities(combinedHandle); | |||
} | |||
//Note: when this is called, the CommandBuffer is flushed so the not temporary DOTS entity ID will be used | |||
void ConvertPendingEntities(JobHandle combinedHandle) | |||
protected override void OnDestroy() | |||
{ | |||
var entityCommandBuffer = new EntityCommandBuffer((Allocator)Common.Allocator.TempJob); | |||
var cmd = entityCommandBuffer.AsParallelWriter(); | |||
_cachedList.Clear(); | |||
//note with DOTS 0.17 unfortunately this allocates a lot :( | |||
EntityManager.GetAllUniqueSharedComponentData(_cachedList); | |||
Dependency = JobHandle.CombineDependencies(Dependency, combinedHandle); | |||
for (int i = 0; i < _cachedList.Count; i++) | |||
unsafe | |||
{ | |||
var dotsEntityToSetup = _cachedList[i]; | |||
if (dotsEntityToSetup.@group == ExclusiveGroupStruct.Invalid) continue; | |||
var mapper = entitiesDB.QueryNativeMappedEntities<DOTSEntityComponent>(dotsEntityToSetup.@group); | |||
//Note: for some reason GetAllUniqueSharedComponentData returns DOTSEntityToSetup with valid values | |||
//that are not used anymore by any entity. Something to keep an eye on if fixed on future versions | |||
//of DOTS | |||
Entities.ForEach((Entity entity, int entityInQueryIndex, in DOTSSveltoEGID egid) => | |||
{ | |||
mapper.Entity(egid.egid.entityID).dotsEntity = entity; | |||
cmd.RemoveComponent<DOTSEntityToSetup>(entityInQueryIndex, entity); | |||
}).WithSharedComponentFilter(dotsEntityToSetup).ScheduleParallel(); | |||
base.OnDestroy(); | |||
MemoryUtilities.NativeFree((IntPtr)_jobHandle, Allocator.Persistent); | |||
} | |||
Dependency.Complete(); | |||
entityCommandBuffer.Playback(EntityManager); | |||
entityCommandBuffer.Dispose(); | |||
} | |||
protected override void OnCreate() | |||
{ | |||
} | |||
protected override void OnUpdate() | |||
{ | |||
throw new NotSupportedException("if this is called something broke the original design"); | |||
} | |||
readonly FasterList<SveltoOnDOTSHandleCreationEngine> _submissionEngines; | |||
readonly FasterList<ISveltoOnDOTSHandleLifeTimeEngine> _sveltoOnDotsHandleLifeTimeEngines; | |||
readonly FasterList<ISveltoOnDOTSStructuralEngine> _submissionEngines; | |||
readonly SimpleEntitiesSubmissionScheduler _submissionScheduler; | |||
readonly List<DOTSEntityToSetup> _cachedList; | |||
EntityCommandBuffer _entityCommandBuffer; | |||
DOTSOperationsForSvelto _dotsOperationsForSvelto; | |||
unsafe JobHandle* _jobHandle; | |||
} | |||
} | |||
#endif |