diff --git a/Svelto.Common b/Svelto.Common
index 6053063..49d3591 160000
--- a/Svelto.Common
+++ b/Svelto.Common
@@ -1 +1 @@
-Subproject commit 6053063786f5c0972cfd3bc9067a50e02e3bd1de
+Subproject commit 49d3591924c8c45e0ec32655f992480f9419e327
diff --git a/Svelto.ECS/CheckEntityUtilities.cs b/Svelto.ECS/CheckEntityUtilities.cs
index a002893..020b4b2 100644
--- a/Svelto.ECS/CheckEntityUtilities.cs
+++ b/Svelto.ECS/CheckEntityUtilities.cs
@@ -1,11 +1,10 @@
-#if DEBUG && !PROFILE_SVELTO
+#if !DEBUG || PROFILE_SVELTO
+#define DONT_USE
+#endif
using System;
using System.Collections.Generic;
-using Svelto.DataStructures;
-#else
-using System;
using System.Diagnostics;
-#endif
+using Svelto.DataStructures;
namespace Svelto.ECS
{
@@ -15,65 +14,72 @@ namespace Svelto.ECS
///
public partial class EnginesRoot
{
-#if DEBUG && !PROFILE_SVELTO
- void CheckRemoveEntityID(EGID egid, Type entityComponent, string caller = "")
+#if DONT_USE
+ [Conditional("CHECK_ALL")]
+#endif
+ void CheckRemoveEntityID(EGID egid, Type entityDescriptorType, string caller = "")
{
- if (_idCheckers.TryGetValue(egid.groupID, out var hash))
- {
- if (hash.Contains(egid.entityID) == false)
- throw new ECSException("Entity with not found ID is about to be removed: id: "
- .FastConcat(" caller: ", caller, " ")
- .FastConcat(egid.entityID).FastConcat(" groupid: ").FastConcat(egid.groupID.ToName())
- .FastConcat(" type: ").FastConcat(entityComponent != null ? entityComponent.Name : "not available"));
+ if (_multipleOperationOnSameEGIDChecker.ContainsKey(egid) == true)
+ throw new ECSException(
+ "Executing multiple structural changes 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(" operation was: ")
+ .FastConcat(_multipleOperationOnSameEGIDChecker[egid] == 1 ? "add" : "remove"));
- hash.Remove(egid.entityID);
+ if (_idChecker.TryGetValue(egid.groupID, out var hash))
+ if (hash.Contains(egid.entityID) == false)
+ throw new ECSException("Trying to remove an Entity never submitted in the database "
+ .FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID)
+ .FastConcat(" groupid: ").FastConcat(egid.groupID.ToName())
+ .FastConcat(" type: ")
+ .FastConcat(entityDescriptorType != null
+ ? entityDescriptorType.Name
+ : "not available"));
+ else
+ hash.Remove(egid.entityID);
- if (hash.Count == 0)
- _idCheckers.Remove(egid.groupID);
- }
- else
- {
- throw new ECSException("Entity with not found ID is about to be removed: id: "
- .FastConcat(" caller: ", caller, " ")
- .FastConcat(egid.entityID).FastConcat(" groupid: ").FastConcat(egid.groupID.ToName())
- .FastConcat(" type: ").FastConcat(entityComponent != null ? entityComponent.Name : "not available"));
- }
+ _multipleOperationOnSameEGIDChecker.Add(egid, 0);
}
-
- void CheckAddEntityID(EGID egid, Type entityComponent, string caller = "")
+#if DONT_USE
+ [Conditional("CHECK_ALL")]
+#endif
+ void CheckAddEntityID(EGID egid, Type entityDescriptorType, string caller = "")
{
-// Console.LogError("added ".FastConcat(egid.ToString()));
-
- if (_idCheckers.TryGetValue(egid.groupID, out var hash) == false)
- hash = _idCheckers[egid.groupID] = new HashSet();
- else
- if (hash.Contains(egid.entityID))
- throw new ECSException("Entity with used ID is about to be built: '"
- .FastConcat("' id: '").FastConcat(egid.entityID).FastConcat("' groupid: '")
- .FastConcat(egid.groupID.ToName()).FastConcat(" ", entityComponent != null ? entityComponent.Name : "not available")
- .FastConcat("'"));
+ if (_multipleOperationOnSameEGIDChecker.ContainsKey(egid) == true)
+ throw new ECSException(
+ "Executing multiple structural changes 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(" operation was: ")
+ .FastConcat(_multipleOperationOnSameEGIDChecker[egid] == 1 ? "add" : "remove"));
+ var hash = _idChecker.GetOrCreate(egid.groupID, () => new HashSet());
+ if (hash.Contains(egid.entityID) == true)
+ throw new ECSException("Trying to add an Entity already submitted to the database "
+ .FastConcat(" caller: ", caller, " ").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);
+
}
- void RemoveGroupID(ExclusiveGroupStruct groupID) { _idCheckers.Remove(groupID); }
-
- readonly FasterDictionary> _idCheckers = new FasterDictionary>();
-#else
- [Conditional("_CHECKS_DISABLED")]
- void CheckRemoveEntityID(EGID egid, Type entityComponent, string caller = "")
- {
- }
+#if DONT_USE
+ [Conditional("CHECK_ALL")]
+#endif
+ void RemoveGroupID(BuildGroup groupID) { _idChecker.Remove(groupID); }
- [Conditional("_CHECKS_DISABLED")]
- void CheckAddEntityID(EGID egid, Type entityComponen, string caller = "")
- {
- }
-
- [Conditional("_CHECKS_DISABLED")]
- void RemoveGroupID(ExclusiveGroupStruct groupID)
- {
- }
+#if DONT_USE
+ [Conditional("CHECK_ALL")]
#endif
+ void ClearChecks() { _multipleOperationOnSameEGIDChecker.FastClear(); }
+
+ readonly FasterDictionary _multipleOperationOnSameEGIDChecker = new FasterDictionary();
+ readonly FasterDictionary> _idChecker = new FasterDictionary>();
}
}
\ No newline at end of file
diff --git a/Svelto.ECS/ComponentBuilder.CheckFields.cs b/Svelto.ECS/ComponentBuilder.CheckFields.cs
index e09f38d..e933d36 100644
--- a/Svelto.ECS/ComponentBuilder.CheckFields.cs
+++ b/Svelto.ECS/ComponentBuilder.CheckFields.cs
@@ -4,12 +4,11 @@ using System.Diagnostics;
#endif
using System;
using System.Reflection;
-using System.Text;
using Svelto.Common;
namespace Svelto.ECS
{
- internal static class ComponentBuilderUtilities
+ static class ComponentBuilderUtilities
{
const string MSG = "Entity Components and Entity View Components fields cannot hold managed fields outside the Svelto rules.";
@@ -18,7 +17,7 @@ namespace Svelto.ECS
#endif
public static void CheckFields(Type entityComponentType, bool needsReflection, bool isStringAllowed = false)
{
- if (entityComponentType == ENTITY_STRUCT_INFO_VIEW || entityComponentType == EGIDType ||
+ if (entityComponentType == ENTITY_INFO_COMPONENT || entityComponentType == EGIDType ||
entityComponentType == EXCLUSIVEGROUPSTRUCTTYPE || entityComponentType == SERIALIZABLE_ENTITY_STRUCT)
{
return;
@@ -80,11 +79,7 @@ namespace Svelto.ECS
}
}
else
- if (fieldInfo.FieldType.IsUnmanagedEx() == true)
- {
- SubCheckFields(fieldInfo.FieldType, entityComponentType, isStringAllowed);
- }
- else
+ if (fieldInfo.FieldType.IsUnmanagedEx() == false)
{
ProcessError("Entity View Components must hold only public interfaces, strings or unmanaged type fields.",
entityComponentType);
@@ -110,10 +105,10 @@ namespace Svelto.ECS
{
//pass if it's Primitive or C# 8 unmanaged, or it's a string and string are allowed
//this check must allow pointers are they are unmanaged types
- if ((isStringAllowed == true && IsString(fieldType) == true) || fieldType.IsUnmanagedEx() == true)
+ if ((isStringAllowed == true && IsString(fieldType) == true) || fieldType.IsValueTypeEx() == true)
{
//if it's a struct we have to check the fields recursively
- if (IsString(fieldType) == false && !fieldType.IsEnum && fieldType.IsPrimitive == false)
+ if (IsString(fieldType) == false)
{
CheckFields(fieldType, false, isStringAllowed);
}
@@ -142,6 +137,6 @@ namespace Svelto.ECS
static readonly Type STRINGTYPE = typeof(string);
static readonly Type STRINGBUILDERTYPE = typeof(System.Text.StringBuilder);
- internal static readonly Type ENTITY_STRUCT_INFO_VIEW = typeof(EntityInfoViewComponent);
+ internal static readonly Type ENTITY_INFO_COMPONENT = typeof(EntityInfoComponent);
}
}
\ No newline at end of file
diff --git a/Svelto.ECS/ComponentBuilder.cs b/Svelto.ECS/ComponentBuilder.cs
index 7089576..a948d2d 100644
--- a/Svelto.ECS/ComponentBuilder.cs
+++ b/Svelto.ECS/ComponentBuilder.cs
@@ -30,6 +30,7 @@ namespace Svelto.ECS
var castedDic = dictionary as ITypeSafeDictionary;
T entityComponent = default;
+
if (IS_ENTITY_VIEW_COMPONENT)
{
DBC.ECS.Check.Require(castedDic.ContainsKey(egid.entityID) == false,
@@ -89,7 +90,7 @@ namespace Svelto.ECS
EntityViewComponentCache.InitCache();
else
{
- if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW && ENTITY_COMPONENT_TYPE.IsUnmanagedEx() == false)
+ if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_INFO_COMPONENT && ENTITY_COMPONENT_TYPE.IsUnmanagedEx() == false)
throw new Exception($"Entity Component check failed, unexpected struct type (must be unmanaged) {ENTITY_COMPONENT_TYPE}");
}
}
@@ -104,7 +105,12 @@ namespace Svelto.ECS
static readonly T DEFAULT_IT;
static readonly string ENTITY_COMPONENT_NAME;
-
+ ///
+ /// Note: this static class will hold forever the references of the entities implementors. These references
+ /// are not even cleared when the engines root is destroyed, as they are static references.
+ /// It must be seen as an application-wide cache system. Honestly, I am not sure if this may cause leaking
+ /// issues in some kind of applications. To remember.
+ ///
static class EntityViewComponentCache
{
internal static readonly FasterList>> cachedFields;
diff --git a/Svelto.ECS/EGIDComponent.cs b/Svelto.ECS/Components/EGIDComponent.cs
similarity index 100%
rename from Svelto.ECS/EGIDComponent.cs
rename to Svelto.ECS/Components/EGIDComponent.cs
diff --git a/Svelto.ECS/EntityHierarchyComponent.cs b/Svelto.ECS/Components/EntityHierarchyComponent.cs
similarity index 100%
rename from Svelto.ECS/EntityHierarchyComponent.cs
rename to Svelto.ECS/Components/EntityHierarchyComponent.cs
diff --git a/Svelto.ECS/Components/LinkedEntityComponent.cs b/Svelto.ECS/Components/LinkedEntityComponent.cs
new file mode 100644
index 0000000..03583d4
--- /dev/null
+++ b/Svelto.ECS/Components/LinkedEntityComponent.cs
@@ -0,0 +1,7 @@
+namespace Svelto.ECS
+{
+ public struct LinkedEntityComponent : IEntityComponent
+ {
+ public EGID linkedEntity;
+ }
+}
\ No newline at end of file
diff --git a/Svelto.ECS/DataStructures/FastTypeSafeDictionary.cs b/Svelto.ECS/DataStructures/FastTypeSafeDictionary.cs
index dc55efc..15aa8ef 100644
--- a/Svelto.ECS/DataStructures/FastTypeSafeDictionary.cs
+++ b/Svelto.ECS/DataStructures/FastTypeSafeDictionary.cs
@@ -61,7 +61,7 @@ namespace Svelto.ECS.Internal
}
}
- public void AddEntitiesToEngines(FasterDictionary, FasterList> entityComponentEnginesDB,
+ public void AddEntitiesToEngines(FasterDictionary> entityComponentEnginesDB,
ITypeSafeDictionary realDic,
ExclusiveGroupStruct @group,
in PlatformProfiler profiler)
@@ -75,7 +75,7 @@ namespace Svelto.ECS.Internal
}
public void RemoveEntitiesFromEngines(
- FasterDictionary, FasterList> entityComponentEnginesDB, in PlatformProfiler profiler,
+ FasterDictionary> entityComponentEnginesDB, in PlatformProfiler profiler,
ExclusiveGroupStruct @group)
{
foreach (var value in _implementation)
@@ -105,7 +105,7 @@ namespace Svelto.ECS.Internal
public void Clear() { _implementation.Clear(); }
public void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup,
- FasterDictionary, FasterList> engines,
+ FasterDictionary> engines,
in PlatformProfiler profiler)
{
var valueIndex = _implementation.GetIndex(fromEntityGid.entityID);
@@ -139,14 +139,14 @@ namespace Svelto.ECS.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ITypeSafeDictionary Create() { return new FastTypeSafeDictionary(); }
- void AddEntityComponentToEngines(FasterDictionary, FasterList> entityComponentEnginesDB,
+ void AddEntityComponentToEngines(FasterDictionary> entityComponentEnginesDB,
ref TValue entity,
ExclusiveGroupStruct? previousGroup,
in PlatformProfiler profiler,
EGID egid)
{
//get all the engines linked to TValue
- if (!entityComponentEnginesDB.TryGetValue(new RefWrapper(_type), out var entityComponentsEngines)) return;
+ if (!entityComponentEnginesDB.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines)) return;
if (previousGroup == null)
{
@@ -184,13 +184,13 @@ namespace Svelto.ECS.Internal
}
}
- static void RemoveEntityComponentFromEngines(FasterDictionary, FasterList> @group,
+ static void RemoveEntityComponentFromEngines(FasterDictionary> @group,
ref TValue entity,
ExclusiveGroupStruct? previousGroup,
in PlatformProfiler profiler,
EGID egid)
{
- if (!@group.TryGetValue(new RefWrapper(_type), out var entityComponentsEngines)) return;
+ if (!@group.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines)) return;
if (previousGroup == null)
{
diff --git a/Svelto.ECS/DataStructures/ITypeSafeDictionary.cs b/Svelto.ECS/DataStructures/ITypeSafeDictionary.cs
index f111a12..77d24e1 100644
--- a/Svelto.ECS/DataStructures/ITypeSafeDictionary.cs
+++ b/Svelto.ECS/DataStructures/ITypeSafeDictionary.cs
@@ -17,16 +17,19 @@ namespace Svelto.ECS.Internal
public interface ITypeSafeDictionary:IDisposable
{
- uint count { get; }
+ uint count { get; }
ITypeSafeDictionary Create();
- void AddEntitiesToEngines(FasterDictionary, FasterList> entityComponentEnginesDb,
- ITypeSafeDictionary realDic, ExclusiveGroupStruct @group, in PlatformProfiler profiler);
- void RemoveEntitiesFromEngines(FasterDictionary, FasterList> entityComponentEnginesDB,
+ //todo: there is something wrong in the design of the execute callback methods. Something to cleanup
+ void ExecuteEnginesAddOrSwapCallbacks(FasterDictionary> entityComponentEnginesDb,
+ ITypeSafeDictionary realDic, ExclusiveGroupStruct? fromGroup, ExclusiveGroupStruct toGroup, in PlatformProfiler profiler);
+ void ExecuteEnginesSwapOrRemoveCallbacks(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup,
+ FasterDictionary> engines, in PlatformProfiler profiler);
+ void ExecuteEnginesRemoveCallbacks(FasterDictionary> entityComponentEnginesDB,
in PlatformProfiler profiler, ExclusiveGroupStruct @group);
+
void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId);
- void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup,
- FasterDictionary, FasterList> engines, in PlatformProfiler profiler);
+
void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup);
void RemoveEntityFromDictionary(EGID fromEntityGid);
@@ -38,5 +41,7 @@ namespace Svelto.ECS.Internal
bool ContainsKey(uint egidEntityId);
uint GetIndex(uint valueEntityId);
bool TryFindIndex(uint entityGidEntityId, out uint index);
+
+ void KeysEvaluator(System.Action action);
}
}
\ No newline at end of file
diff --git a/Svelto.ECS/DataStructures/ThreadSafeNativeBagTest.cs b/Svelto.ECS/DataStructures/ThreadSafeNativeBagTest.cs
new file mode 100644
index 0000000..b001c65
--- /dev/null
+++ b/Svelto.ECS/DataStructures/ThreadSafeNativeBagTest.cs
@@ -0,0 +1,74 @@
+// using System.Threading.Tasks;
+// using NUnit.Framework;
+// using Svelto.Common;
+// using Svelto.ECS.DataStructures;
+//
+// namespace Svelto.ECS.Tests.Common.DataStructures
+// {
+// [TestFixture]
+// public class ThreadSafeNativeBagTest
+// {
+// [Test]
+// public void TestByteReallocWorks()
+// {
+// var threadNativeBag = new ThreadSafeNativeBag(Allocator.Persistent);
+//
+// Parallel.Invoke(() =>
+// {
+// for (int i = 0; i < 100; i++)
+// {
+// threadNativeBag.Enqueue((int)1);
+// }
+// }
+// , // close first Action
+// () =>
+// {
+// for (int i = 0; i < 100; i++)
+// {
+// threadNativeBag.Enqueue((int)2);
+// }
+// }
+// , //close second Action
+//
+// () =>
+// {
+// for (int i = 0; i < 100; i++)
+// {
+// threadNativeBag.Enqueue(3);
+// }
+// } //close third Action
+// ); //close parallel.invoke
+//
+// // for (int i = 0; i < 100; i++)
+// // {
+// // threadNativeBag.Enqueue(1);
+// // }
+//
+// int oneCount = 0, twoCount = 0, threeCount = 0;
+//
+// while (threadNativeBag.count > 0)
+// {
+// var value = threadNativeBag.Dequeue();
+//
+// switch (value)
+// {
+// case 1:
+// oneCount++;
+// break;
+// case 2:
+// twoCount++;
+// break;
+// case 3:
+// threeCount++;
+// break;
+// }
+// }
+//
+// Assert.That(oneCount, Is.EqualTo(100));
+// Assert.That(twoCount, Is.EqualTo(100));
+// Assert.That(threeCount, Is.EqualTo(100));
+//
+// threadNativeBag.Dispose();
+// }
+// }
+// }
\ No newline at end of file
diff --git a/Svelto.ECS/DataStructures/TypeSafeDictionary.cs b/Svelto.ECS/DataStructures/TypeSafeDictionary.cs
index 089fde8..c74f124 100644
--- a/Svelto.ECS/DataStructures/TypeSafeDictionary.cs
+++ b/Svelto.ECS/DataStructures/TypeSafeDictionary.cs
@@ -8,30 +8,36 @@ namespace Svelto.ECS.Internal
{
sealed class TypeSafeDictionary : ITypeSafeDictionary where TValue : struct, IEntityComponent
{
- static readonly Type _type = typeof(TValue);
- static readonly string _typeName = _type.Name;
- static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type);
+ static readonly Type _type = typeof(TValue);
+ static readonly string _typeName = _type.Name;
+ static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type);
- internal static readonly bool _isUmanaged =
+ internal static readonly bool IsUnmanaged =
_type.IsUnmanagedEx() && (typeof(IEntityViewComponent).IsAssignableFrom(_type) == false);
- internal SveltoDictionary>, ManagedStrategy> implMgd;
- internal SveltoDictionary>, NativeStrategy> implUnmgd;
+ SveltoDictionary>, ManagedStrategy,
+ ManagedStrategy> implMgd;
+
+ //used directly by native methods
+ internal SveltoDictionary>, NativeStrategy,
+ NativeStrategy> implUnmgd;
public TypeSafeDictionary(uint size)
{
- if (_isUmanaged)
- implUnmgd = new SveltoDictionary>, NativeStrategy>(size);
+ if (IsUnmanaged)
+ implUnmgd = new SveltoDictionary>,
+ NativeStrategy, NativeStrategy>(size);
else
{
- implMgd = new SveltoDictionary>, ManagedStrategy>(size);
+ implMgd = new SveltoDictionary>,
+ ManagedStrategy, ManagedStrategy>(size);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(uint egidEntityId, in TValue entityComponent)
{
- if (_isUmanaged)
+ if (IsUnmanaged)
implUnmgd.Add(egidEntityId, entityComponent);
else
implMgd.Add(egidEntityId, entityComponent);
@@ -45,7 +51,7 @@ namespace Svelto.ECS.Internal
///
public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId)
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
var typeSafeDictionary = (entitiesToSubmit as TypeSafeDictionary).implUnmgd;
@@ -89,18 +95,19 @@ namespace Svelto.ECS.Internal
}
}
- public void AddEntitiesToEngines
- (FasterDictionary, FasterList> entityComponentEnginesDB
- , ITypeSafeDictionary realDic, ExclusiveGroupStruct group, in PlatformProfiler profiler)
+ public void ExecuteEnginesAddOrSwapCallbacks
+ (FasterDictionary> entityComponentEnginesDB
+ , ITypeSafeDictionary realDic, ExclusiveGroupStruct? fromGroup, ExclusiveGroupStruct toGroup
+ , in PlatformProfiler profiler)
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
var typeSafeDictionary = realDic as ITypeSafeDictionary;
//this can be optimized, should pass all the entities and not restart the process for each one
foreach (var value in implUnmgd)
- AddEntityComponentToEngines(entityComponentEnginesDB, ref typeSafeDictionary[value.Key]
- , null, in profiler, new EGID(value.Key, group));
+ ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(entityComponentEnginesDB, ref typeSafeDictionary[value.Key]
+ , fromGroup, in profiler, new EGID(value.Key, toGroup));
}
else
{
@@ -108,14 +115,14 @@ namespace Svelto.ECS.Internal
//this can be optimized, should pass all the entities and not restart the process for each one
foreach (var value in implMgd)
- AddEntityComponentToEngines(entityComponentEnginesDB, ref typeSafeDictionary[value.Key]
- , null, in profiler, new EGID(value.Key, group));
+ ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(entityComponentEnginesDB, ref typeSafeDictionary[value.Key]
+ , fromGroup, in profiler, new EGID(value.Key, toGroup));
}
}
public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup)
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
var valueIndex = implUnmgd.GetIndex(fromEntityGid.entityID);
@@ -152,7 +159,7 @@ namespace Svelto.ECS.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
implUnmgd.Clear();
}
@@ -165,7 +172,7 @@ namespace Svelto.ECS.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FastClear()
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
implUnmgd.FastClear();
}
@@ -178,7 +185,7 @@ namespace Svelto.ECS.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ContainsKey(uint egidEntityId)
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
return implUnmgd.ContainsKey(egidEntityId);
}
@@ -189,15 +196,12 @@ namespace Svelto.ECS.Internal
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ITypeSafeDictionary Create()
- {
- return TypeSafeDictionaryFactory.Create(1);
- }
+ public ITypeSafeDictionary Create() { return TypeSafeDictionaryFactory.Create(1); }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetIndex(uint valueEntityId)
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
return this.implUnmgd.GetIndex(valueEntityId);
}
@@ -210,7 +214,7 @@ namespace Svelto.ECS.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref TValue GetOrCreate(uint idEntityId)
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
return ref this.implUnmgd.GetOrCreate(idEntityId);
}
@@ -223,7 +227,7 @@ namespace Svelto.ECS.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IBuffer GetValues(out uint count)
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
return this.implUnmgd.GetValues(out count);
}
@@ -236,7 +240,7 @@ namespace Svelto.ECS.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref TValue GetDirectValueByRef(uint key)
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
return ref this.implUnmgd.GetDirectValueByRef(key);
}
@@ -249,7 +253,7 @@ namespace Svelto.ECS.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(uint key)
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
return this.implUnmgd.ContainsKey(key);
}
@@ -259,21 +263,19 @@ namespace Svelto.ECS.Internal
}
}
- public void MoveEntityFromEngines
+ public void ExecuteEnginesSwapOrRemoveCallbacks
(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup
- , FasterDictionary, FasterList> engines, in PlatformProfiler profiler)
+ , FasterDictionary> engines, in PlatformProfiler profiler)
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
var valueIndex = this.implUnmgd.GetIndex(fromEntityGid.entityID);
ref var entity = ref this.implUnmgd.GetDirectValueByRef(valueIndex);
+ //move
if (toGroup != null)
{
- RemoveEntityComponentFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler
- , fromEntityGid);
-
var toGroupCasted = toGroup as ITypeSafeDictionary;
var previousGroup = fromEntityGid.groupID;
@@ -282,12 +284,13 @@ namespace Svelto.ECS.Internal
var index = toGroupCasted.GetIndex(toEntityID.Value.entityID);
- AddEntityComponentToEngines(engines, ref toGroupCasted.GetDirectValueByRef(index), previousGroup
- , in profiler, toEntityID.Value);
+ ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(engines, ref toGroupCasted.GetDirectValueByRef(index)
+ , previousGroup, in profiler, toEntityID.Value);
}
+ //remove
else
{
- RemoveEntityComponentFromEngines(engines, ref entity, null, in profiler, fromEntityGid);
+ ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref entity, in profiler, fromEntityGid);
}
}
else
@@ -298,9 +301,6 @@ namespace Svelto.ECS.Internal
if (toGroup != null)
{
- RemoveEntityComponentFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler
- , fromEntityGid);
-
var toGroupCasted = toGroup as ITypeSafeDictionary;
var previousGroup = fromEntityGid.groupID;
@@ -309,37 +309,38 @@ namespace Svelto.ECS.Internal
var index = toGroupCasted.GetIndex(toEntityID.Value.entityID);
- AddEntityComponentToEngines(engines, ref toGroupCasted.GetDirectValueByRef(index), previousGroup
- , in profiler, toEntityID.Value);
+ ExecuteEnginesAddOrSwapCallbacksOnSingleEntity(engines, ref toGroupCasted.GetDirectValueByRef(index)
+ , previousGroup, in profiler, toEntityID.Value);
}
else
{
- RemoveEntityComponentFromEngines(engines, ref entity, null, in profiler, fromEntityGid);
+ ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref entity, in profiler, fromEntityGid);
}
}
}
- public void RemoveEntitiesFromEngines(FasterDictionary, FasterList> engines
- , in PlatformProfiler profiler, ExclusiveGroupStruct group)
+ public void ExecuteEnginesRemoveCallbacks
+ (FasterDictionary> engines, in PlatformProfiler profiler
+ , ExclusiveGroupStruct group)
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
foreach (var value in implUnmgd)
- RemoveEntityComponentFromEngines(engines, ref implUnmgd.GetValueByRef(value.Key), null
- , in profiler, new EGID(value.Key, group));
+ ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref implUnmgd.GetValueByRef(value.Key)
+ , in profiler, new EGID(value.Key, group));
}
else
{
foreach (var value in implMgd)
- RemoveEntityComponentFromEngines(engines, ref implMgd.GetValueByRef(value.Key), null
- , in profiler, new EGID(value.Key, group));
+ ExecuteEnginesRemoveCallbackOnSingleEntity(engines, ref implMgd.GetValueByRef(value.Key)
+ , in profiler, new EGID(value.Key, group));
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntityFromDictionary(EGID fromEntityGid)
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
this.implUnmgd.Remove(fromEntityGid.entityID);
}
@@ -352,7 +353,7 @@ namespace Svelto.ECS.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetCapacity(uint size)
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
this.implUnmgd.SetCapacity(size);
}
@@ -365,7 +366,7 @@ namespace Svelto.ECS.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Trim()
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
this.implUnmgd.Trim();
}
@@ -378,7 +379,7 @@ namespace Svelto.ECS.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryFindIndex(uint entityId, out uint index)
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
return implUnmgd.TryFindIndex(entityId, out index);
}
@@ -388,10 +389,28 @@ namespace Svelto.ECS.Internal
}
}
+ public void KeysEvaluator(Action action)
+ {
+ if (IsUnmanaged)
+ {
+ foreach (var key in implUnmgd.keys)
+ {
+ action(key);
+ }
+ }
+ else
+ {
+ foreach (var key in implMgd.keys)
+ {
+ action(key);
+ }
+ }
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(uint entityId, out TValue item)
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
return this.implUnmgd.TryGetValue(entityId, out item);
}
@@ -406,13 +425,13 @@ namespace Svelto.ECS.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
- return this.implUnmgd.count;
+ return (uint) this.implUnmgd.count;
}
else
{
- return this.implMgd.count;
+ return (uint) this.implMgd.count;
}
}
}
@@ -422,7 +441,7 @@ namespace Svelto.ECS.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
- if (_isUmanaged)
+ if (IsUnmanaged)
{
return ref this.implUnmgd.GetValueByRef(idEntityId);
}
@@ -433,36 +452,36 @@ namespace Svelto.ECS.Internal
}
}
- static void RemoveEntityComponentFromEngines
- (FasterDictionary, FasterList> engines, ref TValue entity, uint? previousGroup
+ static void ExecuteEnginesRemoveCallbackOnSingleEntity
+ (FasterDictionary> engines, ref TValue entity
, in PlatformProfiler profiler, EGID egid)
{
- if (!engines.TryGetValue(new RefWrapper(_type), out var entityComponentsEngines))
+ if (!engines.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines))
return;
- if (previousGroup == null)
- for (var i = 0; i < entityComponentsEngines.count; i++)
- try
+ for (var i = 0; i < entityComponentsEngines.count; i++)
+ try
+ {
+ using (profiler.Sample(entityComponentsEngines[i], _typeName))
{
- using (profiler.Sample(entityComponentsEngines[i], _typeName))
- {
- (entityComponentsEngines[i] as IReactOnAddAndRemove).Remove(ref entity, egid);
- }
+ (entityComponentsEngines[i] as IReactOnAddAndRemove).Remove(ref entity, egid);
}
- catch
- {
- Svelto.Console.LogError("Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()));
+ }
+ catch
+ {
+ Svelto.Console.LogError(
+ "Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()));
- throw;
- }
+ throw;
+ }
}
- void AddEntityComponentToEngines
- (FasterDictionary, FasterList> engines, ref TValue entity
+ void ExecuteEnginesAddOrSwapCallbacksOnSingleEntity
+ (FasterDictionary> engines, ref TValue entity
, ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler, EGID egid)
{
//get all the engines linked to TValue
- if (!engines.TryGetValue(new RefWrapper(_type), out var entityComponentsEngines))
+ if (!engines.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines))
return;
if (previousGroup == null)
@@ -477,7 +496,8 @@ namespace Svelto.ECS.Internal
}
catch
{
- Svelto.Console.LogError("Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()));
+ Svelto.Console.LogError(
+ "Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()));
throw;
}
@@ -495,7 +515,8 @@ namespace Svelto.ECS.Internal
}
catch (Exception)
{
- Svelto.Console.LogError("Code crashed inside MoveTo callback ".FastConcat(typeof(TValue).ToString()));
+ Svelto.Console.LogError(
+ "Code crashed inside MoveTo callback ".FastConcat(typeof(TValue).ToString()));
throw;
}
@@ -504,11 +525,11 @@ namespace Svelto.ECS.Internal
public void Dispose()
{
- if (_isUmanaged)
+ if (IsUnmanaged)
implUnmgd.Dispose();
else
implMgd.Dispose();
-
+
GC.SuppressFinalize(this);
}
}
diff --git a/Svelto.ECS/DataStructures/AtomicNativeBags.cs b/Svelto.ECS/DataStructures/Unmanaged/AtomicNativeBags.cs
similarity index 65%
rename from Svelto.ECS/DataStructures/AtomicNativeBags.cs
rename to Svelto.ECS/DataStructures/Unmanaged/AtomicNativeBags.cs
index c858617..3d9208e 100644
--- a/Svelto.ECS/DataStructures/AtomicNativeBags.cs
+++ b/Svelto.ECS/DataStructures/Unmanaged/AtomicNativeBags.cs
@@ -1,40 +1,33 @@
+#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.Unity
+namespace Svelto.ECS.DataStructures
{
- ///
- /// A collection of intended to allow one buffer per thread.
- /// from: https://github.com/jeffvella/UnityEcsEvents/blob/develop/Runtime/MultiAppendBuffer.cs
- ///
public unsafe struct AtomicNativeBags:IDisposable
{
- public const int DefaultThreadIndex = -1;
- const int MinThreadIndex = DefaultThreadIndex;
-
-#if UNITY_COLLECTIONS
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
-#endif
- readonly NativeBag* _data;
+
+ NativeBag* _data;
readonly Allocator _allocator;
readonly uint _threadsCount;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool IsInvalidThreadIndex(int index) => index < MinThreadIndex || index > _threadsCount;
+ public uint count => _threadsCount;
- public AtomicNativeBags(Common.Allocator allocator, uint threadsCount)
+ public AtomicNativeBags(Allocator allocator)
{
- _allocator = allocator;
- _threadsCount = threadsCount;
+ _allocator = allocator;
+ _threadsCount = JobsUtility.MaxJobThreadCount + 1;
var bufferSize = MemoryUtilities.SizeOf();
var bufferCount = _threadsCount;
var allocationSize = bufferSize * bufferCount;
var ptr = (byte*)MemoryUtilities.Alloc((uint) allocationSize, allocator);
- MemoryUtilities.MemClear((IntPtr) ptr, (uint) allocationSize);
+ // MemoryUtilities.MemClear((IntPtr) ptr, (uint) allocationSize);
for (int i = 0; i < bufferCount; i++)
{
@@ -49,22 +42,30 @@ namespace Svelto.ECS.DataStructures.Unity
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref NativeBag GetBuffer(int index)
{
+ if (_data == null)
+ throw new Exception("using invalid AtomicNativeBags");
+
return ref MemoryUtilities.ArrayElementAsRef((IntPtr) _data, index);
}
- public uint count => _threadsCount;
-
public void Dispose()
{
+ if (_data == null)
+ throw new Exception("using invalid AtomicNativeBags");
+
for (int i = 0; i < _threadsCount; i++)
{
GetBuffer(i).Dispose();
}
MemoryUtilities.Free((IntPtr) _data, _allocator);
+ _data = null;
}
public void Clear()
{
+ if (_data == null)
+ throw new Exception("using invalid AtomicNativeBags");
+
for (int i = 0; i < _threadsCount; i++)
{
GetBuffer(i).Clear();
@@ -72,3 +73,4 @@ namespace Svelto.ECS.DataStructures.Unity
}
}
}
+#endif
\ No newline at end of file
diff --git a/Svelto.ECS/DataStructures/Unmanaged/NativeBag.cs b/Svelto.ECS/DataStructures/Unmanaged/NativeBag.cs
new file mode 100644
index 0000000..96e89c6
--- /dev/null
+++ b/Svelto.ECS/DataStructures/Unmanaged/NativeBag.cs
@@ -0,0 +1,281 @@
+#if DEBUG && !PROFILE_SVELTO
+#define ENABLE_DEBUG_CHECKS
+#endif
+
+#if DEBUG && !PROFILE_SVELTO
+//#define ENABLE_THREAD_SAFE_CHECKS
+#endif
+
+using System;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using Svelto.Common;
+
+namespace Svelto.ECS.DataStructures
+{
+ ///
+ /// Burst friendly RingBuffer on steroid:
+ /// it can: Enqueue/Dequeue, it wraps 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 T. 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 "copyable"
+ /// 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
+ ///
+ public struct NativeBag : IDisposable
+ {
+ public uint count
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ unsafe
+ {
+ BasicTests();
+#if ENABLE_THREAD_SAFE_CHECKS
+ try
+ {
+#endif
+ return _queue->size;
+#if ENABLE_THREAD_SAFE_CHECKS
+ }
+ finally
+ {
+ Volatile.Write(ref _threadSentinel, 0);
+ }
+#endif
+ }
+ }
+ }
+
+ public uint capacity
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ unsafe
+ {
+ BasicTests();
+#if ENABLE_THREAD_SAFE_CHECKS
+ try
+ {
+#endif
+ return _queue->capacity;
+#if ENABLE_THREAD_SAFE_CHECKS
+ }
+ finally
+ {
+ Volatile.Write(ref _threadSentinel, 0);
+ }
+#endif
+ }
+ }
+ }
+
+ public NativeBag(Allocator allocator)
+ {
+ unsafe
+ {
+ var sizeOf = MemoryUtilities.SizeOf();
+ var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator);
+
+ //clear to nullify the pointers
+ //MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf);
+ listData->allocator = allocator;
+ _queue = listData;
+#if ENABLE_THREAD_SAFE_CHECKS
+ _threadSentinel = 0;
+#endif
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool IsEmpty()
+ {
+ unsafe
+ {
+ BasicTests();
+#if ENABLE_THREAD_SAFE_CHECKS
+ try
+ {
+#endif
+ if (_queue == null || _queue->ptr == null)
+ return true;
+#if ENABLE_THREAD_SAFE_CHECKS
+ }
+ finally
+ {
+ Volatile.Write(ref _threadSentinel, 0);
+ }
+#endif
+ }
+
+ return count == 0;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public unsafe void Dispose()
+ {
+ if (_queue != null)
+ {
+#if ENABLE_THREAD_SAFE_CHECKS
+ //todo: this must be unit tested
+ if (Interlocked.CompareExchange(ref _threadSentinel, 1, 0) != 0)
+ throw new Exception("NativeBag is not thread safe, reading and writing operations can happen" +
+ "on different threads, but not simultaneously");
+
+ try
+ {
+#endif
+ _queue->Dispose();
+ MemoryUtilities.Free((IntPtr) _queue, _queue->allocator);
+ _queue = null;
+#if ENABLE_THREAD_SAFE_CHECKS
+ }
+ finally
+ {
+ Volatile.Write(ref _threadSentinel, 0);
+ }
+#endif
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ref T ReserveEnqueue(out UnsafeArrayIndex index) where T : struct
+ {
+ unsafe
+ {
+ BasicTests();
+
+ var sizeOf = MemoryUtilities.SizeOf();
+ if (_queue->space - sizeOf < 0)
+ _queue->Realloc((uint) ((_queue->capacity + MemoryUtilities.Align4((uint) sizeOf)) * 2.0f));
+
+#if ENABLE_THREAD_SAFE_CHECKS
+ try
+ {
+#endif
+
+ return ref _queue->Reserve(out index);
+#if ENABLE_THREAD_SAFE_CHECKS
+ }
+ finally
+ {
+ Volatile.Write(ref _threadSentinel, 0);
+ }
+#endif
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Enqueue(in T item) where T : struct
+ {
+ unsafe
+ {
+ BasicTests();
+
+#if ENABLE_THREAD_SAFE_CHECKS
+ try
+ {
+#endif
+ var sizeOf = MemoryUtilities.SizeOf();
+ if (_queue->space - sizeOf < 0)
+ _queue->Realloc((uint) ((_queue->capacity + MemoryUtilities.Align4((uint) sizeOf)) * 2.0f));
+
+ _queue->Write(item);
+#if ENABLE_THREAD_SAFE_CHECKS
+ }
+ finally
+ {
+ Volatile.Write(ref _threadSentinel, 0);
+ }
+#endif
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Clear()
+ {
+ unsafe
+ {
+ BasicTests();
+#if ENABLE_THREAD_SAFE_CHECKS
+ try
+ {
+#endif
+ _queue->Clear();
+#if ENABLE_THREAD_SAFE_CHECKS
+ }
+ finally
+ {
+ Volatile.Write(ref _threadSentinel, 0);
+ }
+#endif
+ }
+ }
+
+ public T Dequeue() where T : struct
+ {
+ unsafe
+ {
+ BasicTests();
+#if ENABLE_THREAD_SAFE_CHECKS
+ try
+ {
+#endif
+ return _queue->Read();
+#if ENABLE_THREAD_SAFE_CHECKS
+ }
+ finally
+ {
+ Volatile.Write(ref _threadSentinel, 0);
+ }
+#endif
+ }
+ }
+
+ internal ref T AccessReserved(UnsafeArrayIndex reserverIndex) where T : struct
+ {
+ unsafe
+ {
+ BasicTests();
+#if ENABLE_THREAD_SAFE_CHECKS
+ try
+ {
+#endif
+ return ref _queue->AccessReserved(reserverIndex);
+#if ENABLE_THREAD_SAFE_CHECKS
+ }
+ finally
+ {
+ Volatile.Write(ref _threadSentinel, 0);
+ }
+#endif
+ }
+ }
+
+ [Conditional("ENABLE_DEBUG_CHECKS")]
+ unsafe void BasicTests()
+ {
+ if (_queue == null)
+ throw new Exception("SimpleNativeArray: null-access");
+#if ENABLE_THREAD_SAFE_CHECKS
+ todo: this must be unit tested
+ if (Interlocked.CompareExchange(ref _threadSentinel, 1, 0) != 0)
+ throw new Exception("NativeBag is not thread safe, reading and writing operations can happen"
+ + "on different threads, but not simultaneously");
+#endif
+ }
+
+#if ENABLE_THREAD_SAFE_CHECKS
+ int _threadSentinel;
+#endif
+#if UNITY_NATIVE
+ [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
+#endif
+ unsafe UnsafeBlob* _queue;
+ }
+}
\ No newline at end of file
diff --git a/Svelto.ECS/DataStructures/NativeDynamicArray.cs b/Svelto.ECS/DataStructures/Unmanaged/NativeDynamicArray.cs
similarity index 76%
rename from Svelto.ECS/DataStructures/NativeDynamicArray.cs
rename to Svelto.ECS/DataStructures/Unmanaged/NativeDynamicArray.cs
index 83efdd2..86771e8 100644
--- a/Svelto.ECS/DataStructures/NativeDynamicArray.cs
+++ b/Svelto.ECS/DataStructures/Unmanaged/NativeDynamicArray.cs
@@ -7,13 +7,17 @@ namespace Svelto.ECS.DataStructures
{
public struct NativeDynamicArray : IDisposable
{
-#if UNITY_COLLECTIONS
- [global::Unity.Burst.NoAlias] [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
-#endif
- unsafe UnsafeArray* _list;
-#if DEBUG && !PROFILE_SVELTO
- int hashType;
-#endif
+ public bool isValid
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ unsafe
+ {
+ return _list != null;
+ }
+ }
+ }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Count() where T : struct
@@ -23,8 +27,8 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
- if (hashType != TypeHash.hash)
- throw new Exception("NativeDynamicArray: not excepted type used");
+ if (_hashType != TypeHash.hash)
+ throw new Exception($"NativeDynamicArray: not expected type used");
#endif
return (_list->count / MemoryUtilities.SizeOf());
@@ -39,8 +43,8 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
- if (hashType != TypeHash.hash)
- throw new Exception("NativeDynamicArray: not excepted type used");
+ if (_hashType != TypeHash.hash)
+ throw new Exception("NativeDynamicArray: not expected type used");
#endif
return (_list->capacity / MemoryUtilities.SizeOf());
@@ -51,20 +55,21 @@ namespace Svelto.ECS.DataStructures
{
unsafe
{
- var rtnStruc = new NativeDynamicArray();
#if DEBUG && !PROFILE_SVELTO
- rtnStruc.hashType = TypeHash.hash;
+ var rtnStruc = new NativeDynamicArray {_hashType = TypeHash.hash};
+#else
+ NativeDynamicArray rtnStruc = default;
#endif
var sizeOf = MemoryUtilities.SizeOf();
- uint pointerSize = (uint) MemoryUtilities.SizeOf();
- UnsafeArray* listData = (UnsafeArray*) MemoryUtilities.Alloc(pointerSize, allocator);
+ uint structSize = (uint) MemoryUtilities.SizeOf();
+ UnsafeArray* listData = (UnsafeArray*) MemoryUtilities.Alloc(structSize, allocator);
//clear to nullify the pointers
- MemoryUtilities.MemClear((IntPtr) listData, pointerSize);
+ //MemoryUtilities.MemClear((IntPtr) listData, structSize);
- listData->allocator = allocator;
- listData->Realloc((uint) (newLength * sizeOf));
+ rtnStruc._allocator = allocator;
+ listData->Realloc((uint) (newLength * sizeOf), allocator);
rtnStruc._list = listData;
@@ -80,8 +85,8 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
- if (hashType != TypeHash.hash)
- throw new Exception("NativeDynamicArray: not excepted type used");
+ if (_hashType != TypeHash.hash)
+ throw new Exception("NativeDynamicArray: not expected type used");
if (index >= Count())
throw new Exception($"NativeDynamicArray: out of bound access, index {index} count {Count()}");
#endif
@@ -97,8 +102,8 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
- if (hashType != TypeHash.hash)
- throw new Exception("NativeDynamicArray: not excepted type used");
+ if (_hashType != TypeHash.hash)
+ throw new Exception("NativeDynamicArray: not expected type used");
if (index >= Capacity())
throw new Exception($"NativeDynamicArray: out of bound access, index {index} capacity {Capacity()}");
#endif
@@ -112,7 +117,7 @@ namespace Svelto.ECS.DataStructures
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
#endif
- _list->Dispose();
+ _list->Dispose(_allocator);
_list = null;
}
@@ -124,18 +129,42 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
- if (hashType != TypeHash.hash)
- throw new Exception("NativeDynamicArray: not excepted type used");
+ if (_hashType != TypeHash.hash)
+ throw new Exception("NativeDynamicArray: not expected type used");
#endif
var structSize = (uint) MemoryUtilities.SizeOf();
if (_list->space - (int) structSize < 0)
- _list->Realloc((uint) (((uint) ((Count() + 1) * 1.5f) * (float) structSize)));
+ _list->Realloc((uint) (((uint) ((Count() + 1) * 1.5f) * (float) structSize)), _allocator);
_list->Add(item);
}
}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ref T AddAt(uint index) where T : struct
+ {
+ unsafe
+ {
+#if DEBUG && !PROFILE_SVELTO
+ if (_list == null)
+ throw new Exception("NativeDynamicArray: null-access");
+ if (_hashType != TypeHash.hash)
+ throw new Exception("NativeDynamicArray: not expected type used");
+#endif
+ var structSize = (uint) MemoryUtilities.SizeOf();
+ if (index >= Capacity())
+ _list->Realloc((uint) (((index + 1) * 1.5f) * structSize), _allocator);
+
+ var writeIndex = (index + 1) * structSize;
+ if (_list->count < writeIndex)
+ _list->SetCountTo(writeIndex);
+
+ return ref _list->Get(index);
+ }
+ }
+
public void Grow(uint newCapacity) where T : struct
{
unsafe
@@ -143,15 +172,15 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
- if (hashType != TypeHash.hash)
- throw new Exception("NativeDynamicArray: not excepted type used");
+ if (_hashType != TypeHash.hash)
+ throw new Exception("NativeDynamicArray: not expected type used");
if (newCapacity <= Capacity())
throw new Exception("New capacity must be greater than current one");
#endif
uint structSize = (uint) MemoryUtilities.SizeOf();
uint size = (uint) (newCapacity * structSize);
- _list->Realloc((uint) size);
+ _list->Realloc((uint) size, _allocator);
}
}
@@ -162,8 +191,8 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
- if (hashType != TypeHash.hash)
- throw new Exception("NativeDynamicArray: not excepted type used");
+ if (_hashType != TypeHash.hash)
+ throw new Exception("NativeDynamicArray: not expected type used");
#endif
uint structSize = (uint) MemoryUtilities.SizeOf();
uint size = (uint) (count * structSize);
@@ -180,8 +209,8 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
- if (hashType != TypeHash.hash)
- throw new Exception("NativeDynamicArray: not excepted type used");
+ if (_hashType != TypeHash.hash)
+ throw new Exception("NativeDynamicArray: not expected type used");
var structSize = (uint) MemoryUtilities.SizeOf();
@@ -200,15 +229,15 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
- if (hashType != TypeHash.hash)
- throw new Exception("NativeDynamicArray: not excepted type used");
+ if (_hashType != TypeHash.hash)
+ throw new Exception("NativeDynamicArray: not expected type used");
if (Count() == 0)
throw new Exception("NativeDynamicArray: empty array invalid operation");
#endif
- var count = Count() - 1;
- if (index < count)
+ var indexToMove = Count() - 1;
+ if (index < indexToMove)
{
- Set(index, Get((uint) count));
+ Set(index, Get((uint) indexToMove));
}
_list->Pop();
@@ -216,7 +245,7 @@ namespace Svelto.ECS.DataStructures
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Clear()
+ public void FastClear()
{
unsafe
{
@@ -233,8 +262,8 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
- if (hashType != TypeHash.hash)
- throw new Exception("NativeDynamicArray: not excepted type used");
+ if (_hashType != TypeHash.hash)
+ throw new Exception("NativeDynamicArray: not expected type used");
#endif
return (T*) _list->ptr;
@@ -247,8 +276,8 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
- if (hashType != TypeHash.hash)
- throw new Exception("NativeDynamicArray: not excepted type used");
+ if (_hashType != TypeHash.hash)
+ throw new Exception("NativeDynamicArray: not expected type used");
#endif
return (IntPtr) _list->ptr;
@@ -262,8 +291,8 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
- if (hashType != TypeHash.hash)
- throw new Exception("NativeDynamicArray: not excepted type used");
+ if (_hashType != TypeHash.hash)
+ throw new Exception("NativeDynamicArray: not expected type used");
#endif
var count = Count();
@@ -286,8 +315,8 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
- if (hashType != TypeHash.hash)
- throw new Exception("NativeDynamicArray: not excepted type used");
+ if (_hashType != TypeHash.hash)
+ throw new Exception("NativeDynamicArray: not expected type used");
#endif
var capacity = Capacity();
var lengthToCopyInBytes = capacity * MemoryUtilities.SizeOf();
@@ -309,8 +338,8 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
- if (hashType != TypeHash.hash)
- throw new Exception("NativeDynamicArray: not excepted type used");
+ if (_hashType != TypeHash.hash)
+ throw new Exception("NativeDynamicArray: not expected type used");
#endif
var sizeOf = MemoryUtilities.SizeOf();
@@ -328,42 +357,14 @@ namespace Svelto.ECS.DataStructures
MemoryUtilities.MemClear((IntPtr) _list->ptr, (uint) _list->capacity);
}
}
- }
-
- public ref struct NativeDynamicArrayCast where T : struct
- {
- NativeDynamicArray _array;
-
- public NativeDynamicArrayCast(NativeDynamicArray array) : this() { _array = array; }
-
- public int count
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _array.Count();
- }
-
- public ref T this[int index]
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => ref _array.Get((uint) index);
- }
-
- public ref T this[uint index]
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => ref _array.Get(index);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Add(in T id) { _array.Add(id); }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void UnorderedRemoveAt(uint index) { _array.UnorderedRemoveAt(index); }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void RemoveAt(uint index) { _array.RemoveAt(index); }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Clear() { _array.Clear(); }
+
+#if UNITY_NATIVE
+ [global::Unity.Burst.NoAlias] [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
+#endif
+ unsafe UnsafeArray* _list;
+#if DEBUG && !PROFILE_SVELTO
+ int _hashType;
+#endif
+ Allocator _allocator;
}
}
\ No newline at end of file
diff --git a/Svelto.ECS/DataStructures/Unmanaged/NativeDynamicArrayCast.cs b/Svelto.ECS/DataStructures/Unmanaged/NativeDynamicArrayCast.cs
new file mode 100644
index 0000000..a34078b
--- /dev/null
+++ b/Svelto.ECS/DataStructures/Unmanaged/NativeDynamicArrayCast.cs
@@ -0,0 +1,48 @@
+using System.Runtime.CompilerServices;
+
+namespace Svelto.ECS.DataStructures
+{
+ public struct NativeDynamicArrayCast where T : struct
+ {
+ public NativeDynamicArrayCast(NativeDynamicArray array) : this() { _array = array; }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int Count() => _array.Count();
+
+ public int count => _array.Count();
+
+ public ref T this[int index]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => ref _array.Get((uint) index);
+ }
+
+ public ref T this[uint index]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => ref _array.Get(index);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Add(in T id) { _array.Add(id); }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void UnorderedRemoveAt(uint index) { _array.UnorderedRemoveAt(index); }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void RemoveAt(uint index) { _array.RemoveAt(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(lastIndex); }
+
+ public bool isValid => _array.isValid;
+
+ NativeDynamicArray _array;
+ }
+}
\ No newline at end of file
diff --git a/Svelto.ECS/DataStructures/NativeDynamicArrayUnityExtension.cs b/Svelto.ECS/DataStructures/Unmanaged/NativeDynamicArrayUnityExtension.cs
similarity index 96%
rename from Svelto.ECS/DataStructures/NativeDynamicArrayUnityExtension.cs
rename to Svelto.ECS/DataStructures/Unmanaged/NativeDynamicArrayUnityExtension.cs
index 3688940..3a0dd90 100644
--- a/Svelto.ECS/DataStructures/NativeDynamicArrayUnityExtension.cs
+++ b/Svelto.ECS/DataStructures/Unmanaged/NativeDynamicArrayUnityExtension.cs
@@ -1,4 +1,4 @@
-#if UNITY_COLLECTIONS
+#if UNITY_NATIVE
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
diff --git a/Svelto.ECS/DataStructures/SharedNativeInt.cs b/Svelto.ECS/DataStructures/Unmanaged/SharedNativeInt.cs
similarity index 56%
rename from Svelto.ECS/DataStructures/SharedNativeInt.cs
rename to Svelto.ECS/DataStructures/Unmanaged/SharedNativeInt.cs
index b5269e6..e180084 100644
--- a/Svelto.ECS/DataStructures/SharedNativeInt.cs
+++ b/Svelto.ECS/DataStructures/Unmanaged/SharedNativeInt.cs
@@ -1,22 +1,35 @@
using System;
using System.Runtime.InteropServices;
using System.Threading;
+using Svelto.Common;
namespace Svelto.ECS.DataStructures
{
public struct SharedNativeInt: IDisposable
{
-#if UNITY_COLLECTIONS
+#if UNITY_NATIVE
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
unsafe int* data;
- public static SharedNativeInt Create(int t)
+ 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.data = (int*) Marshal.AllocHGlobal(sizeof(int));
+ current._allocator = allocator;
+ current.data = (int*) MemoryUtilities.Alloc(sizeof(int), allocator);
*current.data = t;
return current;
@@ -31,7 +44,6 @@ namespace Svelto.ECS.DataStructures
if (t.data == null)
throw new Exception("using disposed SharedInt");
#endif
-
return *t.data;
}
}
@@ -40,12 +52,11 @@ namespace Svelto.ECS.DataStructures
{
unsafe
{
-#if DEBUG && !PROFILE_SVELTO
- if (data == null)
- throw new Exception("disposing already disposed data");
-#endif
- Marshal.FreeHGlobal((IntPtr) data);
- data = null;
+ if (data != null)
+ {
+ MemoryUtilities.Free((IntPtr) data, _allocator);
+ data = null;
+ }
}
}
@@ -74,5 +85,31 @@ namespace Svelto.ECS.DataStructures
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 void Set(int val)
+ {
+ unsafe
+ {
+#if DEBUG && !PROFILE_SVELTO
+ if (data == null)
+ throw new Exception("null-access");
+#endif
+
+ Volatile.Write(ref *data, val);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Svelto.ECS/DataStructures/NativeBag.cs b/Svelto.ECS/DataStructures/Unmanaged/ThreadSafeNativeBag.cs
similarity index 55%
rename from Svelto.ECS/DataStructures/NativeBag.cs
rename to Svelto.ECS/DataStructures/Unmanaged/ThreadSafeNativeBag.cs
index e8a5ab9..d0517ee 100644
--- a/Svelto.ECS/DataStructures/NativeBag.cs
+++ b/Svelto.ECS/DataStructures/Unmanaged/ThreadSafeNativeBag.cs
@@ -1,11 +1,14 @@
+#if later
using System;
using System.Runtime.CompilerServices;
+using System.Threading;
using Svelto.Common;
+using Svelto.Utilities;
namespace Svelto.ECS.DataStructures
{
///
- /// Burst friendly RingBuffer on steroid:
+ /// Burst friendly Ring Buffer on steroid:
/// it can: Enqueue/Dequeue, it wraps 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 T. Just be sure that you dequeue what you queue! No check on type
@@ -15,25 +18,8 @@ namespace Svelto.ECS.DataStructures
/// 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
///
- public struct NativeBag : IDisposable
+ public struct ThreadSafeNativeBag : IDisposable
{
-#if UNITY_COLLECTIONS
- [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
-#endif
- unsafe UnsafeBlob* _queue;
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool IsEmpty()
- {
- unsafe
- {
- if (_queue == null || _queue->ptr == null)
- return true;
- }
-
- return count == 0;
- }
-
public uint count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -68,7 +54,7 @@ namespace Svelto.ECS.DataStructures
}
}
- public NativeBag(Allocator allocator)
+ public ThreadSafeNativeBag(Allocator allocator)
{
unsafe
{
@@ -76,36 +62,51 @@ namespace Svelto.ECS.DataStructures
var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator);
//clear to nullify the pointers
- MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf);
+ //MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf);
listData->allocator = allocator;
_queue = listData;
}
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public unsafe void Dispose()
+ _writingGuard = 0;
+ }
+
+ public ThreadSafeNativeBag(Allocator allocator, uint capacity)
{
- if (_queue != null)
+ unsafe
{
- _queue->Dispose();
- _queue = null;
+ var sizeOf = MemoryUtilities.SizeOf();
+ var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator);
+
+ //clear to nullify the pointers
+ //MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf);
+ listData->allocator = allocator;
+ _queue = listData;
+ _queue->Realloc(capacity);
}
+
+ _writingGuard = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ref T ReserveEnqueue(out UnsafeArrayIndex index) where T : struct
+ public bool IsEmpty()
{
unsafe
{
-#if DEBUG && !PROFILE_SVELTO
- if (_queue == null)
- throw new Exception("SimpleNativeArray: null-access");
-#endif
- var sizeOf = MemoryUtilities.SizeOf();
- if (_queue->space - sizeOf < 0)
- _queue->Realloc((uint) ((_queue->capacity + sizeOf) * 2.0f));
+ if (_queue == null || _queue->ptr == null)
+ return true;
+ }
- return ref _queue->Reserve(out index);
+ return count == 0;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public unsafe void Dispose()
+ {
+ if (_queue != null)
+ {
+ _queue->Dispose();
+ MemoryUtilities.Free((IntPtr) _queue, _queue->allocator);
+ _queue = null;
}
}
@@ -118,11 +119,46 @@ namespace Svelto.ECS.DataStructures
if (_queue == null)
throw new Exception("SimpleNativeArray: null-access");
#endif
- var sizeOf = MemoryUtilities.SizeOf();
- if (_queue->space - sizeOf < 0)
- _queue->Realloc((uint) ((_queue->capacity + MemoryUtilities.Align4((uint) sizeOf)) * 2.0f));
+ var sizeOf = MemoryUtilities.SizeOf();
+ var alignedSize = (uint) MemoryUtilities.SizeOfAligned();
+
+ Interlocked.MemoryBarrier();
+ Reset:
+ var oldCapacity = _queue->capacity;
+ var spaceleft = oldCapacity - (_queue->_writeIndex - _queue->_readIndex) - sizeOf;
+
+ while (spaceleft < 0)
+ {
+ //if _writingGuard is not equal to 0, it means that another thread increased the
+ //value so it's possible the reallocing is already happening OR it means that
+ //writing are still in progress and we must be sure that are all flushed first
+ if (Interlocked.CompareExchange(ref _writingGuard, 1, 0) != 0)
+ {
+ ThreadUtility.Yield();
+ goto Reset;
+ }
+
+ var newCapacity = (uint) ((oldCapacity + alignedSize) * 2.0f);
+ Svelto.Console.Log($"realloc {newCapacity}");
+ _queue->Realloc(newCapacity);
+
+ Volatile.Write(ref _writingGuard, 0);
+ }
+
+ int writeIndex;
+
+ //look for the first available slot to write in
+ writeIndex = _queue->_writeIndex;
+ if (Interlocked.CompareExchange(ref _queue->_writeIndex, (int) (writeIndex + alignedSize)
+ , writeIndex) != writeIndex)
+ {
+ ThreadUtility.Yield();
+ goto Reset;
+ }
- _queue->Write(item);
+ Interlocked.Increment(ref _writingGuard);
+ _queue->Write(item, (uint) writeIndex);
+ Interlocked.Decrement(ref _writingGuard);
}
}
@@ -146,17 +182,13 @@ namespace Svelto.ECS.DataStructures
return _queue->Read();
}
}
-
- public ref T AccessReserved(UnsafeArrayIndex reserverIndex) where T : struct
- {
- unsafe
- {
-#if DEBUG && !PROFILE_SVELTO
- if (_queue == null)
- throw new Exception("SimpleNativeArray: null-access");
+
+#if UNITY_NATIVE
+ [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
- return ref _queue->AccessReserved(reserverIndex);
- }
- }
+ unsafe UnsafeBlob* _queue;
+
+ int _writingGuard;
}
-}
\ No newline at end of file
+}
+#endif
\ No newline at end of file
diff --git a/Svelto.ECS/DataStructures/UnsafeArray.cs b/Svelto.ECS/DataStructures/Unmanaged/UnsafeArray.cs
similarity index 76%
rename from Svelto.ECS/DataStructures/UnsafeArray.cs
rename to Svelto.ECS/DataStructures/Unmanaged/UnsafeArray.cs
index a995dc6..a70c112 100644
--- a/Svelto.ECS/DataStructures/UnsafeArray.cs
+++ b/Svelto.ECS/DataStructures/Unmanaged/UnsafeArray.cs
@@ -4,7 +4,7 @@ using Svelto.Common;
namespace Svelto.ECS.DataStructures
{
- struct UnsafeArray : IDisposable
+ struct UnsafeArray
{
internal unsafe byte* ptr => _ptr;
@@ -17,9 +17,6 @@ namespace Svelto.ECS.DataStructures
//expressed in bytes
internal int space => capacity - count;
- ///
- ///
- internal Allocator allocator;
#if DEBUG && !PROFILE_SVELTO
#pragma warning disable 649
internal uint id;
@@ -31,6 +28,11 @@ namespace Svelto.ECS.DataStructures
{
unsafe
{
+#if DEBUG && !PROFILE_SVELTO
+ uint sizeOf = (uint) MemoryUtilities.SizeOf();
+ if (index + sizeOf > _writeIndex)
+ throw new Exception("no reading authorized");
+#endif
return ref Unsafe.AsRef(Unsafe.Add(ptr, (int) index));
}
}
@@ -73,40 +75,34 @@ namespace Svelto.ECS.DataStructures
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Pop() where T : struct
+ public ref T Pop() where T : struct
{
- var structSize = MemoryUtilities.SizeOf();
+ unsafe
+ {
+ var structSize = MemoryUtilities.SizeOf();
- _writeIndex -= (uint)structSize;
+ _writeIndex -= (uint)structSize;
+
+ return ref Unsafe.AsRef(ptr + _writeIndex);
+ }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal void Realloc(uint newCapacity)
+ internal void Realloc(uint newCapacity, Allocator allocator)
{
unsafe
{
- byte* newPointer = null;
-#if DEBUG && !PROFILE_SVELTO
- if (_capacity > 0 && newCapacity <= _capacity)
- throw new Exception("new capacity must be bigger than current");
-#endif
- if (newCapacity >= 0)
- {
- newPointer = (byte*) MemoryUtilities.Alloc(newCapacity, allocator);
- if (count > 0)
- Unsafe.CopyBlock(newPointer, ptr, (uint) count);
- }
-
- if (ptr != null)
- MemoryUtilities.Free((IntPtr) ptr, allocator);
+ if (_ptr == null)
+ _ptr = (byte*) MemoryUtilities.Alloc(newCapacity, allocator);
+ else
+ _ptr = (byte*) MemoryUtilities.Realloc((IntPtr) _ptr, (uint) count, newCapacity, allocator);
- _ptr = newPointer;
_capacity = newCapacity;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Dispose()
+ public void Dispose(Allocator allocator)
{
unsafe
{
@@ -131,7 +127,7 @@ namespace Svelto.ECS.DataStructures
_writeIndex = count;
}
-#if UNITY_COLLECTIONS
+#if UNITY_NATIVE
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
unsafe byte* _ptr;
diff --git a/Svelto.ECS/DataStructures/UnsafeBlob.cs b/Svelto.ECS/DataStructures/Unmanaged/UnsafeBlob.cs
similarity index 84%
rename from Svelto.ECS/DataStructures/UnsafeBlob.cs
rename to Svelto.ECS/DataStructures/Unmanaged/UnsafeBlob.cs
index a658f02..591d5c4 100644
--- a/Svelto.ECS/DataStructures/UnsafeBlob.cs
+++ b/Svelto.ECS/DataStructures/Unmanaged/UnsafeBlob.cs
@@ -23,7 +23,7 @@ namespace Svelto.ECS.DataStructures
internal uint capacity { get; private set; }
//expressed in bytes
- internal uint size => _writeIndex - _readIndex;
+ internal uint size => (uint)_writeIndex - _readIndex;
//expressed in bytes
internal uint space => capacity - size;
@@ -31,7 +31,7 @@ namespace Svelto.ECS.DataStructures
///
///
internal Allocator allocator;
-
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Write(in T item) where T : struct
{
@@ -57,22 +57,53 @@ namespace Svelto.ECS.DataStructures
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), byteCountToEnd);
+ 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
- , restCount);
+ , (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
- var paddedStructSize = MemoryUtilities.Align4(structSize);
+ int paddedStructSize = (int) MemoryUtilities.Align4(structSize);
_writeIndex += paddedStructSize;
}
}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal void Write(in T item, uint writeIndex) where T : struct
+ {
+ unsafe
+ {
+ var structSize = (uint) MemoryUtilities.SizeOf();
+
+ //the idea is, considering the wrap, a read pointer must always be behind a writer pointer
+ var writeHead = writeIndex % capacity;
+
+ if (writeHead + structSize <= capacity)
+ {
+ Unsafe.Write(ptr + writeHead, item);
+ }
+ else //copy with wrap, will start to copy and wrap for the reminder
+ {
+ 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), byteCountToEnd);
+
+ var restCount = structSize - byteCountToEnd;
+
+ //read and copy the remainder
+ Unsafe.CopyBlock(ptr, (byte*) Unsafe.AsPointer(ref localCopyToAvoidGcIssues) + byteCountToEnd
+ , restCount);
+ }
+ }
+ }
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// //ToDo: remove this and create an UnsafeBlobUnaligned, used on NativeRingBuffer where T cannot change
@@ -169,10 +200,10 @@ namespace Svelto.ECS.DataStructures
index = new UnsafeArrayIndex
{
capacity = capacity
- , index = _writeIndex
+ , index = (uint)_writeIndex
};
- var align4 = MemoryUtilities.Align4(sizeOf);
+ int align4 = (int) MemoryUtilities.Align4(sizeOf);
_writeIndex += align4;
return ref buffer;
@@ -218,7 +249,7 @@ namespace Svelto.ECS.DataStructures
{
//copy to the new pointer, from th reader position
var currentSize = _writeIndex - _readIndex;
- Unsafe.CopyBlock(newPointer, ptr + readerHead, currentSize);
+ Unsafe.CopyBlock(newPointer, ptr + readerHead, (uint)currentSize);
}
//the assumption is that if size > 0 (so readerPointer and writerPointer are not the same)
//writerHead wrapped and reached readerHead. so I have to copy from readerHead to the end
@@ -228,7 +259,7 @@ namespace Svelto.ECS.DataStructures
var byteCountToEnd = capacity - readerHead;
Unsafe.CopyBlock(newPointer, ptr + readerHead, byteCountToEnd);
- Unsafe.CopyBlock(newPointer + byteCountToEnd, ptr, writerHead);
+ Unsafe.CopyBlock(newPointer + byteCountToEnd, ptr, (uint)writerHead);
}
}
}
@@ -236,11 +267,11 @@ namespace Svelto.ECS.DataStructures
if (ptr != null)
MemoryUtilities.Free((IntPtr) ptr, allocator);
- _writeIndex = size;
- _readIndex = 0;
-
ptr = newPointer;
capacity = newCapacity;
+
+ _readIndex = 0;
+ _writeIndex = (int)size;
}
}
@@ -265,6 +296,7 @@ namespace Svelto.ECS.DataStructures
_readIndex = 0;
}
- uint _writeIndex, _readIndex;
+ internal int _writeIndex;
+ internal uint _readIndex;
}
}
\ No newline at end of file
diff --git a/Svelto.ECS/Debugger/ExclusiveGroupDebugger.cs b/Svelto.ECS/Debugger/ExclusiveGroupDebugger.cs
index b38e2dc..3ebf160 100644
--- a/Svelto.ECS/Debugger/ExclusiveGroupDebugger.cs
+++ b/Svelto.ECS/Debugger/ExclusiveGroupDebugger.cs
@@ -23,15 +23,16 @@ public static class ExclusiveGroupDebugger
{
if (field.IsStatic && typeof(ExclusiveGroup).IsAssignableFrom(field.FieldType))
{
- string name = $"{type.FullName}.{field.Name}";
- var group = (ExclusiveGroup) field.GetValue(null);
+ var group = (ExclusiveGroup) field.GetValue(null);
+ string name = $"{type.FullName}.{field.Name} ({(uint)group})";
GroupMap.idToName[(ExclusiveGroupStruct) group] = name;
}
if (field.IsStatic && typeof(ExclusiveGroupStruct).IsAssignableFrom(field.FieldType))
{
- string name = $"{type.FullName}.{field.Name}";
- var group = (ExclusiveGroupStruct) field.GetValue(null);
+ var group = (ExclusiveGroupStruct) field.GetValue(null);
+
+ string name = $"{type.FullName}.{field.Name} ({(uint)group})";
GroupMap.idToName[@group] = name;
}
}
diff --git a/Svelto.ECS/DynamicEntityDescriptor.cs b/Svelto.ECS/DynamicEntityDescriptor.cs
index 05aa65d..d44a193 100644
--- a/Svelto.ECS/DynamicEntityDescriptor.cs
+++ b/Svelto.ECS/DynamicEntityDescriptor.cs
@@ -9,7 +9,7 @@ namespace Svelto.ECS
/// This method allocates, so it shouldn't be abused
///
///
- public struct DynamicEntityDescriptor : IEntityDescriptor where TType : IEntityDescriptor, new()
+ public struct DynamicEntityDescriptor : IDynamicEntityDescriptor where TType : IEntityDescriptor, new()
{
internal DynamicEntityDescriptor(bool isExtendible) : this()
{
@@ -22,9 +22,9 @@ namespace Svelto.ECS
//assign it after otherwise the previous copy will overwrite the value in case the item
//is already present
- ComponentsToBuild[length] = new ComponentBuilder
+ ComponentsToBuild[length] = new ComponentBuilder
(
- new EntityInfoViewComponent
+ new EntityInfoComponent
{
componentsToBuild = ComponentsToBuild
}
@@ -72,17 +72,16 @@ namespace Svelto.ECS
}
var defaultEntities = startingEntities;
- var length = defaultEntities.Length;
-
- var index = SetupSpecialEntityComponent(defaultEntities, out localEntitiesToBuild, extraEntitiesLength);
+
+ var index = SetupEntityInfoComponent(defaultEntities, out localEntitiesToBuild, extraEntitiesLength);
- Array.Copy(extraEntities, 0, localEntitiesToBuild, length, extraEntitiesLength);
+ Array.Copy(extraEntities, 0, localEntitiesToBuild, defaultEntities.Length, extraEntitiesLength);
//assign it after otherwise the previous copy will overwrite the value in case the item
//is already present
- localEntitiesToBuild[index] = new ComponentBuilder
+ localEntitiesToBuild[index] = new ComponentBuilder
(
- new EntityInfoViewComponent
+ new EntityInfoComponent
{
componentsToBuild = localEntitiesToBuild
}
@@ -91,7 +90,7 @@ namespace Svelto.ECS
return localEntitiesToBuild;
}
- static int SetupSpecialEntityComponent(IComponentBuilder[] defaultEntities, out IComponentBuilder[] componentsToBuild,
+ static int SetupEntityInfoComponent(IComponentBuilder[] defaultEntities, out IComponentBuilder[] componentsToBuild,
int extraLenght)
{
int length = defaultEntities.Length;
@@ -100,7 +99,7 @@ namespace Svelto.ECS
for (var i = 0; i < length; i++)
{
//the special entity already exists
- if (defaultEntities[i].GetEntityComponentType() == ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW)
+ if (defaultEntities[i].GetEntityComponentType() == ComponentBuilderUtilities.ENTITY_INFO_COMPONENT)
{
index = i;
break;
@@ -120,7 +119,6 @@ namespace Svelto.ECS
return index;
}
-
public IComponentBuilder[] componentsToBuild => ComponentsToBuild;
IComponentBuilder[] ComponentsToBuild;
diff --git a/Svelto.ECS/ECSResources/ECSString.cs b/Svelto.ECS/ECSResources/ECSString.cs
index 2717aaa..50fef70 100644
--- a/Svelto.ECS/ECSResources/ECSString.cs
+++ b/Svelto.ECS/ECSResources/ECSString.cs
@@ -8,6 +8,10 @@ namespace Svelto.ECS.Experimental
///
/// Note: I should extend this to reuse unused id
///
+
+ //todo ResourcesECSDB must be used only inside entity components. Same for ECSString.
+ //what I could do is that if the component is removed from the database, a reference counter to the object
+ //will be modified. If 0 is reached, the ID should be recycled.
public struct ECSString:IEquatable
{
[FieldOffset(0)] uint _id;
diff --git a/Svelto.ECS/EGID.cs b/Svelto.ECS/EGID.cs
index 4945031..e2a0f77 100644
--- a/Svelto.ECS/EGID.cs
+++ b/Svelto.ECS/EGID.cs
@@ -13,6 +13,8 @@ namespace Svelto.ECS
[FieldOffset(0)] public readonly uint entityID;
[FieldOffset(4)] public readonly ExclusiveGroupStruct groupID;
[FieldOffset(0)] readonly ulong _GID;
+
+ public static readonly EGID Empty = new EGID();
public static bool operator ==(EGID obj1, EGID obj2)
{
@@ -28,6 +30,11 @@ namespace Svelto.ECS
{
_GID = MAKE_GLOBAL_ID(entityID, groupID);
}
+
+ public EGID(uint entityID, BuildGroup groupID) : this()
+ {
+ _GID = MAKE_GLOBAL_ID(entityID, groupID.group);
+ }
static ulong MAKE_GLOBAL_ID(uint entityId, uint groupId)
{
@@ -74,7 +81,8 @@ namespace Svelto.ECS
public override string ToString()
{
- return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(groupID.ToName());
+ var value = groupID.ToName();
+ return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(value);
}
}
}
diff --git a/Svelto.ECS/EGIDMapper.cs b/Svelto.ECS/EGIDMapper.cs
index e4a23d0..3596a9d 100644
--- a/Svelto.ECS/EGIDMapper.cs
+++ b/Svelto.ECS/EGIDMapper.cs
@@ -1,15 +1,23 @@
+using System;
using System.Runtime.CompilerServices;
+using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
namespace Svelto.ECS
{
- public readonly struct EGIDMapper where T : struct, IEntityComponent
+ ///
+ /// Note: does mono devirtualize sealed classes? If so it could be worth to use TypeSafeDictionary instead of
+ /// the interface
+ ///
+ ///
+ public readonly struct EGIDMapper: IEGIDMapper where T : struct, IEntityComponent
{
- public uint length => _map.count;
- public ExclusiveGroupStruct groupID { get; }
+ public uint length => _map.count;
+ public ExclusiveGroupStruct groupID { get; }
+ public Type entityType => TypeCache.type;
- public EGIDMapper(ExclusiveGroupStruct groupStructId, ITypeSafeDictionary dic) : this()
+ internal EGIDMapper(ExclusiveGroupStruct groupStructId, ITypeSafeDictionary dic) : this()
{
groupID = groupStructId;
_map = dic;
@@ -19,6 +27,8 @@ namespace Svelto.ECS
public ref T Entity(uint entityID)
{
#if DEBUG && !PROFILE_SVELTO
+ if (_map == null)
+ throw new System.Exception("Not initialized EGIDMapper in this group ".FastConcat(typeof(T).ToString()));
if (_map.TryFindIndex(entityID, out var findIndex) == false)
throw new System.Exception("Entity not found in this group ".FastConcat(typeof(T).ToString()));
#else
@@ -39,29 +49,34 @@ namespace Svelto.ECS
return false;
}
- public IBuffer GetArrayAndEntityIndex(uint entityID, out uint index)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool Exists(uint idEntityId)
{
- if (_map.TryFindIndex(entityID, out index))
- {
- return _map.GetValues(out _);
- }
-
- throw new ECSException("Entity not found");
+ return _map.count > 0 && _map.TryFindIndex(idEntityId, out _);
}
- public bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out IBuffer array)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public uint GetIndex(uint entityID)
{
- index = default;
- if (_map != null && _map.TryFindIndex(entityID, out index))
- {
- array = _map.GetValues(out _);
- return true;
- }
+ return _map.GetIndex(entityID);
+ }
- array = default;
- return false;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool FindIndex(uint valueKey, out uint index)
+ {
+ return _map.TryFindIndex(valueKey, out index);
}
+
+ internal readonly ITypeSafeDictionary _map;
+ }
+
+ public interface IEGIDMapper
+ {
+ bool FindIndex(uint valueKey, out uint index);
+ uint GetIndex(uint entityID);
+ bool Exists(uint idEntityId);
- readonly ITypeSafeDictionary _map;
+ ExclusiveGroupStruct groupID { get; }
+ Type entityType { get; }
}
}
\ No newline at end of file
diff --git a/Svelto.ECS/EnginesRoot.DoubleBufferedEntitiesToAdd.cs b/Svelto.ECS/EnginesRoot.DoubleBufferedEntitiesToAdd.cs
index 4e1eae0..86322d4 100644
--- a/Svelto.ECS/EnginesRoot.DoubleBufferedEntitiesToAdd.cs
+++ b/Svelto.ECS/EnginesRoot.DoubleBufferedEntitiesToAdd.cs
@@ -29,7 +29,7 @@ namespace Svelto.ECS
var otherCount = other.count;
if (otherCount > MaximumNumberOfItemsPerFrameBeforeToClear)
{
- FasterDictionary, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues;
+ FasterDictionary[] otherValuesArray = other.unsafeValues;
for (int i = 0; i < otherCount; ++i)
{
var safeDictionariesCount = otherValuesArray[i].count;
@@ -49,7 +49,7 @@ namespace Svelto.ECS
}
{
- FasterDictionary, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues;
+ FasterDictionary[] otherValuesArray = other.unsafeValues;
for (int i = 0; i < otherCount; ++i)
{
var safeDictionariesCount = otherValuesArray[i].count;
@@ -83,25 +83,25 @@ namespace Svelto.ECS
/// To avoid extra allocation, I don't clear the dictionaries, so I need an extra data structure
/// to keep count of the number of entities submitted this frame
///
- internal FasterDictionary currentEntitiesCreatedPerGroup;
- internal FasterDictionary otherEntitiesCreatedPerGroup;
+ internal FasterDictionary currentEntitiesCreatedPerGroup;
+ internal FasterDictionary otherEntitiesCreatedPerGroup;
//Before I tried for the third time to use a SparseSet instead of FasterDictionary, remember that
//while group indices are sequential, they may not be used in a sequential order. Sparaset needs
//entities to be created sequentially (the index cannot be managed externally)
- internal FasterDictionary, ITypeSafeDictionary>> current;
- internal FasterDictionary, ITypeSafeDictionary>> other;
+ internal FasterDictionary> current;
+ internal FasterDictionary> other;
- readonly FasterDictionary, ITypeSafeDictionary>>
+ readonly FasterDictionary>
_entityComponentsToAddBufferA =
- new FasterDictionary, ITypeSafeDictionary>>();
+ new FasterDictionary>();
- readonly FasterDictionary, ITypeSafeDictionary>>
+ readonly FasterDictionary>
_entityComponentsToAddBufferB =
- new FasterDictionary, ITypeSafeDictionary>>();
+ new FasterDictionary>();
- readonly FasterDictionary _entitiesCreatedPerGroupA = new FasterDictionary();
- readonly FasterDictionary _entitiesCreatedPerGroupB = new FasterDictionary();
+ readonly FasterDictionary _entitiesCreatedPerGroupA = new FasterDictionary();
+ readonly FasterDictionary _entitiesCreatedPerGroupB = new FasterDictionary();
public DoubleBufferedEntitiesToAdd()
{
diff --git a/Svelto.ECS/EnginesRoot.Engines.cs b/Svelto.ECS/EnginesRoot.Engines.cs
index 70c8266..7b3b388 100644
--- a/Svelto.ECS/EnginesRoot.Engines.cs
+++ b/Svelto.ECS/EnginesRoot.Engines.cs
@@ -9,13 +9,15 @@ namespace Svelto.ECS
{
public sealed partial class EnginesRoot
{
- public struct EntitiesSubmitter
+ public readonly struct EntitiesSubmitter
{
public EntitiesSubmitter(EnginesRoot enginesRoot)
{
_weakReference = new Svelto.DataStructures.WeakReference(enginesRoot);
}
+ public bool IsUnused => _weakReference.IsValid == false;
+
public void Invoke()
{
if (_weakReference.IsValid)
@@ -25,7 +27,8 @@ namespace Svelto.ECS
readonly Svelto.DataStructures.WeakReference _weakReference;
}
- public IEntitiesSubmissionScheduler scheduler { get; }
+ readonly EntitiesSubmissionScheduler _scheduler;
+ public IEntitiesSubmissionScheduler scheduler => _scheduler;
///
/// Engines root contextualize your engines and entities. You don't need to limit yourself to one EngineRoot
@@ -35,34 +38,38 @@ namespace Svelto.ECS
/// The EntitySubmissionScheduler cannot hold an EnginesRoot reference, that's why
/// it must receive a weak reference of the EnginesRoot callback.
///
- public EnginesRoot(IEntitiesSubmissionScheduler entitiesComponentScheduler)
+ public EnginesRoot(EntitiesSubmissionScheduler entitiesComponentScheduler)
{
_entitiesOperations = new ThreadSafeDictionary();
serializationDescriptorMap = new SerializationDescriptorMap();
- _reactiveEnginesAddRemove = new FasterDictionary, FasterList>();
- _reactiveEnginesSwap = new FasterDictionary, FasterList>();
+ _reactiveEnginesAddRemove = new FasterDictionary>();
+ _reactiveEnginesSwap = new FasterDictionary>();
+ _reactiveEnginesSubmission = new FasterList