diff --git a/CHANGELOG.md b/CHANGELOG.md
index cd53b4b..20b8fbe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,25 @@
# Svelto.ECS Changelog
All notable changes to this project will be documented in this file. Changes are listed in random order of importance.
+## [3.5.0] - 09-2023
+
+* Introduced Serialization namespace for the serialization code
+* Unity: dropped 2020 support, minimum requirement is no 2021.3
+* Unity DOTS: added CreateDOTSToSveltoSyncEngine method in SveltoOnDOTSEnginesGroup
+* Refactor: split NB/MB struct from their internal logic that must be used only by the framework. Eventually NB and MB structs must be ref, as they are not supposed to be held (they may become invalid over the time). However due to the current DOTS patterns this is not possible. In future a sentinel pattern will allow to lease these buffers with the assumption that they can't be modified while held (and if a modification happens an exception will throw)
+ * Improved managed EGIDMultiMapper. A MultiMapper can improve components fetching performance
+ * Renamed IDisposableEngine interface to IDisposableEngine
+ * added EntityReference Exists method to validate it against a given entity database
+ * BUG FIXED: IReactOnDisposeEx callbacks were not correctly called
+ * BUG FIXED: fixed serious bug that would pass wrong entities indices to the moveTO callback under specific conditions
+ * Added Group Range functionality to GroupCompound
+ * Added Offset to GroupCompound to know the index of a given group compared to the starting group ID of the compound range (check MiniExample 9, Groupsonly for example)
+ * range and bitmask can be now set only in GroupTag and be inherited by GroupCompounds. GroupCompound bitmasks will be the OR of the group tags bitmasks, while the range will be the larger of the group tags ranges.
+ * entity filters enumerator do not iterate anymore filters belonging to disabled groups
+ * remove operations can now be executed in the same frame of a swap. a Remove will always supersed as Swap operation
+ * engines added to a GreoupEngine are automatically added to the enginesgroup
+
+
## [3.4.6] - 05-2023
* SveltoOnDOTS bug fixes/improvements
diff --git a/Core/CheckEntityUtilities.cs b/Core/CheckEntityUtilities.cs
index f32abf4..b4431e8 100644
--- a/Core/CheckEntityUtilities.cs
+++ b/Core/CheckEntityUtilities.cs
@@ -1,77 +1,135 @@
-#if !DEBUG || PROFILE_SVELTO
+//#define VERBOSE
+#if !DEBUG || PROFILE_SVELTO
#define DONT_USE
using System.Diagnostics;
#endif
using System;
using System.Collections.Generic;
+using Svelto.DataStructures;
namespace Svelto.ECS
{
///
- /// Note: this check doesn't catch the case when an add and remove is done on the same entity before the nextI am
+ /// Note: this check doesn't catch the case when an add and remove is done on the same entity before the next
/// submission. Two operations on the same entity are not allowed between submissions.
///
public partial class EnginesRoot
{
-#if DONT_USE
+ enum OperationType
+ {
+ Add,
+ Remove,
+ SwapFrom,
+ SwapTo
+ }
+
+#if DONT_USE
[Conditional("MEANINGLESS")]
#endif
- void CheckRemoveEntityID(EGID egid, Type entityDescriptorType, string caller)
+ void InitDebugChecks()
+ {
+ _multipleOperationOnSameEGIDChecker = new FasterDictionary();
+ _idChecker = new FasterDictionary>();
+ }
+
+#if DONT_USE
+ [Conditional("MEANINGLESS")]
+#endif
+ void CheckSwapEntityID(EGID fromEgid, EGID toEgid, Type entityDescriptorType, string caller)
{
- if (_multipleOperationOnSameEGIDChecker.ContainsKey(egid) == true)
+ if (_multipleOperationOnSameEGIDChecker.TryGetValue(fromEgid, out var fromOperationType) == true)
+ {
+ var operationName = OperationName(fromOperationType);
throw new ECSException(
- "Executing multiple structural changes (remove) in one submission on the same entity is not supported "
- .FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ")
- .FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
- .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
- .FastConcat(" previous operation was: ")
- .FastConcat(_multipleOperationOnSameEGIDChecker[egid] == 1 ? "add" : "remove"));
-
- if (_idChecker.TryGetValue(egid.groupID, out var hash))
+ "Executing multiple structural changes (swapFrom) on the same entity is not supported "
+ .FastConcat(" caller: ", caller, " ").FastConcat(fromEgid.entityID).FastConcat(" groupid: ")
+ .FastConcat(fromEgid.groupID.ToName()).FastConcat(" type: ")
+ .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
+ .FastConcat(" previous operation was: ")
+ .FastConcat(operationName));
+ }
+
+ if (_multipleOperationOnSameEGIDChecker.TryGetValue(toEgid, out var toOperationType) == true)
{
- if (hash.Contains(egid.entityID) == false)
- throw new ECSException("Trying to remove an Entity not present in the database "
- .FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID).FastConcat(" groupid: ")
- .FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
- .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available"));
+ var operationName = OperationName(toOperationType);
+ throw new ECSException(
+ "Executing multiple structural changes (swapTo) on the same entity is not supported "
+ .FastConcat(" caller: ", caller, " ").FastConcat(toEgid.entityID).FastConcat(" groupid: ")
+ .FastConcat(toEgid.groupID.ToName()).FastConcat(" type: ")
+ .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
+ .FastConcat(" previous operation was: ")
+ .FastConcat(operationName));
}
- else
+
+ HashRemove(fromEgid, entityDescriptorType, false, caller);
+ HashAdd(toEgid, entityDescriptorType, caller);
+
+ _multipleOperationOnSameEGIDChecker.Add(fromEgid, OperationType.SwapFrom);
+ _multipleOperationOnSameEGIDChecker.Add(toEgid, OperationType.SwapTo);
+ }
+
+#if DONT_USE
+ [Conditional("MEANINGLESS")]
+#endif
+ void CheckRemoveEntityID(EGID egid, Type entityDescriptorType, string caller)
+ {
+ bool isAllowed = false;
+ if (_multipleOperationOnSameEGIDChecker.TryGetValue(egid, out var operationType) == true)
{
- throw new ECSException("Trying to remove an Entity with a group never used so far "
- .FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID).FastConcat(" groupid: ")
- .FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
- .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available"));
+ isAllowed = operationType == OperationType.Remove || operationType == OperationType.SwapFrom;
+
+ if (isAllowed)
+ {
+#if VERBOSE
+ var operationName = OperationName(operationType);
+ Console.LogDebugWarning(
+ "Executing multiple structural changes (remove) in one submission on the same entity. Remove supersedes swap and remove operations "
+ .FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ")
+ .FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
+ .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
+ .FastConcat(" previous operation was: ")
+ .FastConcat(operationName));
+#endif
+ }
+ else
+ throw new ECSException(
+ "Executing multiple structural changes (remove) in one submission on the same entity is not supported "
+ .FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ")
+ .FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
+ .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
+ .FastConcat(" previous operation was: ")
+ .FastConcat("add"));
}
- hash.Remove(egid.entityID);
+ HashRemove(egid, entityDescriptorType, isAllowed, caller);
- _multipleOperationOnSameEGIDChecker.Add(egid, 0);
+ if (isAllowed == false)
+ _multipleOperationOnSameEGIDChecker.Add(egid, OperationType.Remove);
+ else
+ _multipleOperationOnSameEGIDChecker[egid] = OperationType.Remove;
}
+
#if DONT_USE
[Conditional("MEANINGLESS")]
#endif
void CheckAddEntityID(EGID egid, Type entityDescriptorType, string caller)
{
- if (_multipleOperationOnSameEGIDChecker.ContainsKey(egid) == true)
+ if (_multipleOperationOnSameEGIDChecker.TryGetValue(egid, out var operationType) == true)
+ {
+ var operationName = OperationName(operationType);
+
throw new ECSException(
"Executing multiple structural changes (build) on the same entity is not supported "
- .FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ")
- .FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
- .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
- .FastConcat(" previous operation was: ")
- .FastConcat(_multipleOperationOnSameEGIDChecker[egid] == 1 ? "add" : "remove"));
+ .FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ")
+ .FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
+ .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
+ .FastConcat(" previous operation was: ")
+ .FastConcat(operationName));
+ }
- var hash = _idChecker.GetOrAdd(egid.groupID, () => new HashSet());
- if (hash.Contains(egid.entityID) == true)
- throw new ECSException("Trying to add an Entity already present in the database "
- .FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID)
- .FastConcat(" groupid: ").FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
- .FastConcat(entityDescriptorType != null
- ? entityDescriptorType.Name
- : "not available"));
- hash.Add(egid.entityID);
-
- _multipleOperationOnSameEGIDChecker.Add(egid, 1);
+ HashAdd(egid, entityDescriptorType, caller);
+
+ _multipleOperationOnSameEGIDChecker.Add(egid, OperationType.Add);
}
#if DONT_USE
@@ -85,9 +143,72 @@ namespace Svelto.ECS
#if DONT_USE
[Conditional("MEANINGLESS")]
#endif
- void ClearChecksForMultipleOperationsOnTheSameEgid() { _multipleOperationOnSameEGIDChecker.Clear(); }
+ void ClearChecksForMultipleOperationsOnTheSameEgid()
+ {
+ _multipleOperationOnSameEGIDChecker.Clear();
+ }
+
+ void HashRemove(EGID egid, Type entityDescriptorType, bool isAllowed, string caller)
+ {
+ if (_idChecker.TryGetValue(egid.groupID, out HashSet hash))
+ {
+ if (hash.Contains(egid.entityID) == false && isAllowed == false)
+ throw new ECSException(
+ "Trying to remove an Entity not present in the database "
+ .FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID).FastConcat(" groupid: ")
+ .FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
+ .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available"));
+ }
+ else
+ {
+ throw new ECSException(
+ "Trying to remove an Entity with a group never used so far "
+ .FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID).FastConcat(" groupid: ")
+ .FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
+ .FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available"));
+ }
+
+ hash.Remove(egid.entityID);
+ }
+
+ void HashAdd(EGID egid, Type entityDescriptorType, string caller)
+ {
+ var hash = _idChecker.GetOrAdd(egid.groupID, () => new HashSet());
+ if (hash.Contains(egid.entityID) == true)
+ throw new ECSException(
+ "Trying to add an Entity already present in the database "
+ .FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID)
+ .FastConcat(" groupid: ").FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
+ .FastConcat(
+ entityDescriptorType != null
+ ? entityDescriptorType.Name
+ : "not available"));
+ hash.Add(egid.entityID);
+ }
+
+ string OperationName(OperationType operationType)
+ {
+ string operationName;
+ switch (operationType)
+ {
+ case OperationType.Remove:
+ operationName = "remove";
+ break;
+ case OperationType.Add:
+ operationName = "add";
+ break;
+ case OperationType.SwapFrom:
+ operationName = "swapFrom";
+ break;
+ default:
+ operationName = "swapTo";
+ break;
+ }
+
+ return operationName;
+ }
- readonly DataStructures.FasterDictionary _multipleOperationOnSameEGIDChecker;
- readonly DataStructures.FasterDictionary> _idChecker;
+ DataStructures.FasterDictionary _multipleOperationOnSameEGIDChecker;
+ DataStructures.FasterDictionary> _idChecker;
}
}
\ No newline at end of file
diff --git a/Core/ComponentBuilder.CheckFields.cs b/Core/ComponentBuilder.CheckFields.cs
index 9e76063..de51676 100644
--- a/Core/ComponentBuilder.CheckFields.cs
+++ b/Core/ComponentBuilder.CheckFields.cs
@@ -6,6 +6,7 @@ using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using Svelto.ECS.Hybrid;
+using Svelto.ECS.Serialization;
namespace Svelto.ECS
{
@@ -154,13 +155,11 @@ namespace Svelto.ECS
static readonly Type STRINGBUILDERTYPE = typeof(System.Text.StringBuilder);
internal static readonly Type ENTITY_INFO_COMPONENT = typeof(EntityInfoComponent);
+
public static ComponentID ENTITY_INFO_COMPONENT_ID
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- return ComponentTypeID.id;
- }
+ get => ComponentTypeID.id;
}
}
}
\ No newline at end of file
diff --git a/Core/ComponentBuilder.cs b/Core/ComponentBuilder.cs
index c9a53e3..3487c41 100644
--- a/Core/ComponentBuilder.cs
+++ b/Core/ComponentBuilder.cs
@@ -52,7 +52,7 @@ namespace Svelto.ECS
if (IS_UNMANAGED)
EntityComponentIDMap.Register(new Filler());
#endif
-
+ ComponentTypeID.Init();
ComponentBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT);
if (IS_ENTITY_VIEW_COMPONENT)
diff --git a/Core/ComponentID.cs b/Core/ComponentID.cs
index 7d0d046..fdacf0c 100644
--- a/Core/ComponentID.cs
+++ b/Core/ComponentID.cs
@@ -17,7 +17,7 @@ namespace Svelto.ECS
[DebuggerTypeProxy(typeof(ComponentIDDebugProxy))]
- public struct ComponentID: IEquatable
+ public struct ComponentID: IEquatable, IComparable
{
public static implicit operator int(ComponentID id)
{
@@ -46,6 +46,11 @@ namespace Svelto.ECS
{
return _id;
}
+
+ public int CompareTo(ComponentID other)
+ {
+ return _id.CompareTo(other._id);
+ }
int _id;
}
diff --git a/Core/ComponentTypeID.cs b/Core/ComponentTypeID.cs
index 6ecf992..0ad42cb 100644
--- a/Core/ComponentTypeID.cs
+++ b/Core/ComponentTypeID.cs
@@ -5,7 +5,7 @@ using Svelto.ECS.Internal;
namespace Svelto.ECS
{
- public static class BurstCompatibleCounter
+ static class BurstCompatibleCounter
{
public static int counter;
}
@@ -34,8 +34,10 @@ namespace Svelto.ECS
[Unity.Burst.BurstDiscard]
//SharedStatic values must be initialized from not burstified code
#endif
- static void Init()
+ internal static void Init()
{
+ if (_id.Data != 0)
+ return;
_id.Data = Interlocked.Increment(ref BurstCompatibleCounter.counter);
ComponentTypeMap.Add(typeof(T), id);
}
diff --git a/Core/EGID.cs b/Core/EGID.cs
index a3c2fd9..75263e0 100644
--- a/Core/EGID.cs
+++ b/Core/EGID.cs
@@ -6,8 +6,8 @@ using System.Runtime.InteropServices;
namespace Svelto.ECS
{
- [Serialization.DoNotSerialize]
- [Serializable]
+ [Serialization.DoNotSerialize] //EGID cannot be serialised with Svelto serialization code
+ [Serializable] //I do not remember why we marked this a Serializable though
[StructLayout(LayoutKind.Explicit)]
public struct EGID : IEquatable, IComparable
{
diff --git a/Core/EnginesGroup/IStepEngine.cs b/Core/EnginesGroup/IStepEngine.cs
index 12e7b50..0055b2b 100644
--- a/Core/EnginesGroup/IStepEngine.cs
+++ b/Core/EnginesGroup/IStepEngine.cs
@@ -1,3 +1,6 @@
+using System.Collections.Generic;
+using Svelto.DataStructures;
+
namespace Svelto.ECS
{
public interface IStepEngine : IEngine
@@ -9,17 +12,22 @@ namespace Svelto.ECS
public interface IStepEngine : IEngine
{
- void Step(in T _param);
+ void Step(in T param);
string name { get; }
}
+
+ public interface IGroupEngine
+ {
+ public IEnumerable engines { get; }
+ }
//this must stay IStepEngine as it may be part of a group itself
- public interface IStepGroupEngine : IStepEngine
+ public interface IStepGroupEngine : IStepEngine, IGroupEngine
{
}
- public interface IStepGroupEngine : IStepEngine
+ public interface IStepGroupEngine : IStepEngine, IGroupEngine
{
}
}
\ No newline at end of file
diff --git a/Core/EnginesGroup/SortedEnginesGroup.cs b/Core/EnginesGroup/SortedEnginesGroup.cs
index f9fee97..0e37231 100644
--- a/Core/EnginesGroup/SortedEnginesGroup.cs
+++ b/Core/EnginesGroup/SortedEnginesGroup.cs
@@ -1,12 +1,13 @@
+using System.Collections.Generic;
using Svelto.DataStructures;
using Svelto.Common;
namespace Svelto.ECS
{
///
- /// SortedEnginesGroup is a practical way to group engines that can be ticked together. The class requires a
- /// SequenceOrder struct that define the order of execution. The pattern to use is the following:
- /// First define as many enums as you want with the ID of the engines to use. E.G.:
+ /// SortedEnginesGroup is a practical way to group engines that need to be ticked in order. The class requires a
+ /// SequenceOrder struct that defines the order of execution. The pattern to use is the following:
+ /// First define as many enums as you want with the IDs of the engines to use. E.G.:
/// public enum WiresCompositionEngineNames
///{
/// WiresTimeRunningGroup,
@@ -17,11 +18,11 @@ namespace Svelto.ECS
/// then link these ID to the actual engines, using the attribute Sequenced:
///
/// [Sequenced(nameof(WiresCompositionEngineNames.WiresTimeRunningGroup))]
- /// class WiresTimeRunningGroup : UnsortedDeterministicEnginesGroupg {}
+ /// class WiresTimeRunningGroup : UnsortedDeterministicEnginesGroup {}
///
- /// Note that the engine can be another group itself (like in this example).
+ /// Note that the engine can be another engines group itself.
///
- /// then define the ISequenceOrder struct. E.G.:
+ /// then define the ISequenceOrder struct to define the order of execution. E.G.:
/// public struct DeterministicTimeRunningEnginesOrder: ISequenceOrder
/// {
/// private static readonly string[] order =
@@ -35,7 +36,18 @@ namespace Svelto.ECS
/// }
///
/// Now you can use the Type you just created (i.e.: DeterministicTimeRunningEnginesOrder) as generic parameter
- /// of the SortedEnginesGroup.
+ /// of the SortedEnginesGroup. like this:
+ ///
+ /// public class SortedDoofusesEnginesExecutionGroup : SortedEnginesGroup
+ /// {
+ /// public SortedDoofusesEnginesExecutionGroup(FasterList engines) : base(engines)
+ /// {
+ /// }
+ /// }
+ ///
+ /// This will then Tick the engines passed by constructor with the order defined in the DeterministicTimeRunningEnginesOrder
+ /// calling _enginesGroup.Step();
+ ///
/// While the system may look convoluted, is an effective way to keep the engines assemblies decoupled from
/// each other
/// The class is abstract and it requires a user defined interface to push the user to use recognisable names meaningful
@@ -43,7 +55,7 @@ namespace Svelto.ECS
///
/// user defined interface that implements IStepEngine
public abstract class SortedEnginesGroup : IStepGroupEngine
- where SequenceOrder : struct, ISequenceOrder where Interface : IStepEngine
+ where SequenceOrder : struct, ISequenceOrder where Interface : class, IStepEngine
{
protected SortedEnginesGroup(FasterList engines)
{
@@ -65,7 +77,15 @@ namespace Svelto.ECS
}
public string name => _name;
-
+ public IEnumerable engines
+ {
+ get
+ {
+ for (int i = 0; i < _instancedSequence.items.count; i++)
+ yield return _instancedSequence.items[i];
+ }
+ }
+
readonly string _name;
readonly Sequence _instancedSequence;
}
@@ -76,7 +96,7 @@ namespace Svelto.ECS
///
/// Specialised Parameter that can be passed to all the engines in the group
public abstract class SortedEnginesGroup: IStepGroupEngine
- where SequenceOrder : struct, ISequenceOrder where Interface : IStepEngine
+ where SequenceOrder : struct, ISequenceOrder where Interface : class, IStepEngine
{
protected SortedEnginesGroup(FasterList engines)
{
@@ -84,7 +104,7 @@ namespace Svelto.ECS
_instancedSequence = new Sequence(engines);
}
- public void Step(in Parameter param)
+ public void Step(in Parameter time)
{
var sequenceItems = _instancedSequence.items;
using (var profiler = new PlatformProfiler(_name))
@@ -92,12 +112,20 @@ namespace Svelto.ECS
for (var index = 0; index < sequenceItems.count; index++)
{
var engine = sequenceItems[index];
- using (profiler.Sample(engine.name)) engine.Step(param);
+ using (profiler.Sample(engine.name)) engine.Step(time);
}
}
}
public string name => _name;
+ public IEnumerable engines
+ {
+ get
+ {
+ for (int i = 0; i < _instancedSequence.items.count; i++)
+ yield return _instancedSequence.items[i];
+ }
+ }
readonly string _name;
readonly Sequence _instancedSequence;
diff --git a/Core/EnginesGroup/UnsortedEnginesGroup.cs b/Core/EnginesGroup/UnsortedEnginesGroup.cs
index f1e9bfc..090b9b1 100644
--- a/Core/EnginesGroup/UnsortedEnginesGroup.cs
+++ b/Core/EnginesGroup/UnsortedEnginesGroup.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using Svelto.Common;
using Svelto.DataStructures;
@@ -6,12 +7,16 @@ namespace Svelto.ECS
///
/// UnsortedEnginesGroup is a practical way to group engines that can be ticked together. As the name suggest
/// there is no way to defines an order, although the engines will run in the same order they are added.
- /// It is abstract and it requires a user defined interface to push the user to use recognisable names meaningful
- /// to the context where they are used.
+ /// It is abstract and it requires a user defined class to push the user to use recognisable names meaningful
+ /// to the context where they are used. like this:
+ /// public class SirensSequentialEngines: UnsortedEnginesGroup
+ /// {
+ ///
+ /// }
///
/// user defined interface that implements IStepEngine
public abstract class UnsortedEnginesGroup : IStepGroupEngine
- where Interface : IStepEngine
+ where Interface : class, IStepEngine
{
protected UnsortedEnginesGroup()
{
@@ -27,12 +32,12 @@ namespace Svelto.ECS
public void Step()
{
- var sequenceItems = _instancedSequence;
using (var profiler = new PlatformProfiler(_name))
{
- for (var index = 0; index < sequenceItems.count; index++)
+ var instancedSequenceCount = _instancedSequence.count;
+ for (var index = 0; index < instancedSequenceCount; index++)
{
- var engine = sequenceItems[index];
+ var engine = _instancedSequence[index];
using (profiler.Sample(engine.name)) engine.Step();
}
}
@@ -45,8 +50,18 @@ namespace Svelto.ECS
public string name => _name;
+ public IEnumerable engines
+ {
+ get
+ {
+ for (int i = 0; i < _instancedSequence.count; i++)
+ yield return _instancedSequence[i];
+ }
+ }
+
readonly string _name;
readonly FasterList _instancedSequence;
+
}
///
@@ -55,7 +70,7 @@ namespace Svelto.ECS
///
/// Specialised Parameter that can be passed to all the engines in the group
public abstract class UnsortedEnginesGroup : IStepGroupEngine
- where Interface : IStepEngine
+ where Interface : class, IStepEngine
{
protected UnsortedEnginesGroup()
{
@@ -69,15 +84,15 @@ namespace Svelto.ECS
_instancedSequence = engines;
}
- public void Step(in Parameter param)
+ public void Step(in Parameter time)
{
- var sequenceItems = _instancedSequence;
using (var profiler = new PlatformProfiler(_name))
{
- for (var index = 0; index < sequenceItems.count; index++)
+ var instancedSequenceCount = _instancedSequence.count;
+ for (var index = 0; index < instancedSequenceCount; index++)
{
- var engine = sequenceItems[index];
- using (profiler.Sample(engine.name)) engine.Step(param);
+ var engine = _instancedSequence[index];
+ using (profiler.Sample(engine.name)) engine.Step(time);
}
}
}
@@ -86,6 +101,15 @@ namespace Svelto.ECS
{
_instancedSequence.Add(engine);
}
+
+ public IEnumerable engines
+ {
+ get
+ {
+ for (int i = 0; i < _instancedSequence.count; i++)
+ yield return _instancedSequence[i];
+ }
+ }
public string name => _name;
diff --git a/Core/EnginesRoot.Engines.cs b/Core/EnginesRoot.Engines.cs
index 8892fe3..410ee83 100644
--- a/Core/EnginesRoot.Engines.cs
+++ b/Core/EnginesRoot.Engines.cs
@@ -18,7 +18,6 @@ namespace Svelto.ECS
{
EntityDescriptorsWarmup.WarmUp();
GroupHashMap.WarmUp();
- //SharedDictonary.Init();
SerializationDescriptorMap.Init();
_swapEntities = SwapEntities;
@@ -38,13 +37,11 @@ namespace Svelto.ECS
public EnginesRoot(EntitiesSubmissionScheduler entitiesComponentScheduler)
{
_entitiesOperations = new EntitiesOperations();
- _idChecker = new FasterDictionary>();
_cachedRangeOfSubmittedIndices = new FasterList<(uint, uint)>();
- _transientEntityIDsLeftAndAffectedByRemoval = new FasterList();
- _transientEntityIDsLeftWithoutDuplicates = new FasterDictionary();
-
- _multipleOperationOnSameEGIDChecker = new FasterDictionary();
+ _transientEntityIDsAffectedByRemoveAtSwapBack = new FasterDictionary();
+
+ InitDebugChecks();
#if UNITY_NATIVE //because of the thread count, ATM this is only for unity
_nativeSwapOperationQueue = new AtomicNativeBags(Allocator.Persistent);
_nativeRemoveOperationQueue = new AtomicNativeBags(Allocator.Persistent);
@@ -152,7 +149,9 @@ namespace Svelto.ECS
if (engine is IReactOnDispose viewEngineDispose)
CheckReactEngineComponents(
+#pragma warning disable CS0618
typeof(IReactOnDispose<>), viewEngineDispose, _reactiveEnginesDispose, type.Name);
+#pragma warning restore CS0618
if (engine is IReactOnDisposeEx viewEngineDisposeEx)
CheckReactEngineComponents(
@@ -160,7 +159,9 @@ namespace Svelto.ECS
if (engine is IReactOnSwap viewEngineSwap)
#pragma warning disable CS0612
+#pragma warning disable CS0618
CheckReactEngineComponents(typeof(IReactOnSwap<>), viewEngineSwap, _reactiveEnginesSwap, type.Name);
+#pragma warning restore CS0618
#pragma warning restore CS0612
if (engine is IReactOnSwapEx viewEngineSwapEx)
@@ -172,7 +173,11 @@ namespace Svelto.ECS
if (engine is IReactOnSubmissionStarted submissionEngineStarted)
_reactiveEnginesSubmissionStarted.Add(submissionEngineStarted);
-
+
+ if (engine is IGroupEngine stepGroupEngine)
+ foreach (var stepEngine in stepGroupEngine.engines)
+ AddEngine(stepEngine);
+
_enginesTypeSet.Add(refWrapper);
_enginesSet.Add(engine);
@@ -243,6 +248,9 @@ namespace Svelto.ECS
void Dispose(bool disposing)
{
+ if (_isDisposed)
+ return;
+
using (var profiler = new PlatformProfiler("Final Dispose"))
{
//Note: The engines are disposed before the the remove callback to give the chance to behave
@@ -252,7 +260,7 @@ namespace Svelto.ECS
foreach (var engine in _disposableEngines)
try
{
- if (engine is IDisposingEngine dengine)
+ if (engine is IDisposableEngine dengine)
dengine.isDisposing = true;
engine.Dispose();
diff --git a/Core/EnginesRoot.Entities.cs b/Core/EnginesRoot.Entities.cs
index 0c25bc8..0c680d6 100644
--- a/Core/EnginesRoot.Entities.cs
+++ b/Core/EnginesRoot.Entities.cs
@@ -8,7 +8,26 @@ using Svelto.ECS.Internal;
namespace Svelto.ECS
{
- public partial class EnginesRoot : IDisposable, IUnitTestingInterface
+ public static class EGIDMultiMapperNBExtension
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static EGIDMultiMapper QueryMappedEntities(this EntitiesDB entitiesDb, LocalFasterReadOnlyList groups)
+ where T : struct, _IInternalEntityComponent
+ {
+ var dictionary = new FasterDictionary>((uint) groups.count);
+
+ foreach (var group in groups)
+ {
+ entitiesDb.QueryOrCreateEntityDictionary(group, out var typeSafeDictionary);
+ //if (typeSafeDictionary.count > 0) avoiding this allows these egidmappers to be precreated and stored
+ dictionary.Add(group, typeSafeDictionary as ITypeSafeDictionary);
+ }
+
+ return new EGIDMultiMapper(dictionary);
+ }
+ }
+
+ public partial class EnginesRoot: IDisposable, IUnitTestingInterface
{
///--------------------------------------------
///
@@ -34,13 +53,15 @@ namespace Svelto.ECS
{
CheckAddEntityID(entityID, descriptorType, caller);
- DBC.ECS.Check.Require(entityID.groupID.isInvalid == false,
+ DBC.ECS.Check.Require(
+ entityID.groupID.isInvalid == false,
"invalid group detected, are you using new ExclusiveGroupStruct() instead of new ExclusiveGroup()?");
var reference = _entityLocator.ClaimReference();
_entityLocator.SetReference(reference, entityID);
- var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd, componentsToBuild, implementors
+ var dic = EntityFactory.BuildGroupedEntities(
+ entityID, _groupedEntityToAdd, componentsToBuild, implementors
#if DEBUG && !PROFILE_SVELTO
, descriptorType
#endif
@@ -52,7 +73,7 @@ namespace Svelto.ECS
///
/// Preallocate memory to avoid the impact to resize arrays when many entities are submitted at once
///
- void Preallocate(ExclusiveGroupStruct groupID, uint size, IComponentBuilder[] entityComponentsToBuild)
+ internal void Preallocate(ExclusiveGroupStruct groupID, uint size, IComponentBuilder[] entityComponentsToBuild)
{
void PreallocateEntitiesToAdd()
{
@@ -67,14 +88,14 @@ namespace Svelto.ECS
for (var index = 0; index < numberOfEntityComponents; index++)
{
var entityComponentBuilder = entityComponentsToBuild[index];
- var entityComponentType = entityComponentBuilder.getComponentID;
+ var entityComponentType = entityComponentBuilder.getComponentID;
- var dbList = group.GetOrAdd(entityComponentType, () => entityComponentBuilder.CreateDictionary(size));
+ var dbList = group.GetOrAdd(entityComponentType, () => entityComponentBuilder.CreateDictionary(size));
entityComponentBuilder.Preallocate(dbList, size);
if (_groupsPerEntity.TryGetValue(entityComponentType, out var groupedGroup) == false)
groupedGroup = _groupsPerEntity[entityComponentType] =
- new FasterDictionary();
+ new FasterDictionary();
groupedGroup[groupID] = dbList;
}
@@ -88,7 +109,8 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
FasterDictionary GetDBGroup(ExclusiveGroupStruct fromIdGroupId)
{
- if (_groupEntityComponentsDB.TryGetValue(fromIdGroupId,
+ if (_groupEntityComponentsDB.TryGetValue(
+ fromIdGroupId,
out FasterDictionary fromGroup) == false)
throw new ECSException("Group doesn't exist: ".FastConcat(fromIdGroupId.ToName()));
@@ -98,7 +120,8 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
FasterDictionary GetOrAddDBGroup(ExclusiveGroupStruct toGroupId)
{
- return _groupEntityComponentsDB.GetOrAdd(toGroupId,
+ return _groupEntityComponentsDB.GetOrAdd(
+ toGroupId,
() => new FasterDictionary());
}
@@ -106,9 +129,11 @@ namespace Svelto.ECS
{
var fromGroup = GetDBGroup(fromEntityGID.groupID);
- if (fromGroup.TryGetValue(ComponentBuilderUtilities.ENTITY_INFO_COMPONENT_ID,
+ if (fromGroup.TryGetValue(
+ ComponentBuilderUtilities.ENTITY_INFO_COMPONENT_ID,
out var entityInfoDic) //
- && ((ITypeSafeDictionary)entityInfoDic).TryGetValue(fromEntityGID.entityID,
+ && ((ITypeSafeDictionary)entityInfoDic).TryGetValue(
+ fromEntityGID.entityID,
out var entityInfo)) //there could be multiple entity descriptors registered in the same group, so it's necessary to check if the entity registered in the group has entityInfoComponent
{
#if PARANOID_CHECK
@@ -135,9 +160,11 @@ namespace Svelto.ECS
{
var fromGroup = GetDBGroup(fromEntityGID.groupID);
- if (fromGroup.TryGetValue(ComponentBuilderUtilities.ENTITY_INFO_COMPONENT_ID,
+ if (fromGroup.TryGetValue(
+ ComponentBuilderUtilities.ENTITY_INFO_COMPONENT_ID,
out var entityInfoDic) //
- && ((ITypeSafeDictionary)entityInfoDic).TryGetValue(fromEntityGID.entityID,
+ && ((ITypeSafeDictionary)entityInfoDic).TryGetValue(
+ fromEntityGID.entityID,
out var entityInfo)) //there could be multiple entity descriptors registered in the same group, so it's necessary to check if the entity registered in the group has entityInfoComponent
{
#if PARANOID_CHECK
@@ -168,14 +195,14 @@ namespace Svelto.ECS
//for each group id, save a dictionary indexed by entity type of entities indexed by id
// group EntityComponentType entityID, EntityComponent
internal readonly FasterDictionary>
- _groupEntityComponentsDB;
+ _groupEntityComponentsDB;
//for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are
//found indexed by group id. TypeSafeDictionary are never created, they instead point to the ones hold
//by _groupEntityComponentsDB
// >>
internal readonly FasterDictionary>
- _groupsPerEntity;
+ _groupsPerEntity;
#if SVELTO_LEGACY_FILTERS
//The filters stored for each component and group
internal readonly FasterDictionary>
diff --git a/Core/EnginesRoot.GenericEntityFactory.cs b/Core/EnginesRoot.GenericEntityFactory.cs
index 00f068e..d24b96c 100644
--- a/Core/EnginesRoot.GenericEntityFactory.cs
+++ b/Core/EnginesRoot.GenericEntityFactory.cs
@@ -77,7 +77,7 @@ namespace Svelto.ECS
}
#endif
- //enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside
+ //NOTE: enginesRoot is a weakreference ONLY because GenericEntityStreamConsumerFactory can be injected inside
//engines of other enginesRoot
readonly Svelto.DataStructures.WeakReference _enginesRoot;
}
diff --git a/Core/EnginesRoot.GenericEntityFunctions.cs b/Core/EnginesRoot.GenericEntityFunctions.cs
index 2c03ed1..3403604 100644
--- a/Core/EnginesRoot.GenericEntityFunctions.cs
+++ b/Core/EnginesRoot.GenericEntityFunctions.cs
@@ -55,8 +55,7 @@ namespace Svelto.ECS
dictionary.KeysEvaluator((key) =>
{
- _enginesRoot.Target.CheckRemoveEntityID(new EGID(key, fromGroupID), null, caller);
- _enginesRoot.Target.CheckAddEntityID(new EGID(key, toGroupID), null, caller);
+ _enginesRoot.Target.CheckSwapEntityID(new EGID(key, fromGroupID), new EGID(key, toGroupID), null, caller);
});
#endif
_enginesRoot.Target.QueueSwapGroupOperation(fromGroupID, toGroupID, caller);
@@ -100,8 +99,7 @@ namespace Svelto.ECS
var enginesRootTarget = _enginesRoot.Target;
- enginesRootTarget.CheckRemoveEntityID(fromEGID, TypeCache.type, caller);
- enginesRootTarget.CheckAddEntityID(toEGID, TypeCache.type, caller);
+ enginesRootTarget.CheckSwapEntityID(fromEGID, toEGID, TypeCache.type, caller);
enginesRootTarget.QueueSwapEntityOperation(fromEGID, toEGID
, _enginesRoot.Target.FindRealComponents(fromEGID)
@@ -137,8 +135,7 @@ namespace Svelto.ECS
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- void QueueSwapEntityOperation
- (EGID fromID, EGID toID, IComponentBuilder[] componentBuilders, string caller)
+ void QueueSwapEntityOperation(EGID fromID, EGID toID, IComponentBuilder[] componentBuilders, string caller)
{
_entitiesOperations.QueueSwapOperation(fromID, toID, componentBuilders, caller);
}
diff --git a/Core/EnginesRoot.Submission.cs b/Core/EnginesRoot.Submission.cs
index d267c48..914118a 100644
--- a/Core/EnginesRoot.Submission.cs
+++ b/Core/EnginesRoot.Submission.cs
@@ -2,6 +2,7 @@
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;
+using Svelto.DataStructures.Native;
using Svelto.ECS.Internal;
namespace Svelto.ECS
@@ -13,7 +14,7 @@ namespace Svelto.ECS
{
//clear the data checks before the submission. We want to allow structural changes inside the callbacks
ClearChecksForMultipleOperationsOnTheSameEgid();
-
+
_entitiesOperations.ExecuteRemoveAndSwappingOperations(
_swapEntities,
_removeEntities,
@@ -22,7 +23,7 @@ namespace Svelto.ECS
this);
AddEntities(profiler);
-
+
//clear the data checks after the submission, so if structural changes happened inside the callback, the debug structure is reset for the next frame operations
ClearChecksForMultipleOperationsOnTheSameEgid();
}
@@ -45,8 +46,7 @@ namespace Svelto.ECS
}
}
- static void RemoveEntities(
- FasterDictionary>>
+ static void RemoveEntities(FasterDictionary>>
removeOperations, FasterList entitiesRemoved, EnginesRoot enginesRoot)
{
using (var sampler = new PlatformProfiler("remove Entities"))
@@ -99,11 +99,11 @@ namespace Svelto.ECS
FasterList<(uint, string)> entityIDsToRemove = groupedEntitiesToRemove.value;
- enginesRoot._transientEntityIDsLeftAndAffectedByRemoval.Clear();
+ enginesRoot._transientEntityIDsAffectedByRemoveAtSwapBack.Clear();
fromComponentsDictionary.RemoveEntitiesFromDictionary(
entityIDsToRemove,
- enginesRoot._transientEntityIDsLeftAndAffectedByRemoval);
+ enginesRoot._transientEntityIDsAffectedByRemoveAtSwapBack);
//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
@@ -111,8 +111,7 @@ namespace Svelto.ECS
entityIDsToRemove,
fromGroup,
componentType,
- fromComponentsDictionary,
- enginesRoot._transientEntityIDsLeftAndAffectedByRemoval);
+ enginesRoot._transientEntityIDsAffectedByRemoveAtSwapBack);
//store new database count after the entities are removed from the datatabase, plus the number of entities removed
enginesRoot._cachedRangeOfSubmittedIndices.Add(
@@ -162,89 +161,96 @@ namespace Svelto.ECS
}
static void SwapEntities(FasterDictionary>>> swapEntitiesOperations,
- FasterList<(EGID, EGID)> entitiesIDSwaps, EnginesRoot enginesRoot)
+ FasterDictionary>>> swapEntitiesOperations,
+ FasterDictionary entitiesIDSwaps, EnginesRoot enginesRoot)
{
using (var sampler = new PlatformProfiler("Swap entities between groups"))
{
using (sampler.Sample("Update Entity References"))
{
var count = entitiesIDSwaps.count;
+ var entitiesIDSwapsValues = entitiesIDSwaps.unsafeValues;
for (int i = 0; i < count; i++)
{
- var (fromEntityGid, toEntityGid) = entitiesIDSwaps[i];
+ var (fromEntityGid, toEntityGid) = entitiesIDSwapsValues[i];
enginesRoot._entityLocator.UpdateEntityReference(fromEntityGid, toEntityGid);
}
}
-
+
using (sampler.Sample("Swap Entities"))
{
- enginesRoot._cachedRangeOfSubmittedIndices.Clear();
-
//Entities to swap are organised in order to minimise the amount of dictionary lookups.
//swapEntitiesOperations iterations happen in the following order:
//for each fromGroup, get all the entities to swap for each component type.
//then get the list of IDs for each ToGroup.
//now swap the set of FromGroup -> ToGroup entities per ID.
- foreach (var entitiesToSwap in swapEntitiesOperations)
+ foreach (var entitiesToSwap in swapEntitiesOperations) //each operation is a component to swap
{
+ enginesRoot._cachedRangeOfSubmittedIndices.Clear();
ExclusiveGroupStruct fromGroup = entitiesToSwap.key;
- var fromGroupDictionary = enginesRoot.GetDBGroup(fromGroup);
+ FasterDictionary fromGroupDictionary = enginesRoot.GetDBGroup(fromGroup);
//iterate all the fromgroups
foreach (var groupedEntitiesToSwap in entitiesToSwap.value)
{
- var componentType = groupedEntitiesToSwap.key;
+ ComponentID componentType = groupedEntitiesToSwap.key;
ITypeSafeDictionary fromComponentsDictionaryDB = fromGroupDictionary[componentType];
- //get the subset of togroups that come from from group
+ //for each fromgroup get the subset of togroups (entities that move from fromgroup to any togroup)
foreach (var entitiesInfoToSwap in groupedEntitiesToSwap.value)
{
ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key;
ITypeSafeDictionary toComponentsDictionaryDB = enginesRoot.GetOrAddTypeSafeDictionary(
- toGroup,
- enginesRoot.GetOrAddDBGroup(toGroup),
- componentType,
- fromComponentsDictionaryDB);
+ toGroup, enginesRoot.GetOrAddDBGroup(toGroup), componentType, fromComponentsDictionaryDB);
- 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;
+ DBC.ECS.Check.Assert(toComponentsDictionaryDB != null, "something went wrong with the creation of dictionaries");
+ //entities moving from fromgroup to this togroup
+ FasterDictionary fromEntityToEntityIDs = entitiesInfoToSwap.value;
+ DBC.ECS.Check.Assert(fromEntityToEntityIDs.count > 0, "something went wrong, no entities to swap");
//ensure that to dictionary has enough room to store the new entities
- 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))(toComponentsDictionaryDB.count,
- toComponentsDictionaryDB.count + fromEntityToEntityIDs.count));
+ var newBufferSize = (uint)(toComponentsDictionaryDB.count + fromEntityToEntityIDs.count);
+ toComponentsDictionaryDB.EnsureCapacity(newBufferSize);
- enginesRoot._transientEntityIDsLeftAndAffectedByRemoval.Clear();
+ //fortunately swap adds the swapped entities at the end of the buffer
+ //so we can just iterate the list using the indices ranges added in the cachedIndices
+ enginesRoot._cachedRangeOfSubmittedIndices.Add(((uint, uint))(toComponentsDictionaryDB.count, newBufferSize));
+ enginesRoot._transientEntityIDsAffectedByRemoveAtSwapBack.Clear();
fromComponentsDictionaryDB.SwapEntitiesBetweenDictionaries(
- fromEntityToEntityIDs,
- fromGroup,
- toGroup,
- toComponentsDictionaryDB,
- enginesRoot._transientEntityIDsLeftAndAffectedByRemoval);
+ fromEntityToEntityIDs, fromGroup, toGroup, toComponentsDictionaryDB,
+ enginesRoot._transientEntityIDsAffectedByRemoveAtSwapBack);
//important: this must happen after the entities are swapped in the database
enginesRoot.SwapEntityBetweenPersistentFilters(
- fromEntityToEntityIDs,
- fromComponentsDictionaryDB,
- toComponentsDictionaryDB,
- fromGroup,
- toGroup,
- componentType,
- enginesRoot._transientEntityIDsLeftAndAffectedByRemoval);
+ fromEntityToEntityIDs, fromGroup, toGroup, componentType,
+ enginesRoot._transientEntityIDsAffectedByRemoveAtSwapBack);
+ }
+ }
+
+ var rangeEnumerator = enginesRoot._cachedRangeOfSubmittedIndices.GetEnumerator();
+ using (sampler.Sample("Execute Swap Callbacks Fast"))
+ {
+ foreach (var groupedEntitiesToSwap in entitiesToSwap.value)
+ {
+ var componentType = groupedEntitiesToSwap.key;
+
+ foreach (var entitiesInfoToSwap in groupedEntitiesToSwap.value)
+ {
+ rangeEnumerator.MoveNext();
+
+ //get all the engines linked to TValue
+ if (!enginesRoot._reactiveEnginesSwapEx.TryGetValue(componentType, out var entityComponentsEngines))
+ continue;
+
+ ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key;
+ ITypeSafeDictionary toComponentsDictionary = GetTypeSafeDictionary(
+ toGroup, enginesRoot.GetDBGroup(toGroup), componentType);
+
+ toComponentsDictionary.ExecuteEnginesSwapCallbacksFast(
+ entityComponentsEngines, fromGroup, toGroup, rangeEnumerator.Current, in sampler);
+ }
}
}
}
@@ -261,64 +267,20 @@ namespace Svelto.ECS
var componentType = groupedEntitiesToSwap.key;
//get all the engines linked to TValue
- if (!enginesRoot._reactiveEnginesSwap.TryGetValue(
- componentType,
- out var entityComponentsEngines))
+ if (enginesRoot._reactiveEnginesSwap.TryGetValue(componentType, out var entityComponentsEngines) == false)
continue;
foreach (var entitiesInfoToSwap in groupedEntitiesToSwap.value)
{
ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key;
ITypeSafeDictionary toComponentsDictionary = GetTypeSafeDictionary(
- toGroup,
- enginesRoot.GetDBGroup(toGroup),
- componentType);
+ toGroup, enginesRoot.GetDBGroup(toGroup), componentType);
var infosToProcess = entitiesInfoToSwap.value;
toComponentsDictionary.ExecuteEnginesSwapCallbacks(
- infosToProcess,
- entityComponentsEngines,
- fromGroup,
- toGroup,
- in sampler);
- }
- }
- }
- }
-
- var rangeEnumerator = enginesRoot._cachedRangeOfSubmittedIndices.GetEnumerator();
- using (sampler.Sample("Execute Swap Callbacks Fast"))
- {
- foreach (var entitiesToSwap in swapEntitiesOperations)
- {
- ExclusiveGroupStruct fromGroup = entitiesToSwap.key;
-
- foreach (var groupedEntitiesToSwap in entitiesToSwap.value)
- {
- var componentType = groupedEntitiesToSwap.key;
-
- foreach (var entitiesInfoToSwap in groupedEntitiesToSwap.value)
- {
- rangeEnumerator.MoveNext();
-
- //get all the engines linked to TValue
- if (!enginesRoot._reactiveEnginesSwapEx.TryGetValue(
- componentType, out var entityComponentsEngines))
- continue;
-
- ExclusiveGroupStruct toGroup = entitiesInfoToSwap.key;
- ITypeSafeDictionary toComponentsDictionary = GetTypeSafeDictionary(
- toGroup,
- enginesRoot.GetDBGroup(toGroup),
- componentType);
-
- toComponentsDictionary.ExecuteEnginesSwapCallbacksFast(
- entityComponentsEngines,
- fromGroup,
- toGroup,
- rangeEnumerator.Current,
- in sampler);
+ infosToProcess, entityComponentsEngines,
+ fromGroup, toGroup, in sampler);
}
}
}
@@ -353,17 +315,17 @@ namespace Svelto.ECS
{
var type = entityComponentsToSubmit.key;
var fromDictionary = entityComponentsToSubmit.value;
-
+
var toDictionary = GetOrAddTypeSafeDictionary(groupID, groupDB, type, 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
- _cachedRangeOfSubmittedIndices.Add(
- ((uint, uint))(toDictionary.count, toDictionary.count + fromDictionary.count));
+ _cachedRangeOfSubmittedIndices.Add(((uint, uint))(toDictionary.count, toDictionary.count + fromDictionary.count));
//Fill the DB with the entity components generated this frame.
- fromDictionary.AddEntitiesToDictionary(toDictionary, groupID
+ fromDictionary.AddEntitiesToDictionary(
+ toDictionary, groupID
#if SLOW_SVELTO_SUBMISSION
- , entityLocator
+ , entityLocator
#endif
);
}
@@ -383,7 +345,7 @@ namespace Svelto.ECS
foreach (var entityComponentsToSubmit in groupToSubmit.components)
{
var type = entityComponentsToSubmit.key;
-
+
var toDictionary = GetTypeSafeDictionary(groupID, groupDB, type);
enumerator.MoveNext();
toDictionary.ExecuteEnginesAddEntityCallbacksFast(_reactiveEnginesAddEx, groupID, enumerator.Current, in sampler);
@@ -413,8 +375,7 @@ namespace Svelto.ECS
//this contains the total number of components ever submitted in the DB
ITypeSafeDictionary toDictionary = GetTypeSafeDictionary(groupID, groupDB, type);
- fromDictionary.ExecuteEnginesAddCallbacks(
- _reactiveEnginesAdd, toDictionary, groupID, in sampler);
+ fromDictionary.ExecuteEnginesAddCallbacks(_reactiveEnginesAdd, toDictionary, groupID, in sampler);
}
}
}
@@ -475,9 +436,10 @@ namespace Svelto.ECS
ITypeSafeDictionary fromDictionary = dictionaryOfEntities.value;
ITypeSafeDictionary toDictionary = GetOrAddTypeSafeDictionary(toGroupId, toGroup, refWrapperType, fromDictionary);
- fromDictionary.AddEntitiesToDictionary(toDictionary, toGroupId
+ fromDictionary.AddEntitiesToDictionary(
+ toDictionary, toGroupId
#if SLOW_SVELTO_SUBMISSION
- , entityLocator
+ , entityLocator
#endif
);
}
@@ -491,7 +453,8 @@ namespace Svelto.ECS
ITypeSafeDictionary toDictionary = GetTypeSafeDictionary(toGroupId, toGroup, refWrapperType);
//SwapEX happens inside
- fromDictionary.ExecuteEnginesSwapCallbacks_Group(_reactiveEnginesSwap, _reactiveEnginesSwapEx, toDictionary,
+ fromDictionary.ExecuteEnginesSwapCallbacks_Group(
+ _reactiveEnginesSwap, _reactiveEnginesSwapEx, toDictionary,
fromGroupId, toGroupId, platformProfiler);
}
@@ -520,7 +483,7 @@ namespace Svelto.ECS
//update GroupsPerEntity
if (_groupsPerEntity.TryGetValue(typeID, out var groupedGroup) == false)
groupedGroup = _groupsPerEntity[typeID] =
- new FasterDictionary();
+ new FasterDictionary();
groupedGroup[groupId] = toEntitiesDictionary;
}
@@ -545,14 +508,13 @@ namespace Svelto.ECS
//transient caches>>>>>>>>>>>>>>>>>>>>>
readonly FasterList<(uint, uint)> _cachedRangeOfSubmittedIndices;
- readonly FasterDictionary _transientEntityIDsLeftWithoutDuplicates;
- readonly FasterList _transientEntityIDsLeftAndAffectedByRemoval;
+ readonly FasterDictionary _transientEntityIDsAffectedByRemoveAtSwapBack;
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
static readonly
- Action>>>, FasterList<(EGID, EGID)>
- , EnginesRoot> _swapEntities;
+ Action>>>, FasterDictionary,
+ EnginesRoot> _swapEntities;
static readonly Action<
FasterDictionary>>,
diff --git a/Core/EntitiesDB.cs b/Core/EntitiesDB.cs
index e271f1d..133fe64 100644
--- a/Core/EntitiesDB.cs
+++ b/Core/EntitiesDB.cs
@@ -2,9 +2,7 @@
#define ENABLE_DEBUG_FUNC
#endif
-using System;
using System.Runtime.CompilerServices;
-using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
@@ -14,27 +12,14 @@ namespace Svelto.ECS
{
internal EntitiesDB(EnginesRoot enginesRoot, EnginesRoot.EntityReferenceMap entityReferencesMap)
{
- _enginesRoot = enginesRoot;
+ _enginesRoot = enginesRoot;
_entityReferencesMap = entityReferencesMap;
}
- EntityCollection InternalQueryEntities
- (FasterDictionary entitiesInGroupPerType)
- where T : struct, _IInternalEntityComponent
+ public void PreallocateEntitySpace(ExclusiveGroupStruct groupStructId, uint numberOfEntities)
+ where T : IEntityDescriptor, new()
{
- uint count = 0;
- IBuffer buffer;
- IEntityIDs ids = default;
- if (SafeQueryEntityDictionary(out var typeSafeDictionary, entitiesInGroupPerType) == false)
- buffer = default;
- else
- {
- ITypeSafeDictionary safeDictionary = (typeSafeDictionary as ITypeSafeDictionary);
- buffer = safeDictionary.GetValues(out count);
- ids = safeDictionary.entityIDs;
- }
-
- return new EntityCollection(buffer, ids, count);
+ _enginesRoot.Preallocate(groupStructId, numberOfEntities, EntityDescriptorTemplate.realDescriptor.componentsToBuild);
}
///
@@ -46,7 +31,7 @@ namespace Svelto.ECS
///
///
public EntityCollection QueryEntities(ExclusiveGroupStruct groupStructId)
- where T : struct, _IInternalEntityComponent
+ where T : struct, _IInternalEntityComponent
{
if (groupEntityComponentsDB.TryGetValue(groupStructId, out var entitiesInGroupPerType) == false)
{
@@ -57,7 +42,7 @@ namespace Svelto.ECS
}
public EntityCollection QueryEntities(ExclusiveGroupStruct groupStruct)
- where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent
+ where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent
{
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
{
@@ -70,21 +55,23 @@ namespace Svelto.ECS
var T2entities = InternalQueryEntities(entitiesInGroupPerType);
#if DEBUG && !PROFILE_SVELTO
if (T1entities.count != T2entities.count)
- throw new ECSException("Entity components count do not match in group. Entity 1: ' count: "
- .FastConcat(T1entities.count).FastConcat(" ", typeof(T1).ToString())
- .FastConcat("'. Entity 2: ' count: ".FastConcat(T2entities.count)
- .FastConcat(" ", typeof(T2).ToString())
- .FastConcat(
- "' 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"));
+ throw new ECSException(
+ "Entity components count do not match in group. Entity 1: ' count: "
+ .FastConcat(T1entities.count).FastConcat(" ", typeof(T1).ToString())
+ .FastConcat(
+ "'. Entity 2: ' count: ".FastConcat(T2entities.count)
+ .FastConcat(" ", typeof(T2).ToString())
+ .FastConcat("' 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(T1entities, T2entities);
}
public EntityCollection QueryEntities(ExclusiveGroupStruct groupStruct)
- where T1 : struct, _IInternalEntityComponent
- where T2 : struct, _IInternalEntityComponent
- where T3 : struct, _IInternalEntityComponent
+ where T1 : struct, _IInternalEntityComponent
+ where T2 : struct, _IInternalEntityComponent
+ where T3 : struct, _IInternalEntityComponent
{
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
{
@@ -99,23 +86,25 @@ namespace Svelto.ECS
var T3entities = InternalQueryEntities(entitiesInGroupPerType);
#if DEBUG && !PROFILE_SVELTO
if (T1entities.count != T2entities.count || T2entities.count != T3entities.count)
- throw new ECSException("Entity components count do not match in group. Entity 1: "
- .FastConcat(typeof(T1).ToString()).FastConcat(" count: ")
- .FastConcat(T1entities.count).FastConcat(
- " Entity 2: ".FastConcat(typeof(T2).ToString()).FastConcat(" count: ")
- .FastConcat(T2entities.count)
- .FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString()))
- .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"));
+ throw new ECSException(
+ "Entity components count do not match in group. Entity 1: "
+ .FastConcat(typeof(T1).ToString()).FastConcat(" count: ")
+ .FastConcat(T1entities.count).FastConcat(
+ " Entity 2: ".FastConcat(typeof(T2).ToString()).FastConcat(" count: ")
+ .FastConcat(T2entities.count)
+ .FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString()))
+ .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(T1entities, T2entities, T3entities);
}
public EntityCollection QueryEntities(ExclusiveGroupStruct groupStruct)
- where T1 : struct, _IInternalEntityComponent
- where T2 : struct, _IInternalEntityComponent
- where T3 : struct, _IInternalEntityComponent
- where T4 : struct, _IInternalEntityComponent
+ where T1 : struct, _IInternalEntityComponent
+ where T2 : struct, _IInternalEntityComponent
+ where T3 : struct, _IInternalEntityComponent
+ where T4 : struct, _IInternalEntityComponent
{
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
{
@@ -132,23 +121,25 @@ namespace Svelto.ECS
var T4entities = InternalQueryEntities(entitiesInGroupPerType);
#if DEBUG && !PROFILE_SVELTO
if (T1entities.count != T2entities.count || T2entities.count != T3entities.count
- || T3entities.count != T4entities.count)
- throw new ECSException("Entity components count do not match in group. Entity 1: "
- .FastConcat(typeof(T1).ToString()).FastConcat(" count: ")
- .FastConcat(T1entities.count).FastConcat(
- " Entity 2: ".FastConcat(typeof(T2).ToString()).FastConcat(" count: ")
- .FastConcat(T2entities.count)
- .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(" this means that you are mixing descriptors in the same group that do not share the components that you are querying"));
+ || T3entities.count != T4entities.count)
+ throw new ECSException(
+ "Entity components count do not match in group. Entity 1: "
+ .FastConcat(typeof(T1).ToString()).FastConcat(" count: ")
+ .FastConcat(T1entities.count).FastConcat(
+ " Entity 2: ".FastConcat(typeof(T2).ToString()).FastConcat(" count: ")
+ .FastConcat(T2entities.count)
+ .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(
+ " 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(T1entities, T2entities, T3entities, T4entities);
}
- public GroupsEnumerable QueryEntities
- (in LocalFasterReadOnlyList groups) where T : struct, _IInternalEntityComponent
+ public GroupsEnumerable QueryEntities(in LocalFasterReadOnlyList groups)
+ where T : struct, _IInternalEntityComponent
{
return new GroupsEnumerable(this, groups);
}
@@ -159,31 +150,31 @@ namespace Svelto.ECS
///
///
public GroupsEnumerable QueryEntities(in LocalFasterReadOnlyList groups)
- where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent
+ where T1 : struct, _IInternalEntityComponent where T2 : struct, _IInternalEntityComponent
{
return new GroupsEnumerable(this, groups);
}
- public GroupsEnumerable QueryEntities
- (in LocalFasterReadOnlyList groups) where T1 : struct, _IInternalEntityComponent
- where T2 : struct, _IInternalEntityComponent
- where T3 : struct, _IInternalEntityComponent
+ public GroupsEnumerable QueryEntities(in LocalFasterReadOnlyList groups)
+ where T1 : struct, _IInternalEntityComponent
+ where T2 : struct, _IInternalEntityComponent
+ where T3 : struct, _IInternalEntityComponent
{
return new GroupsEnumerable(this, groups);
}
- public GroupsEnumerable QueryEntities
- (in LocalFasterReadOnlyList groups) where T1 : struct, _IInternalEntityComponent
- where T2 : struct, _IInternalEntityComponent
- where T3 : struct, _IInternalEntityComponent
- where T4 : struct, _IInternalEntityComponent
+ public GroupsEnumerable QueryEntities(in LocalFasterReadOnlyList groups)
+ where T1 : struct, _IInternalEntityComponent
+ where T2 : struct, _IInternalEntityComponent
+ where T3 : struct, _IInternalEntityComponent
+ where T4 : struct, _IInternalEntityComponent
{
return new GroupsEnumerable(this, groups);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EGIDMapper QueryMappedEntities(ExclusiveGroupStruct groupStructId)
- where T : struct, _IInternalEntityComponent
+ where T : struct, _IInternalEntityComponent
{
if (SafeQueryEntityDictionary(groupStructId, out var typeSafeDictionary) == false)
throw new EntityGroupNotFoundException(typeof(T), groupStructId.ToName());
@@ -192,8 +183,8 @@ namespace Svelto.ECS
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool TryQueryMappedEntities
- (ExclusiveGroupStruct groupStructId, out EGIDMapper mapper) where T : struct, _IInternalEntityComponent
+ public bool TryQueryMappedEntities(ExclusiveGroupStruct groupStructId, out EGIDMapper mapper)
+ where T : struct, _IInternalEntityComponent
{
mapper = default;
if (SafeQueryEntityDictionary(groupStructId, out var typeSafeDictionary) == false
@@ -235,8 +226,7 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ExistsAndIsNotEmpty(ExclusiveGroupStruct gid)
{
- if (groupEntityComponentsDB.TryGetValue(
- gid, out FasterDictionary group) == true)
+ if (groupEntityComponentsDB.TryGetValue(gid, out FasterDictionary group) == true)
{
return group.count > 0;
}
@@ -271,9 +261,8 @@ namespace Svelto.ECS
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- bool SafeQueryEntityDictionary
- (out ITypeSafeDictionary typeSafeDictionary
- , FasterDictionary entitiesInGroupPerType) where T : struct, _IInternalEntityComponent
+ bool SafeQueryEntityDictionary(FasterDictionary entitiesInGroupPerType,
+ out ITypeSafeDictionary typeSafeDictionary) where T : struct, _IInternalEntityComponent
{
if (entitiesInGroupPerType.TryGetValue(ComponentTypeID.id, out var safeDictionary)
== false)
@@ -289,10 +278,24 @@ namespace Svelto.ECS
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal bool SafeQueryEntityDictionary
- (ExclusiveGroupStruct group, out ITypeSafeDictionary typeSafeDictionary) where T : struct, _IInternalEntityComponent
+ internal bool SafeQueryEntityDictionary(ExclusiveGroupStruct group, out ITypeSafeDictionary typeSafeDictionary)
+ where T : struct, _IInternalEntityComponent
{
- if (UnsafeQueryEntityDictionary(group, ComponentTypeID.id, out var safeDictionary) == false)
+ ITypeSafeDictionary safeDictionary;
+ bool ret;
+ //search for the group
+ if (groupEntityComponentsDB.TryGetValue(group, out FasterDictionary entitiesInGroupPerType) == false)
+ {
+ safeDictionary = null;
+ ret = false;
+ }
+ else
+ {
+ ret = entitiesInGroupPerType.TryGetValue(ComponentTypeID.id, out safeDictionary);
+ }
+
+ //search for the indexed entities in the group
+ if (ret == false)
{
typeSafeDictionary = default;
return false;
@@ -305,11 +308,21 @@ namespace Svelto.ECS
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal bool UnsafeQueryEntityDictionary
- (ExclusiveGroupStruct group, ComponentID id, out ITypeSafeDictionary typeSafeDictionary)
+ internal void QueryOrCreateEntityDictionary(ExclusiveGroupStruct group, out ITypeSafeDictionary typeSafeDictionary)
+ where T : struct, _IInternalEntityComponent
{
//search for the group
- if (groupEntityComponentsDB.TryGetValue(group, out var entitiesInGroupPerType) == false)
+ FasterDictionary entitiesInGroupPerType =
+ groupEntityComponentsDB.GetOrAdd(group, () => new FasterDictionary());
+
+ typeSafeDictionary = entitiesInGroupPerType.GetOrAdd(ComponentTypeID.id, () => TypeSafeDictionaryFactory.Create(0));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal bool UnsafeQueryEntityDictionary(ExclusiveGroupStruct groupID, ComponentID id, out ITypeSafeDictionary typeSafeDictionary)
+ {
+ //search for the group
+ if (groupEntityComponentsDB.TryGetValue(groupID, out FasterDictionary entitiesInGroupPerType) == false)
{
typeSafeDictionary = null;
return false;
@@ -319,8 +332,28 @@ namespace Svelto.ECS
return entitiesInGroupPerType.TryGetValue(id, out typeSafeDictionary);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ EntityCollection InternalQueryEntities(FasterDictionary entitiesInGroupPerType)
+ where T : struct, _IInternalEntityComponent
+ {
+ uint count = 0;
+ IBuffer buffer;
+ IEntityIDs ids = default;
+
+ if (SafeQueryEntityDictionary(entitiesInGroupPerType, out var typeSafeDictionary) == false)
+ buffer = default;
+ else
+ {
+ ITypeSafeDictionary safeDictionary = (typeSafeDictionary as ITypeSafeDictionary);
+ buffer = safeDictionary.GetValues(out count);
+ ids = safeDictionary.entityIDs;
+ }
+
+ return new EntityCollection(buffer, ids, count);
+ }
+
static readonly FasterDictionary _emptyDictionary =
- new FasterDictionary();
+ new FasterDictionary();
readonly EnginesRoot _enginesRoot;
@@ -331,14 +364,14 @@ namespace Svelto.ECS
//values directly, that can be iterated over, so that is possible to iterate over all the entity components of
//a specific type inside a specific group.
FasterDictionary>
- groupEntityComponentsDB => _enginesRoot._groupEntityComponentsDB;
+ groupEntityComponentsDB => _enginesRoot._groupEntityComponentsDB;
//for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are
//found indexed by group id. TypeSafeDictionary are never created, they instead point to the ones hold
//by _groupEntityComponentsDB
// >>
FasterDictionary> groupsPerComponent =>
- _enginesRoot._groupsPerEntity;
+ _enginesRoot._groupsPerEntity;
EnginesRoot.EntityReferenceMap _entityReferencesMap;
}
diff --git a/Core/EntitiesOperations.cs b/Core/EntitiesOperations.cs
index f6364cf..a00beaa 100644
--- a/Core/EntitiesOperations.cs
+++ b/Core/EntitiesOperations.cs
@@ -31,21 +31,40 @@ namespace Svelto.ECS
_thisSubmissionInfo._groupsToRemove.Add((groupID, caller));
}
- public void QueueRemoveOperation(EGID entityEgid, IComponentBuilder[] componentBuilders, string caller)
+ public void QueueRemoveOperation(EGID fromEgid, IComponentBuilder[] componentBuilders, string caller)
{
- _thisSubmissionInfo._entitiesRemoved.Add(entityEgid);
-
+ _thisSubmissionInfo._entitiesRemoved.Add(fromEgid);
+ RevertSwapOperation(fromEgid);
+
//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, _newGroupsDictionary, _recycleDictionary);
+ fromEgid.groupID, _newGroupsDictionary, _recycleDictionary);
foreach (var operation in componentBuilders)
{
removedComponentsPerType //recycle or create dictionaries per component type
.RecycleOrAdd(operation.getComponentID, _newList, _clearList)
//add entity to remove
- .Add((entityEgid.entityID, caller));
+ .Add((fromEgid.entityID, caller));
+ }
+
+ void RevertSwapOperation(EGID fromEgid)
+ {
+ if (_thisSubmissionInfo._entitiesSwapped.Remove(fromEgid, out (EGID fromEgid, EGID toEgid) val)) //Remove supersedes swap, check comment in IEntityFunctions.cs
+ {
+ var swappedComponentsPerType = _thisSubmissionInfo._currentSwapEntitiesOperations[fromEgid.groupID];
+
+ var componentBuildersLength = componentBuilders.Length - 1;
+
+ for (var index = componentBuildersLength; index >= 0; index--)
+ {
+ var operation = componentBuilders[index];
+
+ //todo: maybe the order of swappedComponentsPerType should be fromID, toGroupID, componentID
+ swappedComponentsPerType[operation.getComponentID][val.toEgid.groupID].Remove(fromEgid.entityID);
+ }
+ }
}
}
@@ -54,42 +73,42 @@ namespace Svelto.ECS
_thisSubmissionInfo._groupsToSwap.Add((fromGroupID, toGroupID, caller));
}
- public void QueueSwapOperation(EGID fromID, EGID toID, IComponentBuilder[] componentBuilders, string caller)
+ public void QueueSwapOperation(EGID fromEGID, EGID toEGID, IComponentBuilder[] componentBuilders, string caller)
{
- _thisSubmissionInfo._entitiesSwapped.Add((fromID, toID));
+ _thisSubmissionInfo._entitiesSwapped.Add(fromEGID, (fromEGID, toEGID));
//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
+
+ //Get (or create) the dictionary that holds the entities that are swapping from fromEGID group
var swappedComponentsPerType = _thisSubmissionInfo._currentSwapEntitiesOperations.RecycleOrAdd(
- fromID.groupID, _newGroupsDictionaryWithCaller, _recycleGroupDictionaryWithCaller);
+ fromEGID.groupID, _newGroupsDictionaryWithCaller, _recycleGroupDictionaryWithCaller);
var componentBuildersLength = componentBuilders.Length - 1;
+ //for each component of the entity that is swapping
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
+ 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(operation.getComponentID, _newGroupDictionary, _recycleDicitionaryWithCaller)
//recycle or create list of entities to swap
- .RecycleOrAdd(toID.groupID, _newListWithCaller, _clearListWithCaller)
+ .RecycleOrAdd(toEGID.groupID, _newListWithCaller, _clearListWithCaller)
//add entity to swap
- .Add((fromID.entityID, toID.entityID, caller));
+ .Add(fromEGID.entityID, new SwapInfo(fromEGID.entityID, toEGID.entityID, caller));
}
}
-
+
[MethodImpl(MethodImplOptions.Synchronized)]
public bool AnyOperationQueued()
{
return _thisSubmissionInfo.AnyOperationQueued();
}
- public void ExecuteRemoveAndSwappingOperations(Action>>>, FasterList<(EGID, EGID)>,
- EnginesRoot> swapEntities, Action>>,
- FasterList, EnginesRoot> removeEntities, Action removeGroup,
+ public void ExecuteRemoveAndSwappingOperations(
+ Action>>>, FasterDictionary, EnginesRoot> swapEntities, Action>>,
+ FasterList, EnginesRoot> removeEntities, Action removeGroup,
Action swapGroup, EnginesRoot enginesRoot)
{
(_thisSubmissionInfo, _lastSubmittedInfo) = (_lastSubmittedInfo, _thisSubmissionInfo);
@@ -104,7 +123,7 @@ 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);
@@ -119,7 +138,7 @@ 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);
@@ -127,88 +146,86 @@ namespace Svelto.ECS
}
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> NewGroupsDictionary()
+
+ static FasterDictionary> NewGroupsDictionary()
{
return new FasterDictionary>();
}
-
- void RecycleDictionary(ref FasterDictionary> recycled)
+
+ static void RecycleDictionary(ref FasterDictionary> recycled)
{
recycled.Recycle();
}
- FasterList<(uint, string)> NewList()
+ static FasterList<(uint, string)> NewList()
{
return new FasterList<(uint, string)>();
}
- void ClearList(ref FasterList<(uint, string)> target)
+ static void ClearList(ref FasterList<(uint, string)> target)
{
target.Clear();
}
- void RecycleDicitionaryWithCaller(ref FasterDictionary> target)
+ static void RecycleDicitionaryWithCaller(ref FasterDictionary> target)
{
target.Recycle();
}
- void ClearListWithCaller(ref FasterList<(uint, uint, string)> target)
+ static void ClearListWithCaller(ref FasterDictionary target)
{
target.Clear();
}
- FasterList<(uint, uint, string)> NewListWithCaller()
+ static FasterDictionary NewListWithCaller()
{
- return new FasterList<(uint, uint, string)>();
+ return new FasterDictionary();
}
- FasterDictionary>> NewGroupsDictionaryWithCaller()
+ static FasterDictionary>>
+ NewGroupsDictionaryWithCaller()
{
- return new FasterDictionary>>();
+ return new FasterDictionary>>();
}
- void RecycleGroupDictionaryWithCaller(ref FasterDictionary>> recycled)
+ static void RecycleGroupDictionaryWithCaller(
+ ref FasterDictionary>> recycled)
{
recycled.Recycle();
}
- FasterDictionary> NewGroupDictionary()
+ static FasterDictionary> NewGroupDictionary()
{
- return new FasterDictionary>();
+ return new FasterDictionary>();
}
struct Info
{
- //from group //actual component type
- internal FasterDictionary>>>
- _currentSwapEntitiesOperations;
+ //from group //actual component type
+ internal FasterDictionary>>> _currentSwapEntitiesOperations;
internal FasterDictionary>> _currentRemoveEntitiesOperations;
- internal FasterList<(EGID, EGID)> _entitiesSwapped;
- internal FasterList _entitiesRemoved;
- public FasterList<(ExclusiveBuildGroup, ExclusiveBuildGroup, string)> _groupsToSwap;
- public FasterList<(ExclusiveBuildGroup, string)> _groupsToRemove;
+ internal FasterDictionary _entitiesSwapped;
+ internal FasterList _entitiesRemoved;
+ public FasterList<(ExclusiveBuildGroup, ExclusiveBuildGroup, string)> _groupsToSwap;
+ public FasterList<(ExclusiveBuildGroup, string)> _groupsToRemove;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool AnyOperationQueued()
{
return _entitiesSwapped.count > 0 || _entitiesRemoved.count > 0 || _groupsToSwap.count > 0
- || _groupsToRemove.count > 0;
+ || _groupsToRemove.count > 0;
}
internal void Clear()
@@ -223,32 +240,68 @@ namespace Svelto.ECS
internal void Init()
{
- _entitiesSwapped = new FasterList<(EGID, EGID)>();
+ _entitiesSwapped = new FasterDictionary();
_entitiesRemoved = new FasterList();
- _groupsToRemove = new FasterList<(ExclusiveBuildGroup, string)>();
- _groupsToSwap = new FasterList<(ExclusiveBuildGroup, ExclusiveBuildGroup, string)>();
+ _groupsToRemove = new FasterList<(ExclusiveBuildGroup, string)>();
+ _groupsToSwap = new FasterList<(ExclusiveBuildGroup, ExclusiveBuildGroup, string)>();
_currentSwapEntitiesOperations =
- new FasterDictionary>>>();
+ new FasterDictionary>>>();
_currentRemoveEntitiesOperations =
- new FasterDictionary>>();
+ new FasterDictionary>>();
}
}
Info _lastSubmittedInfo;
Info _thisSubmissionInfo;
- readonly Func>> _newGroupDictionary;
+ readonly Func>> _newGroupDictionary;
readonly Func>> _newGroupsDictionary;
readonly ActionRef>> _recycleDictionary;
readonly Func> _newList;
readonly ActionRef> _clearList;
- readonly Func>>> _newGroupsDictionaryWithCaller;
- readonly ActionRef>>> _recycleGroupDictionaryWithCaller;
- readonly ActionRef>> _recycleDicitionaryWithCaller;
- readonly Func> _newListWithCaller;
- readonly ActionRef> _clearListWithCaller;
+
+ readonly Func>>>
+ _newGroupsDictionaryWithCaller;
+
+ readonly ActionRef>>>
+ _recycleGroupDictionaryWithCaller;
+
+ readonly ActionRef>> _recycleDicitionaryWithCaller;
+ readonly Func> _newListWithCaller;
+ readonly ActionRef> _clearListWithCaller;
+ }
+
+ public struct SwapInfo
+ {
+ public uint fromID; //to do this information should be redundant, try to remove it
+ public uint toID;
+ public uint toIndex;
+ public string trace;
+
+ public SwapInfo(uint fromEgidEntityId, uint toEgidEntityId, string s)
+ {
+ fromID = fromEgidEntityId;
+ toID = toEgidEntityId;
+ toIndex = 0;
+ trace = s;
+ }
+
+ public void Deconstruct(out uint fromID, out uint toID, out uint toIndex, out string caller)
+ {
+ fromID = this.fromID;
+ toID = this.toID;
+ toIndex = this.toIndex;
+ caller = this.trace;
+ }
+
+ public void Deconstruct(out uint fromID, out uint toID, out string caller)
+ {
+ fromID = this.fromID;
+ toID = this.toID;
+ caller = this.trace;
+ }
}
}
\ No newline at end of file
diff --git a/Core/EntityCollection.cs b/Core/EntityCollection.cs
index 1875918..0a37e22 100644
--- a/Core/EntityCollection.cs
+++ b/Core/EntityCollection.cs
@@ -10,15 +10,15 @@ namespace Svelto.ECS
{
DBC.ECS.Check.Require(count == 0 || buffer.isValid, "Buffer is found in impossible state");
- _buffer = buffer;
- _entityIDs = entityIDs;
+ this.buffer = buffer;
+ this.entityIDs = entityIDs;
this.count = count;
}
public uint count { get; }
- public readonly IBufferBase _buffer;
- public readonly IEntityIDs _entityIDs;
+ public readonly IBufferBase buffer;
+ public readonly IEntityIDs entityIDs;
}
public readonly ref struct EntityCollection where T1 : struct, _IInternalEntityComponent
diff --git a/Core/EntityInitializer.cs b/Core/EntityInitializer.cs
index f50c49d..0865588 100644
--- a/Core/EntityInitializer.cs
+++ b/Core/EntityInitializer.cs
@@ -19,9 +19,7 @@ namespace Svelto.ECS
public void Init(T initializer) where T : struct, _IInternalEntityComponent
{
- if (_group.TryGetValue(
- ComponentTypeID.id,
- out var typeSafeDictionary) == false)
+ if (_group.TryGetValue(ComponentTypeID.id, out var typeSafeDictionary) == false)
return;
var dictionary = (ITypeSafeDictionary)typeSafeDictionary;
@@ -29,16 +27,14 @@ namespace Svelto.ECS
if (ComponentBuilder.HAS_EGID)
SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref initializer, _ID);
#endif
-
if (dictionary.TryFindIndex(_ID.entityID, out var findElementIndex))
dictionary.GetDirectValueByRef(findElementIndex) = initializer;
}
- internal ref T GetOrAdd() where T : struct, _IInternalEntityComponent
+ internal ref T GetOrAdd() where T : unmanaged, _IInternalEntityComponent
{
- ref var entityDictionary = ref _group.GetOrAdd(
- ComponentTypeID.id,
- () => new UnmanagedTypeSafeDictionary(1));
+ ref var entityDictionary = ref _group.GetOrAdd(ComponentTypeID.id, () => new UnmanagedTypeSafeDictionary(1));
+
var dictionary = (ITypeSafeDictionary)entityDictionary;
return ref dictionary.GetOrAdd(_ID.entityID);
@@ -46,15 +42,12 @@ namespace Svelto.ECS
public ref T Get() where T : struct, _IInternalEntityComponent
{
- return ref (_group[ComponentTypeID.id] as ITypeSafeDictionary)
- .GetValueByRef(_ID.entityID);
+ return ref (_group[ComponentTypeID.id] as ITypeSafeDictionary).GetValueByRef(_ID.entityID);
}
public bool Has() where T : struct, _IInternalEntityComponent
{
- if (_group.TryGetValue(
- ComponentTypeID.id,
- out var typeSafeDictionary))
+ if (_group.TryGetValue(ComponentTypeID.id, out var typeSafeDictionary))
{
var dictionary = (ITypeSafeDictionary)typeSafeDictionary;
diff --git a/Core/EntityReference/EnginesRoot.LocatorMap.cs b/Core/EntityReference/EnginesRoot.LocatorMap.cs
index b8c2966..afcd1f0 100644
--- a/Core/EntityReference/EnginesRoot.LocatorMap.cs
+++ b/Core/EntityReference/EnginesRoot.LocatorMap.cs
@@ -146,6 +146,7 @@ namespace Svelto.ECS
_egidToReferenceMap.Remove(fromGroupId);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityReference GetEntityReference(EGID egid)
{
if (_egidToReferenceMap.TryGetValue(egid.groupID, out var groupMap))
@@ -153,15 +154,16 @@ 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.");
+ throw new ECSException(
+ $"Entity {egid} does not exist. If you just created it, get it from initializer.reference.");
#endif
}
- return EntityReference.Invalid;
+ throw new ECSException(
+ $"Entity {egid} does not exist. If you just created it, get it from initializer.reference.");
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public SharedSveltoDictionaryNative GetEntityReferenceMap(ExclusiveGroupStruct groupID)
{
if (_egidToReferenceMap.TryGetValue(groupID, out var groupMap) == false)
@@ -170,6 +172,7 @@ namespace Svelto.ECS
return groupMap;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetEGID(EntityReference reference, out EGID egid)
{
egid = default;
@@ -189,6 +192,7 @@ namespace Svelto.ECS
return false;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public EGID GetEGID(EntityReference reference)
{
#if DEBUG && !PROFILE_SVELTO
@@ -202,7 +206,6 @@ namespace Svelto.ECS
if (entityReferenceMapElement.version != reference.version)
throw new ECSException("outdated Reference");
#endif
-
return entityReferenceMapElement.egid;
}
@@ -244,8 +247,7 @@ namespace Svelto.ECS
//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>
- _egidToReferenceMap;
+ SharedSveltoDictionaryNative> _egidToReferenceMap;
}
EntityReferenceMap entityLocator => _entityLocator;
diff --git a/Core/EntityReference/EntitiesDB.References.cs b/Core/EntityReference/EntitiesDB.References.cs
index a47fea6..b4fbb04 100644
--- a/Core/EntityReference/EntitiesDB.References.cs
+++ b/Core/EntityReference/EntitiesDB.References.cs
@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
+using Svelto.DataStructures;
using Svelto.DataStructures.Native;
namespace Svelto.ECS
diff --git a/Core/EntityReference/EntityReference.cs b/Core/EntityReference/EntityReference.cs
index 6b5aa99..3d85283 100644
--- a/Core/EntityReference/EntityReference.cs
+++ b/Core/EntityReference/EntityReference.cs
@@ -67,11 +67,16 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ToEGID(EntitiesDB entitiesDB, out EGID egid)
{
- DBC.ECS.Check.Require(this != Invalid, "Invalid Reference Used");
-
return entitiesDB.TryGetEGID(this, out egid);
}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool Exists(EntitiesDB entitiesDB)
+ {
+ return this != Invalid && entitiesDB.TryGetEGID(this, out _);
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong ToULong()
{
return _GID;
diff --git a/Core/Filters/CombinedFilterID.cs b/Core/Filters/CombinedFilterID.cs
index 2196c31..68fdbcc 100644
--- a/Core/Filters/CombinedFilterID.cs
+++ b/Core/Filters/CombinedFilterID.cs
@@ -26,6 +26,11 @@ namespace Svelto.ECS
{
id = (long)filterID << 32 | (long)contextID.id << 16;
}
+
+ public CombinedFilterID(uint filterID, FilterContextID contextID)
+ {
+ id = (long)filterID << 32 | (long)contextID.id << 16;
+ }
public static implicit operator CombinedFilterID((int filterID, FilterContextID contextID) data)
{
diff --git a/Core/Filters/EnginesRoot.Filters.cs b/Core/Filters/EnginesRoot.Filters.cs
index 305f329..6b4c102 100644
--- a/Core/Filters/EnginesRoot.Filters.cs
+++ b/Core/Filters/EnginesRoot.Filters.cs
@@ -1,6 +1,5 @@
using Svelto.DataStructures;
using Svelto.DataStructures.Native;
-using Svelto.ECS.Internal;
namespace Svelto.ECS
{
@@ -59,11 +58,9 @@ namespace Svelto.ECS
///
///
///
- ///
///
void RemoveEntitiesFromPersistentFilters
- (FasterList<(uint entityID, string)> entityIDsRemoved, ExclusiveGroupStruct fromGroup, ComponentID refWrapperType
- , ITypeSafeDictionary fromDic, FasterList entityIDsLeftAndAffectedByRemoval)
+ (FasterList<(uint entityID, string)> entityIDsRemoved, ExclusiveGroupStruct fromGroup, ComponentID refWrapperType, FasterDictionary entityIDsAffectedByRemoveAtSwapBack)
{
//is there any filter used by this component?
if (_indicesOfPersistentFiltersUsedByThisComponent.TryGetValue(
@@ -72,14 +69,6 @@ namespace Svelto.ECS
var numberOfFilters = listOfFilters.count;
var filters = _persistentEntityFilters.unsafeValues;
- //remove duplicates
- _transientEntityIDsLeftWithoutDuplicates.Clear();
- var entityAffectedCount = entityIDsLeftAndAffectedByRemoval.count;
- for (int i = 0; i < entityAffectedCount; i++)
- {
- _transientEntityIDsLeftWithoutDuplicates[entityIDsLeftAndAffectedByRemoval[i]] = -1;
- }
-
for (int filterIndex = 0; filterIndex < numberOfFilters; ++filterIndex)
{
//foreach filter linked to this component
@@ -105,16 +94,11 @@ namespace Svelto.ECS
//of the deleted component
//entityIDsAffectedByRemoval tracks all the entitiesID of the components that need to be updated
//in the filters because their indices in the array changed.
- foreach (var entity in _transientEntityIDsLeftWithoutDuplicates)
+ foreach (var entity in entityIDsAffectedByRemoveAtSwapBack)
{
- var entityId = entity.key;
- if (fromGroupFilter.Exists(entityId)) //does the entityID that has been swapped exist in the filter?
- {
- if (entity.value == -1)
- entity.value = (int)fromDic.GetIndex(entityId); //let's find the index of the entityID in the dictionary only once
-
- fromGroupFilter._entityIDToDenseIndex[entityId] = (uint) entity.value; //update the index in the filter of the component that has been swapped
- }
+ var resultEntityIdToDenseIndex = fromGroupFilter._entityIDToDenseIndex;
+ if (resultEntityIdToDenseIndex.TryFindIndex(entity.key, out var index)) //does the entityID that has been swapped exist in the filter?
+ resultEntityIdToDenseIndex.unsafeValues[index] = entity.value; //update the index in the filter of the component that has been swapped
}
}
}
@@ -123,66 +107,49 @@ namespace Svelto.ECS
//this method is called by the framework only if listOfFilters.count > 0
void SwapEntityBetweenPersistentFilters
- (FasterList<(uint, uint, string)> fromEntityToEntityIDs, ITypeSafeDictionary fromDic
- , ITypeSafeDictionary toDic, ExclusiveGroupStruct fromGroup, ExclusiveGroupStruct toGroup
- , ComponentID refWrapperType, FasterList entityIDsLeftAndAffectedByRemoval)
+ (FasterDictionary fromEntityToEntityIDs, ExclusiveGroupStruct fromGroup, ExclusiveGroupStruct toGroup
+ , ComponentID refWrapperType, FasterDictionary entityIDsAffectedByRemoveAtSwapBack)
{
//is there any filter used by this component?
- if (_indicesOfPersistentFiltersUsedByThisComponent.TryGetValue(
- refWrapperType, out NativeDynamicArrayCast listOfFilters) == true)
+ if (_indicesOfPersistentFiltersUsedByThisComponent.TryGetValue(refWrapperType, out NativeDynamicArrayCast