Browse Source

Update to Svelto.ECS 3.4

pull/107/head
Sebastiano Mandala 1 year ago
parent
commit
ce29abeedc
100 changed files with 1963 additions and 2985 deletions
  1. +1
    -1
      com.sebaslab.svelto.common
  2. +20
    -1
      com.sebaslab.svelto.ecs/CHANGELOG.md
  3. +3
    -4
      com.sebaslab.svelto.ecs/Core/CheckEntityUtilities.cs
  4. +16
    -10
      com.sebaslab.svelto.ecs/Core/ComponentBuilder.cs
  5. +1
    -1
      com.sebaslab.svelto.ecs/Core/EGIDMapper.cs
  6. +4
    -4
      com.sebaslab.svelto.ecs/Core/EnginesRoot.DoubleBufferedEntitiesToAdd.cs
  7. +100
    -77
      com.sebaslab.svelto.ecs/Core/EnginesRoot.Engines.cs
  8. +2
    -1
      com.sebaslab.svelto.ecs/Core/EnginesRoot.Entities.cs
  9. +13
    -7
      com.sebaslab.svelto.ecs/Core/EnginesRoot.GenericEntityFactory.cs
  10. +4
    -4
      com.sebaslab.svelto.ecs/Core/EnginesRoot.GenericEntityFunctions.cs
  11. +124
    -95
      com.sebaslab.svelto.ecs/Core/EnginesRoot.Submission.cs
  12. +32
    -34
      com.sebaslab.svelto.ecs/Core/EntitiesDB.cs
  13. +142
    -71
      com.sebaslab.svelto.ecs/Core/EntitiesOperations.cs
  14. +10
    -10
      com.sebaslab.svelto.ecs/Core/EntityCollection.cs
  15. +6
    -5
      com.sebaslab.svelto.ecs/Core/EntityDescriptor/DynamicEntityDescriptor.cs
  16. +5
    -3
      com.sebaslab.svelto.ecs/Core/EntityDescriptor/ExtendibleEntityDescriptor.cs
  17. +61
    -35
      com.sebaslab.svelto.ecs/Core/EntityDescriptor/GenericEntityDescriptor.cs
  18. +4
    -1
      com.sebaslab.svelto.ecs/Core/EntityInfoView.cs
  19. +17
    -13
      com.sebaslab.svelto.ecs/Core/EntityInitializer.cs
  20. +49
    -32
      com.sebaslab.svelto.ecs/Core/EntityReference/EnginesRoot.LocatorMap.cs
  21. +6
    -0
      com.sebaslab.svelto.ecs/Core/EntityReference/EntitiesDB.References.cs
  22. +9
    -2
      com.sebaslab.svelto.ecs/Core/EntityReference/EntityReference.cs
  23. +0
    -10
      com.sebaslab.svelto.ecs/Core/EntitySubmitOperation.cs
  24. +3
    -1
      com.sebaslab.svelto.ecs/Core/EntityViewUtility.cs
  25. +20
    -0
      com.sebaslab.svelto.ecs/Core/Filters/CombinedFilterID.cs
  26. +3
    -5
      com.sebaslab.svelto.ecs/Core/Filters/EnginesRoot.Filters.cs
  27. +181
    -131
      com.sebaslab.svelto.ecs/Core/Filters/EntitiesDB.Filters.cs
  28. +25
    -9
      com.sebaslab.svelto.ecs/Core/Filters/EntityFilterCollection.cs
  29. +19
    -0
      com.sebaslab.svelto.ecs/Core/Filters/FilterContextID.cs
  30. +14
    -11
      com.sebaslab.svelto.ecs/Core/Filters/Legacy/EntitiesDB.LegacyFilters.cs
  31. +7
    -5
      com.sebaslab.svelto.ecs/Core/Filters/Legacy/LegacyFilterGroup.cs
  32. +5
    -3
      com.sebaslab.svelto.ecs/Core/Filters/Legacy/LegacyFilteredIndices.cs
  33. +5
    -3
      com.sebaslab.svelto.ecs/Core/Filters/Legacy/LegacyGroupFilters.cs
  34. +4
    -3
      com.sebaslab.svelto.ecs/Core/Filters/NativeEntityFilterCollection.cs
  35. +4
    -2
      com.sebaslab.svelto.ecs/Core/Filters/NativeEntityFilterIterator.cs
  36. +5
    -4
      com.sebaslab.svelto.ecs/Core/GlobalTypeID.cs
  37. +29
    -30
      com.sebaslab.svelto.ecs/Core/Groups/EntitiesDB.FindGroups.cs
  38. +1
    -1
      com.sebaslab.svelto.ecs/Core/Groups/ExclusiveBuildGroup.cs
  39. +4
    -9
      com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroupStruct.cs
  40. +74
    -39
      com.sebaslab.svelto.ecs/Core/Groups/GroupCompound.cs
  41. +14
    -13
      com.sebaslab.svelto.ecs/Core/Groups/GroupHashMap.cs
  42. +2
    -2
      com.sebaslab.svelto.ecs/Core/Groups/GroupNamesMap.cs
  43. +4
    -3
      com.sebaslab.svelto.ecs/Core/Groups/QueryGroups.cs
  44. +5
    -1
      com.sebaslab.svelto.ecs/Core/Hybrid/IEntityViewComponent.cs
  45. +0
    -11
      com.sebaslab.svelto.ecs/Core/IBaseEntityComponent.cs
  46. +26
    -12
      com.sebaslab.svelto.ecs/Core/IEngine.cs
  47. +9
    -0
      com.sebaslab.svelto.ecs/Core/IEntityComponent.cs
  48. +3
    -3
      com.sebaslab.svelto.ecs/Core/SetEGIDWithoutBoxing.cs
  49. +22
    -21
      com.sebaslab.svelto.ecs/Core/SpecialEnumerators/DoubleIterationEnumerator.cs
  50. +6
    -5
      com.sebaslab.svelto.ecs/Core/Streams/Consumer.cs
  51. +3
    -2
      com.sebaslab.svelto.ecs/Core/Streams/EnginesRoot.Streams.cs
  52. +5
    -4
      com.sebaslab.svelto.ecs/Core/Streams/EntitiesStreams.cs
  53. +2
    -1
      com.sebaslab.svelto.ecs/Core/Streams/EntityStream.cs
  54. +5
    -4
      com.sebaslab.svelto.ecs/Core/Streams/GenericentityStreamConsumerFactory.cs
  55. +2
    -10
      com.sebaslab.svelto.ecs/Core/TypeSafeDictionaryFactory.cs
  56. +10
    -0
      com.sebaslab.svelto.ecs/Core/_IInternalEntityComponent.cs
  57. +1
    -1
      com.sebaslab.svelto.ecs/DataStructures/ITypeSafeDictionary.cs
  58. +5
    -11
      com.sebaslab.svelto.ecs/DataStructures/ManagedTypeSafeDictionary.cs
  59. +24
    -14
      com.sebaslab.svelto.ecs/DataStructures/TypeSafeDictionaryMethods.cs
  60. +1
    -1
      com.sebaslab.svelto.ecs/DataStructures/TypeSafeDictionaryUtilities.cs
  61. +0
    -87
      com.sebaslab.svelto.ecs/DataStructures/Unmanaged/AtomicNativeBags.cs
  62. +0
    -204
      com.sebaslab.svelto.ecs/DataStructures/Unmanaged/NativeBag.cs
  63. +0
    -516
      com.sebaslab.svelto.ecs/DataStructures/Unmanaged/NativeDynamicArray.cs
  64. +0
    -82
      com.sebaslab.svelto.ecs/DataStructures/Unmanaged/NativeDynamicArrayCast.cs
  65. +0
    -23
      com.sebaslab.svelto.ecs/DataStructures/Unmanaged/NativeDynamicArrayUnityExtension.cs
  66. +0
    -47
      com.sebaslab.svelto.ecs/DataStructures/Unmanaged/SharedDisposableNative.cs
  67. +0
    -127
      com.sebaslab.svelto.ecs/DataStructures/Unmanaged/SharedNativeInt.cs
  68. +0
    -147
      com.sebaslab.svelto.ecs/DataStructures/Unmanaged/UnsafeArray.cs
  69. +0
    -272
      com.sebaslab.svelto.ecs/DataStructures/Unmanaged/UnsafeBlob.cs
  70. +4
    -5
      com.sebaslab.svelto.ecs/DataStructures/UnmanagedTypeSafeDictionary.cs
  71. +49
    -0
      com.sebaslab.svelto.ecs/ECSResources/ECSResourceManager.cs
  72. +1
    -8
      com.sebaslab.svelto.ecs/ECSResources/ECSResources.cs
  73. +1
    -1
      com.sebaslab.svelto.ecs/ECSResources/ECSString.cs
  74. +0
    -24
      com.sebaslab.svelto.ecs/Extensions/DisposeDisposablesEngine.cs
  75. +6
    -6
      com.sebaslab.svelto.ecs/Extensions/Native/EnginesRoot.NativeOperation.cs
  76. +10
    -0
      com.sebaslab.svelto.ecs/Extensions/Native/EntityNativeDBExtensions.cs
  77. +2
    -2
      com.sebaslab.svelto.ecs/Extensions/Native/NativeEGIDMapper.cs
  78. +2
    -3
      com.sebaslab.svelto.ecs/Extensions/Native/NativeEGIDMultiMapper.cs
  79. +28
    -12
      com.sebaslab.svelto.ecs/Extensions/Native/NativeEntityFactory.cs
  80. +16
    -12
      com.sebaslab.svelto.ecs/Extensions/Native/NativeEntityInitializer.cs
  81. +2
    -2
      com.sebaslab.svelto.ecs/Extensions/Native/NativeEntityRemove.cs
  82. +3
    -3
      com.sebaslab.svelto.ecs/Extensions/Native/NativeEntitySwap.cs
  83. +1
    -2
      com.sebaslab.svelto.ecs/Extensions/Native/UnityNativeEntityDBExtensions.cs
  84. +2
    -2
      com.sebaslab.svelto.ecs/Extensions/ProcessorCount.cs
  85. +15
    -7
      com.sebaslab.svelto.ecs/Extensions/Svelto/AllGroupsEnumerable.cs
  86. +2
    -1
      com.sebaslab.svelto.ecs/Extensions/Svelto/EGIDMultiMapper.cs
  87. +201
    -153
      com.sebaslab.svelto.ecs/Extensions/Svelto/EntityCollectionExtension.cs
  88. +4
    -1
      com.sebaslab.svelto.ecs/Extensions/Svelto/EntityManagedDBExtensions.cs
  89. +1
    -0
      com.sebaslab.svelto.ecs/Extensions/Svelto/ExclusiveGroupExtensions.cs
  90. +10
    -10
      com.sebaslab.svelto.ecs/Extensions/Svelto/GroupsEnumerable.cs
  91. +4
    -2
      com.sebaslab.svelto.ecs/Extensions/Svelto/Legacy/EntitiesDBFiltersExtension.cs
  92. +4
    -2
      com.sebaslab.svelto.ecs/Extensions/Svelto/Legacy/FilterGroupExtensions.cs
  93. +5
    -1
      com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/Jobs/UnityJobExtensions.cs
  94. +257
    -0
      com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/DOTSOperationsForSvelto.cs
  95. +0
    -55
      com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/DOTSSveltoEGID.cs
  96. +0
    -215
      com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/EntityCommandBufferForSvelto.cs
  97. +61
    -0
      com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/SveltoOnDOTSComponents.cs
  98. +17
    -0
      com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/SveltoOnDOTSCreationEngine.cs
  99. +33
    -22
      com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/SveltoOnDOTSEnginesGroup.cs
  100. +41
    -133
      com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/SveltoOnDOTSEntitiesSubmissionGroup.cs

+ 1
- 1
com.sebaslab.svelto.common

@@ -1 +1 @@
Subproject commit d75300010cb12c0aa80e5f7a4c7c425c07f4bcf2
Subproject commit ff24942680a5e5d257541ed857a140e62525fe45

+ 20
- 1
com.sebaslab.svelto.ecs/CHANGELOG.md View File

@@ -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


+ 3
- 4
com.sebaslab.svelto.ecs/Core/CheckEntityUtilities.cs View File

@@ -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;
}
}

+ 16
- 10
com.sebaslab.svelto.ecs/Core/ComponentBuilder.cs View File

@@ -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)
{


+ 1
- 1
com.sebaslab.svelto.ecs/Core/EGIDMapper.cs View File

@@ -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; }


+ 4
- 4
com.sebaslab.svelto.ecs/Core/EnginesRoot.DoubleBufferedEntitiesToAdd.cs View File

@@ -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;
}


+ 100
- 77
com.sebaslab.svelto.ecs/Core/EnginesRoot.Engines.cs View File

@@ -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


+ 2
- 1
com.sebaslab.svelto.ecs/Core/EnginesRoot.Entities.cs View File

@@ -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;



+ 13
- 7
com.sebaslab.svelto.ecs/Core/EnginesRoot.GenericEntityFactory.cs View File

@@ -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)


+ 4
- 4
com.sebaslab.svelto.ecs/Core/EnginesRoot.GenericEntityFunctions.cs View File

@@ -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);
}
}
}

+ 124
- 95
com.sebaslab.svelto.ecs/Core/EnginesRoot.Submission.cs View File

@@ -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;
}
}

+ 32
- 34
com.sebaslab.svelto.ecs/Core/EntitiesDB.cs View File

@@ -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)
{


+ 142
- 71
com.sebaslab.svelto.ecs/Core/EntitiesOperations.cs View File

@@ -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;
}
}

+ 10
- 10
com.sebaslab.svelto.ecs/Core/EntityCollection.cs View File

@@ -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


+ 6
- 5
com.sebaslab.svelto.ecs/Core/EntityDescriptor/DynamicEntityDescriptor.cs View File

@@ -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 =
{


+ 5
- 3
com.sebaslab.svelto.ecs/Core/EntityDescriptor/ExtendibleEntityDescriptor.cs View File

@@ -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>();
}


+ 61
- 35
com.sebaslab.svelto.ecs/Core/EntityDescriptor/GenericEntityDescriptor.cs View File

@@ -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>()
};
}



+ 4
- 1
com.sebaslab.svelto.ecs/Core/EntityInfoView.cs View File

@@ -1,6 +1,9 @@
using Svelto.ECS.Hybrid;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
struct EntityInfoComponent: IBaseEntityComponent
struct EntityInfoComponent: IManagedComponent
{
public IComponentBuilder[] componentsToBuild;
}

+ 17
- 13
com.sebaslab.svelto.ecs/Core/EntityInitializer.cs View File

@@ -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;
}
}

+ 49
- 32
com.sebaslab.svelto.ecs/Core/EntityReference/EnginesRoot.LocatorMap.cs View File

@@ -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;
}
}

+ 6
- 0
com.sebaslab.svelto.ecs/Core/EntityReference/EntitiesDB.References.cs View File

@@ -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
- 2
com.sebaslab.svelto.ecs/Core/EntityReference/EntityReference.cs View File

@@ -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);


+ 0
- 10
com.sebaslab.svelto.ecs/Core/EntitySubmitOperation.cs View File

@@ -1,10 +0,0 @@
namespace Svelto.ECS
{
enum EntitySubmitOperationType
{
Swap,
Remove,
RemoveGroup,
SwapGroup
}
}

+ 3
- 1
com.sebaslab.svelto.ecs/Core/EntityViewUtility.cs View File

@@ -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 ",


+ 20
- 0
com.sebaslab.svelto.ecs/Core/Filters/CombinedFilterID.cs View File

@@ -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);
}
}
}

+ 3
- 5
com.sebaslab.svelto.ecs/Core/Filters/EnginesRoot.Filters.cs View File

@@ -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++)
{


+ 181
- 131
com.sebaslab.svelto.ecs/Core/Filters/EntitiesDB.Filters.cs View File

@@ -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;
}
}
}

+ 25
- 9
com.sebaslab.svelto.ecs/Core/Filters/EntityFilterCollection.cs View File

@@ -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)]


+ 19
- 0
com.sebaslab.svelto.ecs/Core/Filters/FilterContextID.cs View File

@@ -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();
}
}
}

com.sebaslab.svelto.ecs/Core/Filters/EntitiesDB.LegacyFilters.cs → com.sebaslab.svelto.ecs/Core/Filters/Legacy/EntitiesDB.LegacyFilters.cs View File

@@ -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

com.sebaslab.svelto.ecs/Core/Filters/LegacyFilterGroup.cs → com.sebaslab.svelto.ecs/Core/Filters/Legacy/LegacyFilterGroup.cs View File

@@ -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

com.sebaslab.svelto.ecs/Core/Filters/LegacyFilteredIndices.cs → com.sebaslab.svelto.ecs/Core/Filters/Legacy/LegacyFilteredIndices.cs View File

@@ -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

com.sebaslab.svelto.ecs/Core/Filters/LegacyGroupFilters.cs → com.sebaslab.svelto.ecs/Core/Filters/Legacy/LegacyGroupFilters.cs View File

@@ -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

+ 4
- 3
com.sebaslab.svelto.ecs/Core/Filters/NativeEntityFilterCollection.cs View File

@@ -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);


+ 4
- 2
com.sebaslab.svelto.ecs/Core/Filters/NativeEntityFilterIterator.cs View File

@@ -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)
{


+ 5
- 4
com.sebaslab.svelto.ecs/Core/GlobalTypeID.cs View File

@@ -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);


+ 29
- 30
com.sebaslab.svelto.ecs/Core/Groups/EntitiesDB.FindGroups.cs View File

@@ -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


+ 1
- 1
com.sebaslab.svelto.ecs/Core/Groups/ExclusiveBuildGroup.cs View File

@@ -24,7 +24,7 @@ namespace Svelto.ECS
public override string ToString()
{
return this.group.ToName();
return group.ToName();
}

internal ExclusiveGroupStruct @group { get; }


+ 4
- 9
com.sebaslab.svelto.ecs/Core/Groups/ExclusiveGroupStruct.cs View File

@@ -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)


+ 74
- 39
com.sebaslab.svelto.ecs/Core/Groups/GroupCompound.cs View File

@@ -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;
}
}

+ 14
- 13
com.sebaslab.svelto.ecs/Core/Groups/GroupHashMap.cs View File

@@ -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


+ 2
- 2
com.sebaslab.svelto.ecs/Core/Groups/GroupNamesMap.cs View File

@@ -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;


+ 4
- 3
com.sebaslab.svelto.ecs/Core/Groups/QueryGroups.cs View File

@@ -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;



+ 5
- 1
com.sebaslab.svelto.ecs/Core/Hybrid/IEntityViewComponent.cs View File

@@ -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


+ 0
- 11
com.sebaslab.svelto.ecs/Core/IBaseEntityComponent.cs View File

@@ -1,11 +0,0 @@
namespace Svelto.ECS
{
///<summary>Entity Components MUST implement IEntityComponent</summary>
public interface IBaseEntityComponent
{
}
public interface IEntityComponent:IBaseEntityComponent
{
}
}

+ 26
- 12
com.sebaslab.svelto.ecs/Core/IEngine.cs View File

@@ -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();
}
}

+ 9
- 0
com.sebaslab.svelto.ecs/Core/IEntityComponent.cs View File

@@ -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
{
}
}

+ 3
- 3
com.sebaslab.svelto.ecs/Core/SetEGIDWithoutBoxing.cs View File

@@ -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();


+ 22
- 21
com.sebaslab.svelto.ecs/Core/SpecialEnumerators/DoubleIterationEnumerator.cs View File

@@ -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)
{


+ 6
- 5
com.sebaslab.svelto.ecs/Core/Streams/Consumer.cs View File

@@ -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()


+ 3
- 2
com.sebaslab.svelto.ecs/Core/Streams/EnginesRoot.Streams.cs View File

@@ -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);
}


+ 5
- 4
com.sebaslab.svelto.ecs/Core/Streams/EntitiesStreams.cs View File

@@ -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()


+ 2
- 1
com.sebaslab.svelto.ecs/Core/Streams/EntityStream.cs View File

@@ -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;



+ 5
- 4
com.sebaslab.svelto.ecs/Core/Streams/GenericentityStreamConsumerFactory.cs View File

@@ -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;
}
}

+ 2
- 10
com.sebaslab.svelto.ecs/Core/TypeSafeDictionaryFactory.cs View File

@@ -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)


+ 10
- 0
com.sebaslab.svelto.ecs/Core/_IInternalEntityComponent.cs View File

@@ -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
{
}
}
}

+ 1
- 1
com.sebaslab.svelto.ecs/DataStructures/ITypeSafeDictionary.cs View File

@@ -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);


+ 5
- 11
com.sebaslab.svelto.ecs/DataStructures/ManagedTypeSafeDictionary.cs View File

@@ -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


+ 24
- 14
com.sebaslab.svelto.ecs/DataStructures/TypeSafeDictionaryMethods.cs View File

@@ -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


+ 1
- 1
com.sebaslab.svelto.ecs/DataStructures/TypeSafeDictionaryUtilities.cs View File

@@ -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);



+ 0
- 87
com.sebaslab.svelto.ecs/DataStructures/Unmanaged/AtomicNativeBags.cs View File

@@ -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

+ 0
- 204
com.sebaslab.svelto.ecs/DataStructures/Unmanaged/NativeBag.cs View File

@@ -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;
}
}

+ 0
- 516
com.sebaslab.svelto.ecs/DataStructures/Unmanaged/NativeDynamicArray.cs View File

@@ -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;
}
}

+ 0
- 82
com.sebaslab.svelto.ecs/DataStructures/Unmanaged/NativeDynamicArrayCast.cs View File

@@ -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;
}
}

+ 0
- 23
com.sebaslab.svelto.ecs/DataStructures/Unmanaged/NativeDynamicArrayUnityExtension.cs View File

@@ -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

+ 0
- 47
com.sebaslab.svelto.ecs/DataStructures/Unmanaged/SharedDisposableNative.cs View File

@@ -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);
}
}
}
}
}

+ 0
- 127
com.sebaslab.svelto.ecs/DataStructures/Unmanaged/SharedNativeInt.cs View File

@@ -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);
}
}
}
}

+ 0
- 147
com.sebaslab.svelto.ecs/DataStructures/Unmanaged/UnsafeArray.cs View File

@@ -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;
}
}

+ 0
- 272
com.sebaslab.svelto.ecs/DataStructures/Unmanaged/UnsafeBlob.cs View File

@@ -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;
}
}

+ 4
- 5
com.sebaslab.svelto.ecs/DataStructures/UnmanagedTypeSafeDictionary.cs View File

@@ -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)]


+ 49
- 0
com.sebaslab.svelto.ecs/ECSResources/ECSResourceManager.cs View File

@@ -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
- 8
com.sebaslab.svelto.ecs/ECSResources/ECSResources.cs View File

@@ -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
- 1
com.sebaslab.svelto.ecs/ECSResources/ECSString.cs View File

@@ -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


+ 0
- 24
com.sebaslab.svelto.ecs/Extensions/DisposeDisposablesEngine.cs View File

@@ -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;
}

}

+ 6
- 6
com.sebaslab.svelto.ecs/Extensions/Native/EnginesRoot.NativeOperation.cs View File

@@ -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;


+ 10
- 0
com.sebaslab.svelto.ecs/Extensions/Native/EntityNativeDBExtensions.cs View File

@@ -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);
}
}
}

+ 2
- 2
com.sebaslab.svelto.ecs/Extensions/Native/NativeEGIDMapper.cs View File

@@ -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));


+ 2
- 3
com.sebaslab.svelto.ecs/Extensions/Native/NativeEGIDMultiMapper.cs View File

@@ -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)
{


+ 28
- 12
com.sebaslab.svelto.ecs/Extensions/Native/NativeEntityFactory.cs View File

@@ -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

+ 16
- 12
com.sebaslab.svelto.ecs/Extensions/Native/NativeEntityInitializer.cs View File

@@ -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;


+ 2
- 2
com.sebaslab.svelto.ecs/Extensions/Native/NativeEntityRemove.cs View File

@@ -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);


+ 3
- 3
com.sebaslab.svelto.ecs/Extensions/Native/NativeEntitySwap.cs View File

@@ -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)));
}


+ 1
- 2
com.sebaslab.svelto.ecs/Extensions/Native/UnityNativeEntityDBExtensions.cs View File

@@ -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);


+ 2
- 2
com.sebaslab.svelto.ecs/Extensions/ProcessorCount.cs View File

@@ -10,8 +10,8 @@ namespace Svelto.ECS
{
var iterationsPerBatch = totalIterations / processorCount;

if (iterationsPerBatch < 32)
return 32;
if (iterationsPerBatch < 64)
return 64;
return (int) iterationsPerBatch;
}


+ 15
- 7
com.sebaslab.svelto.ecs/Extensions/Svelto/AllGroupsEnumerable.cs View File

@@ -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;
}



+ 2
- 1
com.sebaslab.svelto.ecs/Extensions/Svelto/EGIDMultiMapper.cs View File

@@ -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,


+ 201
- 153
com.sebaslab.svelto.ecs/Extensions/Svelto/EntityCollectionExtension.cs View File

@@ -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;
}
}
}

+ 4
- 1
com.sebaslab.svelto.ecs/Extensions/Svelto/EntityManagedDBExtensions.cs View File

@@ -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);
}


+ 1
- 0
com.sebaslab.svelto.ecs/Extensions/Svelto/ExclusiveGroupExtensions.cs View File

@@ -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)]


+ 10
- 10
com.sebaslab.svelto.ecs/Extensions/Svelto/GroupsEnumerable.cs View File

@@ -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)
{


com.sebaslab.svelto.ecs/Extensions/Svelto/EntitiesDBFiltersExtension.cs → com.sebaslab.svelto.ecs/Extensions/Svelto/Legacy/EntitiesDBFiltersExtension.cs View File

@@ -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

com.sebaslab.svelto.ecs/Extensions/Svelto/FilterGroupExtensions.cs → com.sebaslab.svelto.ecs/Extensions/Svelto/Legacy/FilterGroupExtensions.cs View File

@@ -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

+ 5
- 1
com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/Jobs/UnityJobExtensions.cs View File

@@ -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);
}


+ 257
- 0
com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/DOTSOperationsForSvelto.cs View File

@@ -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

+ 0
- 55
com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/DOTSSveltoEGID.cs View File

@@ -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

+ 0
- 215
com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/EntityCommandBufferForSvelto.cs View File

@@ -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

+ 61
- 0
com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/SveltoOnDOTSComponents.cs View File

@@ -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

+ 17
- 0
com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/SveltoOnDOTSCreationEngine.cs View File

@@ -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

+ 33
- 22
com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/SveltoOnDOTSEnginesGroup.cs View File

@@ -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

+ 41
- 133
com.sebaslab.svelto.ecs/Extensions/Unity/DOTS/UECS/SveltoOnDOTSEntitiesSubmissionGroup.cs View File

@@ -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

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save