Browse Source

Merge branch 'master' into SECS_3_0_BETA

# Conflicts:
#	package.json
pull/54/head
sebas77 4 years ago
parent
commit
b47e3d9d8c
100 changed files with 3481 additions and 2266 deletions
  1. +1
    -1
      Svelto.Common
  2. +60
    -54
      Svelto.ECS/CheckEntityUtilities.cs
  3. +6
    -11
      Svelto.ECS/ComponentBuilder.CheckFields.cs
  4. +8
    -2
      Svelto.ECS/ComponentBuilder.cs
  5. +0
    -0
      Svelto.ECS/Components/EGIDComponent.cs
  6. +0
    -0
      Svelto.ECS/Components/EntityHierarchyComponent.cs
  7. +7
    -0
      Svelto.ECS/Components/LinkedEntityComponent.cs
  8. +7
    -7
      Svelto.ECS/DataStructures/FastTypeSafeDictionary.cs
  9. +11
    -6
      Svelto.ECS/DataStructures/ITypeSafeDictionary.cs
  10. +74
    -0
      Svelto.ECS/DataStructures/ThreadSafeNativeBagTest.cs
  11. +106
    -85
      Svelto.ECS/DataStructures/TypeSafeDictionary.cs
  12. +21
    -19
      Svelto.ECS/DataStructures/Unmanaged/AtomicNativeBags.cs
  13. +281
    -0
      Svelto.ECS/DataStructures/Unmanaged/NativeBag.cs
  14. +87
    -86
      Svelto.ECS/DataStructures/Unmanaged/NativeDynamicArray.cs
  15. +48
    -0
      Svelto.ECS/DataStructures/Unmanaged/NativeDynamicArrayCast.cs
  16. +1
    -1
      Svelto.ECS/DataStructures/Unmanaged/NativeDynamicArrayUnityExtension.cs
  17. +47
    -10
      Svelto.ECS/DataStructures/Unmanaged/SharedNativeInt.cs
  18. +84
    -52
      Svelto.ECS/DataStructures/Unmanaged/ThreadSafeNativeBag.cs
  19. +21
    -25
      Svelto.ECS/DataStructures/Unmanaged/UnsafeArray.cs
  20. +45
    -13
      Svelto.ECS/DataStructures/Unmanaged/UnsafeBlob.cs
  21. +5
    -4
      Svelto.ECS/Debugger/ExclusiveGroupDebugger.cs
  22. +10
    -12
      Svelto.ECS/DynamicEntityDescriptor.cs
  23. +4
    -0
      Svelto.ECS/ECSResources/ECSString.cs
  24. +9
    -1
      Svelto.ECS/EGID.cs
  25. +36
    -21
      Svelto.ECS/EGIDMapper.cs
  26. +12
    -12
      Svelto.ECS/EnginesRoot.DoubleBufferedEntitiesToAdd.cs
  27. +74
    -44
      Svelto.ECS/EnginesRoot.Engines.cs
  28. +104
    -97
      Svelto.ECS/EnginesRoot.Entities.cs
  29. +4
    -4
      Svelto.ECS/EnginesRoot.GenericEntityFactory.cs
  30. +76
    -44
      Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs
  31. +28
    -8
      Svelto.ECS/EnginesRoot.Submission.cs
  32. +63
    -35
      Svelto.ECS/EntitiesDB.FindGroups.cs
  33. +154
    -116
      Svelto.ECS/EntitiesDB.cs
  34. +23
    -171
      Svelto.ECS/EntityCollection.cs
  35. +0
    -266
      Svelto.ECS/EntityCollections.cs
  36. +6
    -6
      Svelto.ECS/EntityComponentInitializer.cs
  37. +11
    -2
      Svelto.ECS/EntityDescriptorTemplate.cs
  38. +14
    -14
      Svelto.ECS/EntityFactory.cs
  39. +2
    -2
      Svelto.ECS/EntityGroupNotFoundException.cs
  40. +0
    -11
      Svelto.ECS/EntityHierarchyStruct.cs
  41. +1
    -1
      Svelto.ECS/EntityInfoView.cs
  42. +0
    -205
      Svelto.ECS/EntityStream.cs
  43. +12
    -2
      Svelto.ECS/EntitySubmissionScheduler.cs
  44. +14
    -2
      Svelto.ECS/EntitySubmitOperation.cs
  45. +1
    -1
      Svelto.ECS/EntityViewUtility.cs
  46. +1
    -0
      Svelto.ECS/ExclusiveGroup.cs
  47. +29
    -6
      Svelto.ECS/ExclusiveGroupStruct.cs
  48. +1
    -1
      Svelto.ECS/ExtendibleEntityDescriptor.cs
  49. +2
    -2
      Svelto.ECS/Extensions/ProcessorCount.cs
  50. +4
    -62
      Svelto.ECS/Extensions/Svelto/AllGroupsEnumerable.cs
  51. +123
    -53
      Svelto.ECS/Extensions/Svelto/EntityCollectionExtension.cs
  52. +41
    -1
      Svelto.ECS/Extensions/Svelto/EntityManagedDBExtensions.cs
  53. +45
    -5
      Svelto.ECS/Extensions/Svelto/EntityNativeDBExtensions.cs
  54. +38
    -0
      Svelto.ECS/Extensions/Svelto/ExclusiveGroupExtensions.cs
  55. +111
    -34
      Svelto.ECS/Extensions/Svelto/GroupsEnumerable.cs
  56. +0
    -0
      Svelto.ECS/Extensions/Unity/DOTS/Jobs/DisposeJob.cs
  57. +22
    -17
      Svelto.ECS/Extensions/Unity/DOTS/Jobs/JobifiedEnginesGroup.cs
  58. +9
    -10
      Svelto.ECS/Extensions/Unity/DOTS/Jobs/SortedJobifiedEnginesGroup.cs
  59. +68
    -0
      Svelto.ECS/Extensions/Unity/DOTS/Jobs/UnityJobExtensions.cs
  60. +30
    -28
      Svelto.ECS/Extensions/Unity/DOTS/Native/EnginesRoot.NativeOperation.cs
  61. +97
    -0
      Svelto.ECS/Extensions/Unity/DOTS/Native/NativeEGIDMapper.cs
  62. +55
    -0
      Svelto.ECS/Extensions/Unity/DOTS/Native/NativeEGIDMultiMapper.cs
  63. +0
    -0
      Svelto.ECS/Extensions/Unity/DOTS/Native/NativeEntityComponentInitializer.cs
  64. +5
    -6
      Svelto.ECS/Extensions/Unity/DOTS/Native/NativeEntityFactory.cs
  65. +4
    -4
      Svelto.ECS/Extensions/Unity/DOTS/Native/NativeEntityRemove.cs
  66. +5
    -5
      Svelto.ECS/Extensions/Unity/DOTS/Native/NativeEntitySwap.cs
  67. +73
    -0
      Svelto.ECS/Extensions/Unity/DOTS/Native/UnityEntityDBExtensions.cs
  68. +0
    -83
      Svelto.ECS/Extensions/Unity/DOTS/NativeEGIDMapper.cs
  69. +0
    -43
      Svelto.ECS/Extensions/Unity/DOTS/NativeEGIDMultiMapper.cs
  70. +0
    -29
      Svelto.ECS/Extensions/Unity/DOTS/PureUECSSystemsGroup.cs
  71. +0
    -39
      Svelto.ECS/Extensions/Unity/DOTS/SyncSveltoToUECSGroup.cs
  72. +12
    -0
      Svelto.ECS/Extensions/Unity/DOTS/UECS/IUECSSubmissionEngine.cs
  73. +2
    -3
      Svelto.ECS/Extensions/Unity/DOTS/UECS/JobifiedSveltoEngines.cs
  74. +100
    -0
      Svelto.ECS/Extensions/Unity/DOTS/UECS/SveltoOverUECSEnginesGroup.cs
  75. +61
    -0
      Svelto.ECS/Extensions/Unity/DOTS/UECS/SveltoUECSEntitiesSubmissionGroup.cs
  76. +25
    -0
      Svelto.ECS/Extensions/Unity/DOTS/UECS/SyncSveltoToUECSGroup.cs
  77. +26
    -0
      Svelto.ECS/Extensions/Unity/DOTS/UECS/SyncUECSToSveltoGroup.cs
  78. +0
    -0
      Svelto.ECS/Extensions/Unity/DOTS/UECS/UECSSveltoEGID.cs
  79. +0
    -119
      Svelto.ECS/Extensions/Unity/DOTS/UnityEntityDBExtensions.cs
  80. +30
    -0
      Svelto.ECS/Extensions/Unity/EntityDescriptorHolderHelper.cs
  81. +6
    -2
      Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs
  82. +74
    -51
      Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs
  83. +7
    -7
      Svelto.ECS/Extensions/Unity/UnityEntitiesSubmissionScheduler.cs
  84. +0
    -27
      Svelto.ECS/FastGroup.cs
  85. +213
    -0
      Svelto.ECS/Filters/EntitiesDB.GroupFilters.cs
  86. +194
    -0
      Svelto.ECS/Filters/FilterGroup.cs
  87. +21
    -0
      Svelto.ECS/Filters/FilteredIndices.cs
  88. +103
    -0
      Svelto.ECS/Filters/GroupFilters.cs
  89. +2
    -2
      Svelto.ECS/GlobalTypeID.cs
  90. +219
    -120
      Svelto.ECS/GroupCompound.cs
  91. +4
    -1
      Svelto.ECS/Hybrid/IEntityViewComponent.cs
  92. +9
    -0
      Svelto.ECS/IDisposingEngine.cs
  93. +18
    -0
      Svelto.ECS/IEngine.cs
  94. +0
    -9
      Svelto.ECS/IEntitiesDB.cs
  95. +2
    -2
      Svelto.ECS/IEntityComponent.cs
  96. +3
    -11
      Svelto.ECS/IEntityFactory.cs
  97. +8
    -11
      Svelto.ECS/IEntityFunctions.cs
  98. +0
    -10
      Svelto.ECS/IReactOnAddAndRemove.cs
  99. +0
    -9
      Svelto.ECS/IReactOnSwap.cs
  100. +1
    -1
      Svelto.ECS/NamedExclusiveGroup.cs

+ 1
- 1
Svelto.Common

@@ -1 +1 @@
Subproject commit 6053063786f5c0972cfd3bc9067a50e02e3bd1de
Subproject commit 49d3591924c8c45e0ec32655f992480f9419e327

+ 60
- 54
Svelto.ECS/CheckEntityUtilities.cs View File

@@ -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
/// </summary>
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("<color=orange>added</color> ".FastConcat(egid.ToString()));

if (_idCheckers.TryGetValue(egid.groupID, out var hash) == false)
hash = _idCheckers[egid.groupID] = new HashSet<uint>();
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<uint>());
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<uint, HashSet<uint>> _idCheckers = new FasterDictionary<uint, HashSet<uint>>();
#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<EGID, uint> _multipleOperationOnSameEGIDChecker = new FasterDictionary<EGID, uint>();
readonly FasterDictionary<uint, HashSet<uint>> _idChecker = new FasterDictionary<uint, HashSet<uint>>();
}
}

+ 6
- 11
Svelto.ECS/ComponentBuilder.CheckFields.cs View File

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

+ 8
- 2
Svelto.ECS/ComponentBuilder.cs View File

@@ -30,6 +30,7 @@ namespace Svelto.ECS
var castedDic = dictionary as ITypeSafeDictionary<T>;

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;

/// <summary>
/// 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.
/// </summary>
static class EntityViewComponentCache
{
internal static readonly FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>> cachedFields;


Svelto.ECS/EGIDComponent.cs → Svelto.ECS/Components/EGIDComponent.cs View File


Svelto.ECS/EntityHierarchyComponent.cs → Svelto.ECS/Components/EntityHierarchyComponent.cs View File


+ 7
- 0
Svelto.ECS/Components/LinkedEntityComponent.cs View File

@@ -0,0 +1,7 @@
namespace Svelto.ECS
{
public struct LinkedEntityComponent : IEntityComponent
{
public EGID linkedEntity;
}
}

+ 7
- 7
Svelto.ECS/DataStructures/FastTypeSafeDictionary.cs View File

@@ -61,7 +61,7 @@ namespace Svelto.ECS.Internal
}
}

public void AddEntitiesToEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB,
public void AddEntitiesToEngines(FasterDictionary<RefWrapperType, FasterList<IEngine>> entityComponentEnginesDB,
ITypeSafeDictionary realDic,
ExclusiveGroupStruct @group,
in PlatformProfiler profiler)
@@ -75,7 +75,7 @@ namespace Svelto.ECS.Internal
}

public void RemoveEntitiesFromEngines(
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB, in PlatformProfiler profiler,
FasterDictionary<RefWrapperType, FasterList<IEngine>> 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<RefWrapper<Type>, FasterList<IEngine>> engines,
FasterDictionary<RefWrapperType, FasterList<IEngine>> 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<TValue>(); }

void AddEntityComponentToEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB,
void AddEntityComponentToEngines(FasterDictionary<RefWrapperType, FasterList<IEngine>> entityComponentEnginesDB,
ref TValue entity,
ExclusiveGroupStruct? previousGroup,
in PlatformProfiler profiler,
EGID egid)
{
//get all the engines linked to TValue
if (!entityComponentEnginesDB.TryGetValue(new RefWrapper<Type>(_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<RefWrapper<Type>, FasterList<IEngine>> @group,
static void RemoveEntityComponentFromEngines(FasterDictionary<RefWrapperType, FasterList<IEngine>> @group,
ref TValue entity,
ExclusiveGroupStruct? previousGroup,
in PlatformProfiler profiler,
EGID egid)
{
if (!@group.TryGetValue(new RefWrapper<Type>(_type), out var entityComponentsEngines)) return;
if (!@group.TryGetValue(new RefWrapperType(_type), out var entityComponentsEngines)) return;

if (previousGroup == null)
{


+ 11
- 6
Svelto.ECS/DataStructures/ITypeSafeDictionary.cs View File

@@ -17,16 +17,19 @@ namespace Svelto.ECS.Internal

public interface ITypeSafeDictionary:IDisposable
{
uint count { get; }
uint count { get; }
ITypeSafeDictionary Create();

void AddEntitiesToEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDb,
ITypeSafeDictionary realDic, ExclusiveGroupStruct @group, in PlatformProfiler profiler);
void RemoveEntitiesFromEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB,
//todo: there is something wrong in the design of the execute callback methods. Something to cleanup
void ExecuteEnginesAddOrSwapCallbacks(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> entityComponentEnginesDb,
ITypeSafeDictionary realDic, ExclusiveGroupStruct? fromGroup, ExclusiveGroupStruct toGroup, in PlatformProfiler profiler);
void ExecuteEnginesSwapOrRemoveCallbacks(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup,
FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, in PlatformProfiler profiler);
void ExecuteEnginesRemoveCallbacks(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> entityComponentEnginesDB,
in PlatformProfiler profiler, ExclusiveGroupStruct @group);
void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId);
void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup,
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> 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<uint> action);
}
}

+ 74
- 0
Svelto.ECS/DataStructures/ThreadSafeNativeBagTest.cs View File

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

+ 106
- 85
Svelto.ECS/DataStructures/TypeSafeDictionary.cs View File

@@ -8,30 +8,36 @@ namespace Svelto.ECS.Internal
{
sealed class TypeSafeDictionary<TValue> : ITypeSafeDictionary<TValue> 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<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, ManagedStrategy<TValue>> implMgd;
internal SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<TValue>> implUnmgd;
SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, ManagedStrategy<TValue>,
ManagedStrategy<int>> implMgd;

//used directly by native methods
internal SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<TValue>,
NativeStrategy<int>> implUnmgd;

public TypeSafeDictionary(uint size)
{
if (_isUmanaged)
implUnmgd = new SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<TValue>>(size);
if (IsUnmanaged)
implUnmgd = new SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>,
NativeStrategy<TValue>, NativeStrategy<int>>(size);
else
{
implMgd = new SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, ManagedStrategy<TValue>>(size);
implMgd = new SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>,
ManagedStrategy<TValue>, ManagedStrategy<int>>(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
/// <exception cref="TypeSafeDictionaryException"></exception>
public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId)
{
if (_isUmanaged)
if (IsUnmanaged)
{
var typeSafeDictionary = (entitiesToSubmit as TypeSafeDictionary<TValue>).implUnmgd;

@@ -89,18 +95,19 @@ namespace Svelto.ECS.Internal
}
}

public void AddEntitiesToEngines
(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB
, ITypeSafeDictionary realDic, ExclusiveGroupStruct group, in PlatformProfiler profiler)
public void ExecuteEnginesAddOrSwapCallbacks
(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> entityComponentEnginesDB
, ITypeSafeDictionary realDic, ExclusiveGroupStruct? fromGroup, ExclusiveGroupStruct toGroup
, in PlatformProfiler profiler)
{
if (_isUmanaged)
if (IsUnmanaged)
{
var typeSafeDictionary = realDic as ITypeSafeDictionary<TValue>;

//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<TValue>.Create(1);
}
public ITypeSafeDictionary Create() { return TypeSafeDictionaryFactory<TValue>.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<TValue> 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<RefWrapper<Type>, FasterList<IEngine>> engines, in PlatformProfiler profiler)
, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> 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<TValue>;
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<TValue>;
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<RefWrapper<Type>, FasterList<IEngine>> engines
, in PlatformProfiler profiler, ExclusiveGroupStruct group)
public void ExecuteEnginesRemoveCallbacks
(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> 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<uint> 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<RefWrapper<Type>, FasterList<IEngine>> engines, ref TValue entity, uint? previousGroup
static void ExecuteEnginesRemoveCallbackOnSingleEntity
(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, ref TValue entity
, in PlatformProfiler profiler, EGID egid)
{
if (!engines.TryGetValue(new RefWrapper<Type>(_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<TValue>).Remove(ref entity, egid);
}
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).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<RefWrapper<Type>, FasterList<IEngine>> engines, ref TValue entity
void ExecuteEnginesAddOrSwapCallbacksOnSingleEntity
(FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, ref TValue entity
, ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler, EGID egid)
{
//get all the engines linked to TValue
if (!engines.TryGetValue(new RefWrapper<Type>(_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);
}
}

Svelto.ECS/DataStructures/AtomicNativeBags.cs → Svelto.ECS/DataStructures/Unmanaged/AtomicNativeBags.cs View File

@@ -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
{
/// <summary>
/// A collection of <see cref="NativeBag"/> intended to allow one buffer per thread.
/// from: https://github.com/jeffvella/UnityEcsEvents/blob/develop/Runtime/MultiAppendBuffer.cs
/// </summary>
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<NativeBag>();
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<NativeBag>((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

+ 281
- 0
Svelto.ECS/DataStructures/Unmanaged/NativeBag.cs View File

@@ -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
{
/// <summary>
/// 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
/// </summary>
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<UnsafeBlob>();
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<T>(out UnsafeArrayIndex index) where T : struct
{
unsafe
{
BasicTests();

var sizeOf = MemoryUtilities.SizeOf<T>();
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<T>(out index);
#if ENABLE_THREAD_SAFE_CHECKS
}
finally
{
Volatile.Write(ref _threadSentinel, 0);
}
#endif
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Enqueue<T>(in T item) where T : struct
{
unsafe
{
BasicTests();

#if ENABLE_THREAD_SAFE_CHECKS
try
{
#endif
var sizeOf = MemoryUtilities.SizeOf<T>();
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<T>() where T : struct
{
unsafe
{
BasicTests();
#if ENABLE_THREAD_SAFE_CHECKS
try
{
#endif
return _queue->Read<T>();
#if ENABLE_THREAD_SAFE_CHECKS
}
finally
{
Volatile.Write(ref _threadSentinel, 0);
}
#endif
}
}

internal ref T AccessReserved<T>(UnsafeArrayIndex reserverIndex) where T : struct
{
unsafe
{
BasicTests();
#if ENABLE_THREAD_SAFE_CHECKS
try
{
#endif
return ref _queue->AccessReserved<T>(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;
}
}

Svelto.ECS/DataStructures/NativeDynamicArray.cs → Svelto.ECS/DataStructures/Unmanaged/NativeDynamicArray.cs View File

@@ -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<T>() 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<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");
if (_hashType != TypeHash<T>.hash)
throw new Exception($"NativeDynamicArray: not expected type used");

#endif
return (_list->count / MemoryUtilities.SizeOf<T>());
@@ -39,8 +43,8 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");

#endif
return (_list->capacity / MemoryUtilities.SizeOf<T>());
@@ -51,20 +55,21 @@ namespace Svelto.ECS.DataStructures
{
unsafe
{
var rtnStruc = new NativeDynamicArray();
#if DEBUG && !PROFILE_SVELTO
rtnStruc.hashType = TypeHash<T>.hash;
var rtnStruc = new NativeDynamicArray {_hashType = TypeHash<T>.hash};
#else
NativeDynamicArray rtnStruc = default;
#endif
var sizeOf = MemoryUtilities.SizeOf<T>();

uint pointerSize = (uint) MemoryUtilities.SizeOf<UnsafeArray>();
UnsafeArray* listData = (UnsafeArray*) MemoryUtilities.Alloc(pointerSize, allocator);
uint structSize = (uint) MemoryUtilities.SizeOf<UnsafeArray>();
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<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
if (index >= Count<T>())
throw new Exception($"NativeDynamicArray: out of bound access, index {index} count {Count<T>()}");
#endif
@@ -97,8 +102,8 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
if (index >= Capacity<T>())
throw new Exception($"NativeDynamicArray: out of bound access, index {index} capacity {Capacity<T>()}");
#endif
@@ -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<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
#endif
var structSize = (uint) MemoryUtilities.SizeOf<T>();

if (_list->space - (int) structSize < 0)
_list->Realloc((uint) (((uint) ((Count<T>() + 1) * 1.5f) * (float) structSize)));
_list->Realloc((uint) (((uint) ((Count<T>() + 1) * 1.5f) * (float) structSize)), _allocator);

_list->Add(item);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T AddAt<T>(uint index) where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
#endif
var structSize = (uint) MemoryUtilities.SizeOf<T>();

if (index >= Capacity<T>())
_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<T>(index);
}
}
public void Grow<T>(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<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
if (newCapacity <= Capacity<T>())
throw new Exception("New capacity must be greater than current one");
#endif
uint structSize = (uint) MemoryUtilities.SizeOf<T>();

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<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
#endif
uint structSize = (uint) MemoryUtilities.SizeOf<T>();
uint size = (uint) (count * structSize);
@@ -180,8 +209,8 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");

var structSize = (uint) MemoryUtilities.SizeOf<T>();
@@ -200,15 +229,15 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
if (Count<T>() == 0)
throw new Exception("NativeDynamicArray: empty array invalid operation");
#endif
var count = Count<T>() - 1;
if (index < count)
var indexToMove = Count<T>() - 1;
if (index < indexToMove)
{
Set<T>(index, Get<T>((uint) count));
Set<T>(index, Get<T>((uint) indexToMove));
}

_list->Pop<T>();
@@ -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<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");
if (_hashType != TypeHash<T>.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<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");
if (_hashType != TypeHash<T>.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<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");

#endif
var count = Count<T>();
@@ -286,8 +315,8 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
#endif
var capacity = Capacity<T>();
var lengthToCopyInBytes = capacity * MemoryUtilities.SizeOf<T>();
@@ -309,8 +338,8 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
if (hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");
if (_hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not expected type used");
#endif

var sizeOf = MemoryUtilities.SizeOf<T>();
@@ -328,42 +357,14 @@ namespace Svelto.ECS.DataStructures
MemoryUtilities.MemClear((IntPtr) _list->ptr, (uint) _list->capacity);
}
}
}

public ref struct NativeDynamicArrayCast<T> where T : struct
{
NativeDynamicArray _array;

public NativeDynamicArrayCast(NativeDynamicArray array) : this() { _array = array; }

public int count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array.Count<T>();
}

public ref T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _array.Get<T>((uint) index);
}

public ref T this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _array.Get<T>(index);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(in T id) { _array.Add(id); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void UnorderedRemoveAt(uint index) { _array.UnorderedRemoveAt<T>(index); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAt(uint index) { _array.RemoveAt<T>(index); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() { _array.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;
}
}

+ 48
- 0
Svelto.ECS/DataStructures/Unmanaged/NativeDynamicArrayCast.cs View File

@@ -0,0 +1,48 @@
using System.Runtime.CompilerServices;

namespace Svelto.ECS.DataStructures
{
public struct NativeDynamicArrayCast<T> where T : struct
{
public NativeDynamicArrayCast(NativeDynamicArray array) : this() { _array = array; }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Count() => _array.Count<T>();

public int count => _array.Count<T>();

public ref T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _array.Get<T>((uint) index);
}

public ref T this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _array.Get<T>(index);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(in T id) { _array.Add(id); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void UnorderedRemoveAt(uint index) { _array.UnorderedRemoveAt<T>(index); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAt(uint index) { _array.RemoveAt<T>(index); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() { _array.FastClear(); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose() { _array.Dispose(); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T AddAt(uint lastIndex) { return ref _array.AddAt<T>(lastIndex); }

public bool isValid => _array.isValid;

NativeDynamicArray _array;
}
}

Svelto.ECS/DataStructures/NativeDynamicArrayUnityExtension.cs → Svelto.ECS/DataStructures/Unmanaged/NativeDynamicArrayUnityExtension.cs View File

@@ -1,4 +1,4 @@
#if UNITY_COLLECTIONS
#if UNITY_NATIVE
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;


Svelto.ECS/DataStructures/SharedNativeInt.cs → Svelto.ECS/DataStructures/Unmanaged/SharedNativeInt.cs View File

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

Svelto.ECS/DataStructures/NativeBag.cs → Svelto.ECS/DataStructures/Unmanaged/ThreadSafeNativeBag.cs View File

@@ -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
{
/// <summary>
/// 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
/// </summary>
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<UnsafeBlob>();
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<T>(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<T>();
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<T>(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<T>();
if (_queue->space - sizeOf < 0)
_queue->Realloc((uint) ((_queue->capacity + MemoryUtilities.Align4((uint) sizeOf)) * 2.0f));
var sizeOf = MemoryUtilities.SizeOf<T>();
var alignedSize = (uint) MemoryUtilities.SizeOfAligned<T>();

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

public ref T AccessReserved<T>(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<T>(reserverIndex);
}
}
unsafe UnsafeBlob* _queue;

int _writingGuard;
}
}
}
#endif

Svelto.ECS/DataStructures/UnsafeArray.cs → Svelto.ECS/DataStructures/Unmanaged/UnsafeArray.cs View File

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

/// <summary>
/// </summary>
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<T>();
if (index + sizeOf > _writeIndex)
throw new Exception("no reading authorized");
#endif
return ref Unsafe.AsRef<T>(Unsafe.Add<T>(ptr, (int) index));
}
}
@@ -73,40 +75,34 @@ namespace Svelto.ECS.DataStructures
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Pop<T>() where T : struct
public ref T Pop<T>() where T : struct
{
var structSize = MemoryUtilities.SizeOf<T>();
unsafe
{
var structSize = MemoryUtilities.SizeOf<T>();
_writeIndex -= (uint)structSize;
_writeIndex -= (uint)structSize;
return ref Unsafe.AsRef<T>(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;

Svelto.ECS/DataStructures/UnsafeBlob.cs → Svelto.ECS/DataStructures/Unmanaged/UnsafeBlob.cs View File

@@ -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
/// <summary>
/// </summary>
internal Allocator allocator;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Write<T>(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<T>(in T item, uint writeIndex) where T : struct
{
unsafe
{
var structSize = (uint) MemoryUtilities.SizeOf<T>();

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

+ 5
- 4
Svelto.ECS/Debugger/ExclusiveGroupDebugger.cs View File

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


+ 10
- 12
Svelto.ECS/DynamicEntityDescriptor.cs View File

@@ -9,7 +9,7 @@ namespace Svelto.ECS
/// This method allocates, so it shouldn't be abused
/// </summary>
/// <typeparam name="TType"></typeparam>
public struct DynamicEntityDescriptor<TType> : IEntityDescriptor where TType : IEntityDescriptor, new()
public struct DynamicEntityDescriptor<TType> : 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<EntityInfoViewComponent>
ComponentsToBuild[length] = new ComponentBuilder<EntityInfoComponent>
(
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<EntityInfoViewComponent>
localEntitiesToBuild[index] = new ComponentBuilder<EntityInfoComponent>
(
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;


+ 4
- 0
Svelto.ECS/ECSResources/ECSString.cs View File

@@ -8,6 +8,10 @@ namespace Svelto.ECS.Experimental
///
/// Note: I should extend this to reuse unused id
///
//todo ResourcesECSDB<T> 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<ECSString>
{
[FieldOffset(0)] uint _id;


+ 9
- 1
Svelto.ECS/EGID.cs View File

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

+ 36
- 21
Svelto.ECS/EGIDMapper.cs View File

@@ -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<T> where T : struct, IEntityComponent
/// <summary>
/// Note: does mono devirtualize sealed classes? If so it could be worth to use TypeSafeDictionary instead of
/// the interface
/// </summary>
/// <typeparam name="T"></typeparam>
public readonly struct EGIDMapper<T>: 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<T>.type;

public EGIDMapper(ExclusiveGroupStruct groupStructId, ITypeSafeDictionary<T> dic) : this()
internal EGIDMapper(ExclusiveGroupStruct groupStructId, ITypeSafeDictionary<T> 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<T> 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<T> 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<T> _map;
}

public interface IEGIDMapper
{
bool FindIndex(uint valueKey, out uint index);
uint GetIndex(uint entityID);
bool Exists(uint idEntityId);
readonly ITypeSafeDictionary<T> _map;
ExclusiveGroupStruct groupID { get; }
Type entityType { get; }
}
}

+ 12
- 12
Svelto.ECS/EnginesRoot.DoubleBufferedEntitiesToAdd.cs View File

@@ -29,7 +29,7 @@ namespace Svelto.ECS
var otherCount = other.count;
if (otherCount > MaximumNumberOfItemsPerFrameBeforeToClear)
{
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues;
FasterDictionary<RefWrapperType, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues;
for (int i = 0; i < otherCount; ++i)
{
var safeDictionariesCount = otherValuesArray[i].count;
@@ -49,7 +49,7 @@ namespace Svelto.ECS
}

{
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues;
FasterDictionary<RefWrapperType, ITypeSafeDictionary>[] 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
/// </summary>
internal FasterDictionary<uint, uint> currentEntitiesCreatedPerGroup;
internal FasterDictionary<uint, uint> otherEntitiesCreatedPerGroup;
internal FasterDictionary<ExclusiveGroupStruct, uint> currentEntitiesCreatedPerGroup;
internal FasterDictionary<ExclusiveGroupStruct, uint> 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<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> current;
internal FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> other;
internal FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> current;
internal FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>> other;

readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>
readonly FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>
_entityComponentsToAddBufferA =
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>();
new FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>();

readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>
readonly FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>
_entityComponentsToAddBufferB =
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>();
new FasterDictionary<uint, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>();

readonly FasterDictionary<uint, uint> _entitiesCreatedPerGroupA = new FasterDictionary<uint, uint>();
readonly FasterDictionary<uint, uint> _entitiesCreatedPerGroupB = new FasterDictionary<uint, uint>();
readonly FasterDictionary<ExclusiveGroupStruct, uint> _entitiesCreatedPerGroupA = new FasterDictionary<ExclusiveGroupStruct, uint>();
readonly FasterDictionary<ExclusiveGroupStruct, uint> _entitiesCreatedPerGroupB = new FasterDictionary<ExclusiveGroupStruct, uint>();

public DoubleBufferedEntitiesToAdd()
{


+ 74
- 44
Svelto.ECS/EnginesRoot.Engines.cs View File

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

public bool IsUnused => _weakReference.IsValid == false;

public void Invoke()
{
if (_weakReference.IsValid)
@@ -25,7 +27,8 @@ namespace Svelto.ECS
readonly Svelto.DataStructures.WeakReference<EnginesRoot> _weakReference;
}

public IEntitiesSubmissionScheduler scheduler { get; }
readonly EntitiesSubmissionScheduler _scheduler;
public IEntitiesSubmissionScheduler scheduler => _scheduler;

/// <summary>
/// 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.
/// </summary>
public EnginesRoot(IEntitiesSubmissionScheduler entitiesComponentScheduler)
public EnginesRoot(EntitiesSubmissionScheduler entitiesComponentScheduler)
{
_entitiesOperations = new ThreadSafeDictionary<ulong, EntitySubmitOperation>();
serializationDescriptorMap = new SerializationDescriptorMap();
_reactiveEnginesAddRemove = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>();
_reactiveEnginesSwap = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>();
_reactiveEnginesAddRemove = new FasterDictionary<RefWrapperType, FasterList<IReactEngine>>();
_reactiveEnginesSwap = new FasterDictionary<RefWrapperType, FasterList<IReactEngine>>();
_reactiveEnginesSubmission = new FasterList<IReactOnSubmission>();
_enginesSet = new FasterList<IEngine>();
_enginesTypeSet = new HashSet<Type>();
_disposableEngines = new FasterList<IDisposable>();
_transientEntitiesOperations = new FasterList<EntitySubmitOperation>();

_groupEntityComponentsDB =
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>();
_groupsPerEntity = new FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>>();
new FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>();
_groupsPerEntity =
new FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>>();
_groupedEntityToAdd = new DoubleBufferedEntitiesToAdd();

_entitiesStream = new EntitiesStream();
_entitiesDB = new EntitiesDB(_groupEntityComponentsDB, _groupsPerEntity, _entitiesStream);
_entityStreams = EntitiesStreams.Create();
_groupFilters =
new FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>>();
_entitiesDB = new EntitiesDB(this);

scheduler = entitiesComponentScheduler;
scheduler.onTick = new EntitiesSubmitter(this);
#if UNITY_BURST
_scheduler = entitiesComponentScheduler;
_scheduler.onTick = new EntitiesSubmitter(this);
#if UNITY_NATIVE
AllocateNativeOperations();
#endif
}

public EnginesRoot
(IEntitiesSubmissionScheduler entitiesComponentScheduler, bool isDeserializationOnly) :
(EntitiesSubmissionScheduler entitiesComponentScheduler, bool isDeserializationOnly) :
this(entitiesComponentScheduler)
{
_isDeserializationOnly = isDeserializationOnly;
@@ -75,12 +82,20 @@ namespace Svelto.ECS
/// </summary>
public void Dispose()
{
_isDisposing = true;

using (var profiler = new PlatformProfiler("Final Dispose"))
{
//Note: The engines are disposed before the the remove callback to give the chance to behave
//differently if a remove happens as a consequence of a dispose
//The pattern is to implement the IDisposable interface and set a flag in the engine. The
//remove callback will then behave differently according the flag.
foreach (var engine in _disposableEngines)
{
try
{
if (engine is IDisposingEngine dengine)
dengine.isDisposing = true;
engine.Dispose();
}
catch (Exception e)
@@ -88,15 +103,15 @@ namespace Svelto.ECS
Svelto.Console.LogException(e);
}
}
foreach (FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>.
foreach (FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>.
KeyValuePairFast groups in _groupEntityComponentsDB)
{
foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast entityList in
groups.Value)
foreach (FasterDictionary<RefWrapperType, ITypeSafeDictionary>.KeyValuePairFast entityList in groups
.Value)
try
{
entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler
entityList.Value.ExecuteEnginesRemoveCallbacks(_reactiveEnginesAddRemove, profiler
, new ExclusiveGroupStruct(groups.Key));
}
catch (Exception e)
@@ -104,15 +119,27 @@ namespace Svelto.ECS
Svelto.Console.LogException(e);
}
}
foreach (FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>.
foreach (FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>.
KeyValuePairFast groups in _groupEntityComponentsDB)
{
foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast entityList in
groups.Value)
foreach (FasterDictionary<RefWrapperType, ITypeSafeDictionary>.KeyValuePairFast entityList in groups
.Value)
entityList.Value.Dispose();
}

foreach (FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>>.
KeyValuePairFast type in _groupFilters)
foreach (FasterDictionary<ExclusiveGroupStruct, GroupFilters>.KeyValuePairFast group in type.Value)
group.Value.Dispose();

_groupFilters.Clear();

#if UNITY_NATIVE
_addOperationQueue.Dispose();
_removeOperationQueue.Dispose();
_swapOperationQueue.Dispose();
#endif
_groupEntityComponentsDB.Clear();
_groupsPerEntity.Clear();

@@ -121,15 +148,13 @@ namespace Svelto.ECS
_enginesTypeSet.Clear();
_reactiveEnginesSwap.Clear();
_reactiveEnginesAddRemove.Clear();
_reactiveEnginesSubmission.Clear();

_entitiesOperations.Clear();
_transientEntitiesOperations.Clear();
#if DEBUG && !PROFILE_SVELTO
_idCheckers.Clear();
#endif

_groupedEntityToAdd.Dispose();
_entitiesStream.Dispose();
_entityStreams.Dispose();
scheduler.Dispose();
}

@@ -146,7 +171,7 @@ namespace Svelto.ECS
public void AddEngine(IEngine engine)
{
var type = engine.GetType();
var refWrapper = new RefWrapper<Type>(type);
var refWrapper = new RefWrapperType(type);
DBC.ECS.Check.Require(engine != null, "Engine to add is invalid or null");
DBC.ECS.Check.Require(
_enginesTypeSet.Contains(refWrapper) == false
@@ -156,10 +181,13 @@ namespace Svelto.ECS
try
{
if (engine is IReactOnAddAndRemove viewEngine)
CheckEntityComponentsEngine(viewEngine, _reactiveEnginesAddRemove);
CheckReactEngineComponents(viewEngine, _reactiveEnginesAddRemove);

if (engine is IReactOnSwap viewEngineSwap)
CheckEntityComponentsEngine(viewEngineSwap, _reactiveEnginesSwap);
CheckReactEngineComponents(viewEngineSwap, _reactiveEnginesSwap);

if (engine is IReactOnSubmission submissionEngine)
_reactiveEnginesSubmission.Add(submissionEngine);

_enginesTypeSet.Add(refWrapper);
_enginesSet.Add(engine);
@@ -180,8 +208,8 @@ namespace Svelto.ECS
}
}

void CheckEntityComponentsEngine<T>(T engine, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines)
where T : class, IEngine
void CheckReactEngineComponents<T>(T engine, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines)
where T : class, IReactEngine
{
var interfaces = engine.GetType().GetInterfaces();

@@ -197,8 +225,8 @@ namespace Svelto.ECS
}

static void AddEngine<T>
(T engine, Type[] entityComponentTypes, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines)
where T : class, IEngine
(T engine, Type[] entityComponentTypes, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines)
where T : class, IReactEngine
{
for (var i = 0; i < entityComponentTypes.Length; i++)
{
@@ -208,23 +236,25 @@ namespace Svelto.ECS
}
}

static void AddEngine<T>(T engine, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, Type type)
where T : class, IEngine
static void AddEngine<T>(T engine, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines, Type type)
where T : class, IReactEngine
{
if (engines.TryGetValue(new RefWrapper<Type>(type), out var list) == false)
if (engines.TryGetValue(new RefWrapperType(type), out var list) == false)
{
list = new FasterList<IEngine>();
list = new FasterList<IReactEngine>();

engines.Add(new RefWrapper<Type>(type), list);
engines.Add(new RefWrapperType(type), list);
}

list.Add(engine);
}

readonly FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> _reactiveEnginesAddRemove;
readonly FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> _reactiveEnginesSwap;
readonly FasterList<IDisposable> _disposableEngines;
readonly FasterList<IEngine> _enginesSet;
readonly HashSet<Type> _enginesTypeSet;
readonly FasterDictionary<RefWrapperType, FasterList<IReactEngine>> _reactiveEnginesAddRemove;
readonly FasterDictionary<RefWrapperType, FasterList<IReactEngine>> _reactiveEnginesSwap;
readonly FasterList<IReactOnSubmission> _reactiveEnginesSubmission;
readonly FasterList<IDisposable> _disposableEngines;
readonly FasterList<IEngine> _enginesSet;
readonly HashSet<Type> _enginesTypeSet;
internal bool _isDisposing;
}
}

+ 104
- 97
Svelto.ECS/EnginesRoot.Entities.cs View File

@@ -16,19 +16,28 @@ namespace Svelto.ECS
{
return new GenericEntityStreamConsumerFactory(this);
}
public IEntityFactory GenerateEntityFactory() { return new GenericEntityFactory(this); }
public IEntityFunctions GenerateEntityFunctions() { return new GenericEntityFunctions(this); }

public IEntityFactory GenerateEntityFactory()
{
return new GenericEntityFactory(this);
}

public IEntityFunctions GenerateEntityFunctions()
{
return new GenericEntityFunctions(this);
}

///--------------------------------------------
[MethodImpl(MethodImplOptions.AggressiveInlining)]
EntityComponentInitializer BuildEntity
(EGID entityID, IComponentBuilder[] componentsToBuild, Type implementorType, IEnumerable<object> implementors = null)
(EGID entityID, IComponentBuilder[] componentsToBuild, Type descriptorType,
IEnumerable<object> implementors = null)
{
CheckAddEntityID(entityID, implementorType);
Check.Require(entityID.groupID != 0, "invalid group detected");
CheckAddEntityID(entityID, descriptorType);
Check.Require(entityID.groupID != 0, "invalid group detected, are you using new ExclusiveGroupStruct() instead of new ExclusiveGroup()?");

var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd, componentsToBuild
, implementors);
, implementors, descriptorType);

return new EntityComponentInitializer(entityID, dic);
}
@@ -41,21 +50,21 @@ namespace Svelto.ECS
var entityComponentsToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild;
var numberOfEntityComponents = entityComponentsToBuild.Length;

FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group = GetOrCreateGroup(groupID, profiler);
FasterDictionary<RefWrapperType, ITypeSafeDictionary> group = GetOrCreateGroup(groupID, profiler);

for (var index = 0; index < numberOfEntityComponents; index++)
{
var entityComponentBuilder = entityComponentsToBuild[index];
var entityComponentType = entityComponentBuilder.GetEntityComponentType();

var refWrapper = new RefWrapper<Type>(entityComponentType);
var refWrapper = new RefWrapperType(entityComponentType);
if (group.TryGetValue(refWrapper, out var dbList) == false)
group[refWrapper] = entityComponentBuilder.Preallocate(ref dbList, size);
else
dbList.SetCapacity(size);

if (_groupsPerEntity.TryGetValue(refWrapper, out var groupedGroup) == false)
groupedGroup = _groupsPerEntity[refWrapper] = new FasterDictionary<uint, ITypeSafeDictionary>();
groupedGroup = _groupsPerEntity[refWrapper] = new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();

groupedGroup[groupID] = dbList;
}
@@ -70,28 +79,30 @@ namespace Svelto.ECS
{
var fromGroup = GetGroup(fromEntityGID.groupID);

//Check if there is an EntityInfoView linked to this entity, if so it's a DynamicEntityDescriptor!
if (fromGroup.TryGetValue(new RefWrapper<Type>(ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW)
, out var entityInfoViewDic)
&& (entityInfoViewDic as ITypeSafeDictionary<EntityInfoViewComponent>).TryGetValue(
fromEntityGID.entityID, out var entityInfoView))
MoveEntityComponents(fromEntityGID, toEntityGID, entityInfoView.componentsToBuild, fromGroup
, sampler);
//Check if there is an EntityInfo linked to this entity, if so it's a DynamicEntityDescriptor!
if (fromGroup.TryGetValue(new RefWrapperType(ComponentBuilderUtilities.ENTITY_INFO_COMPONENT)
, out var entityInfoDic)
&& (entityInfoDic as ITypeSafeDictionary<EntityInfoComponent>).TryGetValue(
fromEntityGID.entityID, out var entityInfo))
SwapOrRemoveEntityComponents(fromEntityGID, toEntityGID, entityInfo.componentsToBuild, fromGroup
, sampler);
//otherwise it's a normal static entity descriptor
else
MoveEntityComponents(fromEntityGID, toEntityGID, componentBuilders, fromGroup, sampler);
SwapOrRemoveEntityComponents(fromEntityGID, toEntityGID, componentBuilders, fromGroup, sampler);
}
}

void MoveEntityComponents(EGID fromEntityGID, EGID? toEntityGID, IComponentBuilder[] entitiesToMove
, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, in PlatformProfiler sampler)
void SwapOrRemoveEntityComponents(EGID fromEntityGID, EGID? toEntityGID, IComponentBuilder[] entitiesToMove
, FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup, in PlatformProfiler sampler)
{
using (sampler.Sample("MoveEntityComponents"))
{
var length = entitiesToMove.Length;

FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = null;
FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup = null;

//Swap is not like adding a new entity. While adding new entities happen at the end of submission
//Adding an entity to a group due to a swap of groups happens now.
if (toEntityGID != null)
{
var toGroupID = toEntityGID.Value.groupID;
@@ -101,36 +112,38 @@ namespace Svelto.ECS
//Add all the entities to the dictionary
for (var i = 0; i < length; i++)
CopyEntityToDictionary(fromEntityGID, toEntityGID.Value, fromGroup, toGroup
, entitiesToMove[i].GetEntityComponentType(), sampler);
, entitiesToMove[i].GetEntityComponentType(), sampler);
}

//call all the callbacks
for (var i = 0; i < length; i++)
MoveEntityComponentFromAndToEngines(fromEntityGID, toEntityGID, fromGroup, toGroup
, entitiesToMove[i].GetEntityComponentType(), sampler);
ExecuteEnginesSwapOrRemoveCallbacks(fromEntityGID, toEntityGID, fromGroup, toGroup
, entitiesToMove[i].GetEntityComponentType(), sampler);

//then remove all the entities from the dictionary
for (var i = 0; i < length; i++)
RemoveEntityFromDictionary(fromEntityGID, fromGroup, entitiesToMove[i].GetEntityComponentType(), sampler);
RemoveEntityFromDictionary(fromEntityGID, fromGroup, entitiesToMove[i].GetEntityComponentType(),
sampler);
}
}

void CopyEntityToDictionary
(EGID entityGID, EGID toEntityGID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup
, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, Type entityComponentType, in PlatformProfiler sampler)
(EGID entityGID, EGID toEntityGID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup
, FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup, Type entityComponentType,
in PlatformProfiler sampler)
{
using (sampler.Sample("CopyEntityToDictionary"))
{
var wrapper = new RefWrapper<Type>(entityComponentType);
var wrapper = new RefWrapperType(entityComponentType);

ITypeSafeDictionary fromTypeSafeDictionary =
GetTypeSafeDictionary(entityGID.groupID, fromGroup, wrapper);

#if DEBUG && !PROFILE_SVELTO
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false)
{
throw new EntityNotFoundException(entityGID, entityComponentType);
}
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false)
{
throw new EntityNotFoundException(entityGID, entityComponentType);
}
#endif
ITypeSafeDictionary toEntitiesDictionary =
GetOrCreateTypeSafeDictionary(toEntityGID.groupID, toGroup, wrapper, fromTypeSafeDictionary);
@@ -139,15 +152,15 @@ namespace Svelto.ECS
}
}

void MoveEntityComponentFromAndToEngines
(EGID entityGID, EGID? toEntityGID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup
, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, Type entityComponentType
, in PlatformProfiler profiler)
void ExecuteEnginesSwapOrRemoveCallbacks
(EGID entityGID, EGID? toEntityGID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup
, FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup, Type entityComponentType
, in PlatformProfiler profiler)
{
using (profiler.Sample("MoveEntityComponentFromAndToEngines"))
{
//add all the entities
var refWrapper = new RefWrapper<Type>(entityComponentType);
var refWrapper = new RefWrapperType(entityComponentType);
var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper);

ITypeSafeDictionary toEntitiesDictionary = null;
@@ -155,24 +168,22 @@ namespace Svelto.ECS
toEntitiesDictionary = toGroup[refWrapper]; //this is guaranteed to exist by AddEntityToDictionary

#if DEBUG && !PROFILE_SVELTO
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false)
throw new EntityNotFoundException(entityGID, entityComponentType);
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false)
throw new EntityNotFoundException(entityGID, entityComponentType);
#endif
fromTypeSafeDictionary.MoveEntityFromEngines(entityGID, toEntityGID, toEntitiesDictionary
, toEntityGID == null
? _reactiveEnginesAddRemove
: _reactiveEnginesSwap, in profiler);
fromTypeSafeDictionary.ExecuteEnginesSwapOrRemoveCallbacks(entityGID, toEntityGID, toEntitiesDictionary
, toEntityGID == null ? _reactiveEnginesAddRemove : _reactiveEnginesSwap, in profiler);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
void RemoveEntityFromDictionary
(EGID entityGID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, Type entityComponentType
, in PlatformProfiler sampler)
(EGID entityGID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup, Type entityComponentType
, in PlatformProfiler sampler)
{
using (sampler.Sample("RemoveEntityFromDictionary"))
{
var refWrapper = new RefWrapper<Type>(entityComponentType);
var refWrapper = new RefWrapperType(entityComponentType);
var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper);

fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID);
@@ -181,65 +192,67 @@ namespace Svelto.ECS

/// <summary>
/// Swap all the entities from one group to another
///
/// TODO: write unit test that also tests that this calls MoveTo callbacks and not Add or Remove.
/// also that the passing EGID is the same of a component with EGID
/// </summary>
/// <param name="fromIdGroupId"></param>
/// <param name="toGroupId"></param>
/// <param name="profiler"></param>
void SwapEntitiesBetweenGroups(uint fromIdGroupId, uint toGroupId, in PlatformProfiler profiler)
void SwapEntitiesBetweenGroups(ExclusiveGroupStruct fromIdGroupId, ExclusiveGroupStruct toGroupId, in PlatformProfiler profiler)
{
using (profiler.Sample("SwapEntitiesBetweenGroups"))
{
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup = GetGroup(fromIdGroupId);
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = GetOrCreateGroup(toGroupId, profiler);
FasterDictionary<RefWrapperType, ITypeSafeDictionary> fromGroup = GetGroup(fromIdGroupId);
FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup = GetOrCreateGroup(toGroupId, profiler);

foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast dictionaryOfEntities
in fromGroup)
foreach (var dictionaryOfEntities in fromGroup)
{
//call all the MoveTo callbacks
dictionaryOfEntities.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove
, dictionaryOfEntities.Value
, new ExclusiveGroupStruct(toGroupId), profiler);

ITypeSafeDictionary toEntitiesDictionary =
GetOrCreateTypeSafeDictionary(toGroupId, toGroup, dictionaryOfEntities.Key
, dictionaryOfEntities.Value);
, dictionaryOfEntities.Value);

FasterDictionary<uint, ITypeSafeDictionary> groupsOfEntityType =
_groupsPerEntity[dictionaryOfEntities.Key];
var groupsOfEntityType = _groupsPerEntity[dictionaryOfEntities.Key];

ITypeSafeDictionary typeSafeDictionary = groupsOfEntityType[fromIdGroupId];
toEntitiesDictionary.AddEntitiesFromDictionary(typeSafeDictionary, toGroupId);
var groupOfEntitiesToCopyAndClear = groupsOfEntityType[fromIdGroupId];
toEntitiesDictionary.AddEntitiesFromDictionary(groupOfEntitiesToCopyAndClear, toGroupId);
//call all the MoveTo callbacks
dictionaryOfEntities.Value.ExecuteEnginesAddOrSwapCallbacks(_reactiveEnginesSwap
, dictionaryOfEntities.Value, new ExclusiveGroupStruct(fromIdGroupId), new ExclusiveGroupStruct(toGroupId), profiler);

typeSafeDictionary.FastClear();
//todo: if it's unmanaged, I can use fastclear
groupOfEntitiesToCopyAndClear.Clear();
}
}
}

FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> GetGroup(uint fromIdGroupId)
FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetGroup(ExclusiveGroupStruct fromIdGroupId)
{
if (_groupEntityComponentsDB.TryGetValue(fromIdGroupId
, out FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>
fromGroup) == false)
, out FasterDictionary<RefWrapperType, ITypeSafeDictionary>
fromGroup) == false)
throw new ECSException("Group doesn't exist: ".FastConcat(fromIdGroupId));

return fromGroup;
}

FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> GetOrCreateGroup(uint toGroupId, in PlatformProfiler profiler)
FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetOrCreateGroup(ExclusiveGroupStruct toGroupId,
in PlatformProfiler profiler)
{
using (profiler.Sample("GetOrCreateGroup"))
{
if (_groupEntityComponentsDB.TryGetValue(
toGroupId, out FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup) == false)
toGroupId, out FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup) == false)
toGroup = _groupEntityComponentsDB[toGroupId] =
new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>();
new FasterDictionary<RefWrapperType, ITypeSafeDictionary>();

return toGroup;
}
}

ITypeSafeDictionary GetOrCreateTypeSafeDictionary
(uint groupId, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, RefWrapper<Type> type
(ExclusiveGroupStruct groupId, FasterDictionary<RefWrapperType, ITypeSafeDictionary> toGroup, RefWrapperType type
, ITypeSafeDictionary fromTypeSafeDictionary)
{
//be sure that the TypeSafeDictionary for the entity Type exists
@@ -251,14 +264,14 @@ namespace Svelto.ECS

//update GroupsPerEntity
if (_groupsPerEntity.TryGetValue(type, out var groupedGroup) == false)
groupedGroup = _groupsPerEntity[type] = new FasterDictionary<uint, ITypeSafeDictionary>();
groupedGroup = _groupsPerEntity[type] = new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();

groupedGroup[groupId] = toEntitiesDictionary;
return toEntitiesDictionary;
}

static ITypeSafeDictionary GetTypeSafeDictionary
(uint groupID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> @group, RefWrapper<Type> refWrapper)
(uint groupID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> @group, RefWrapperType refWrapper)
{
if (@group.TryGetValue(refWrapper, out ITypeSafeDictionary fromTypeSafeDictionary) == false)
{
@@ -268,34 +281,24 @@ namespace Svelto.ECS
return fromTypeSafeDictionary;
}

void RemoveGroupAndEntities(uint groupID, in PlatformProfiler profiler)
void RemoveEntitiesFromGroup(ExclusiveGroupStruct groupID, in PlatformProfiler profiler)
{
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> dictionariesOfEntities =
_groupEntityComponentsDB[groupID];

foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast dictionaryOfEntities in dictionariesOfEntities)
if (_groupEntityComponentsDB.TryGetValue(groupID, out var dictionariesOfEntities))
{
dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler
, new ExclusiveGroupStruct(groupID));
dictionaryOfEntities.Value.FastClear();
foreach (FasterDictionary<RefWrapperType, ITypeSafeDictionary>.KeyValuePairFast dictionaryOfEntities
in dictionariesOfEntities)
{
dictionaryOfEntities.Value.ExecuteEnginesRemoveCallbacks(_reactiveEnginesAddRemove, profiler
, new ExclusiveGroupStruct(groupID));
dictionaryOfEntities.Value.FastClear();

FasterDictionary<uint, ITypeSafeDictionary> groupsOfEntityType =
_groupsPerEntity[dictionaryOfEntities.Key];
groupsOfEntityType[groupID].FastClear();
var groupsOfEntityType =
_groupsPerEntity[dictionaryOfEntities.Key];
groupsOfEntityType[groupID].FastClear();
}
}
}

internal Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityComponent
{
return _entitiesStream.GenerateConsumer<T>(name, capacity);
}

internal Consumer<T> GenerateConsumer<T>(ExclusiveGroupStruct group, string name, uint capacity)
where T : unmanaged, IEntityComponent
{
return _entitiesStream.GenerateConsumer<T>(group, name, capacity);
}

//one datastructure rule them all:
//split by group
//split by type per group. It's possible to get all the entities of a give type T per group thanks
@@ -303,17 +306,21 @@ namespace Svelto.ECS
//ID. This ID doesn't need to be the EGID, it can be just the entityID
//for each group id, save a dictionary indexed by entity type of entities indexed by id
// group EntityComponentType entityID, EntityComponent
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>
internal readonly FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>
_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
// EntityComponentType groupID entityID, EntityComponent
readonly FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> _groupsPerEntity;
// <EntityComponentType <groupID <entityID, EntityComponent>>>
internal readonly FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>>
_groupsPerEntity;

//The filters stored for each component and group
internal readonly FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>>
_groupFilters;

readonly EntitiesDB _entitiesDB;
readonly EntitiesStream _entitiesStream;
readonly EntitiesDB _entitiesDB;

EntitiesDB IUnitTestingInterface.entitiesForTesting => _entitiesDB;
}


+ 4
- 4
Svelto.ECS/EnginesRoot.GenericEntityFactory.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Svelto.Common;

@@ -14,7 +14,7 @@ namespace Svelto.ECS
}

public EntityComponentInitializer BuildEntity<T>
(uint entityID, ExclusiveGroupStruct groupStructId, IEnumerable<object> implementors = null)
(uint entityID, BuildGroup groupStructId, IEnumerable<object> implementors = null)
where T : IEntityDescriptor, new()
{
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId)
@@ -34,14 +34,14 @@ namespace Svelto.ECS
{
return _enginesRoot.Target.BuildEntity(egid, entityDescriptor.componentsToBuild, TypeCache<T>.type, implementors);
}
#if UNITY_BURST
#if UNITY_NATIVE
public NativeEntityFactory ToNative<T>(string memberName) where T : IEntityDescriptor, new()
{
return _enginesRoot.Target.ProvideNativeEntityFactoryQueue<T>(memberName);
}
#endif
public EntityComponentInitializer BuildEntity<T>
(uint entityID, ExclusiveGroupStruct groupStructId, T descriptorEntity, IEnumerable<object> implementors)
(uint entityID, BuildGroup groupStructId, T descriptorEntity, IEnumerable<object> implementors)
where T : IEntityDescriptor
{
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId)


+ 76
- 44
Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs View File

@@ -1,6 +1,8 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
@@ -8,7 +10,7 @@ namespace Svelto.ECS
{
/// <summary>
/// todo: EnginesRoot was a weakreference to give the change to inject
/// entityfunctions from other engines root. It probably should be reverted
/// entity functions from other engines root. It probably should be reverted
/// </summary>
class GenericEntityFunctions : IEntityFunctions
{
@@ -18,7 +20,7 @@ namespace Svelto.ECS
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntity<T>(uint entityID, ExclusiveGroupStruct groupID) where T :
public void RemoveEntity<T>(uint entityID, BuildGroup groupID) where T :
IEntityDescriptor, new()
{
RemoveEntity<T>(new EGID(entityID, groupID));
@@ -28,64 +30,91 @@ namespace Svelto.ECS
public void RemoveEntity<T>(EGID entityEGID) where T : IEntityDescriptor, new()
{
DBC.ECS.Check.Require(entityEGID.groupID != 0, "invalid group detected");
var descriptorComponentsToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild;
_enginesRoot.Target.CheckRemoveEntityID(entityEGID, TypeCache<T>.type);

_enginesRoot.Target.QueueEntitySubmitOperation<T>(
new EntitySubmitOperation(EntitySubmitOperationType.Remove, entityEGID, entityEGID,
EntityDescriptorTemplate<T>.descriptor.componentsToBuild));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAllEntities<T>(ExclusiveGroupStruct group) where T : IEntityDescriptor, new()
{
throw new NotImplementedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAllEntities<T>() where T : IEntityDescriptor, new()
{
throw new NotImplementedException();
descriptorComponentsToBuild));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveGroupAndEntities(ExclusiveGroupStruct groupID)
public void RemoveEntitiesFromGroup(BuildGroup groupID)
{
_enginesRoot.Target.RemoveGroupID(groupID);
DBC.ECS.Check.Require(groupID != 0, "invalid group detected");
_enginesRoot.Target.RemoveGroupID(groupID);

_enginesRoot.Target.QueueEntitySubmitOperation(
new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, new EGID(0, groupID), new EGID()));
}

// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// void RemoveAllEntities<D, S>(ExclusiveGroup group)
// where D : IEntityDescriptor, new() where S : unmanaged, IEntityComponent
// {
// var targetEntitiesDB = _enginesRoot.Target._entitiesDB;
// var (buffer, count) = targetEntitiesDB.QueryEntities<S>(@group);
// for (uint i = 0; i < count; ++i)
// {
// RemoveEntity<D>(new EGID(i, group));
// }
// }
//
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// void RemoveAllEntities<D, S>()
// where D : IEntityDescriptor, new() where S : unmanaged, IEntityComponent
// {
// var targetEntitiesDB = _enginesRoot.Target._entitiesDB;
// foreach (var ((buffer, count), exclusiveGroupStruct) in targetEntitiesDB.QueryEntities<S>())
// for (uint i = 0; i < count; ++i)
// {
// RemoveEntity<D>(new EGID(i, exclusiveGroupStruct));
// }
// }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntitiesInGroup<T>(ExclusiveGroupStruct fromGroupID, ExclusiveGroupStruct toGroupID)
public void SwapEntitiesInGroup<T>(BuildGroup fromGroupID, BuildGroup toGroupID)
where T : IEntityDescriptor, new()
{
throw new NotImplementedException("can't run this until I add the checks!");
#pragma warning disable 162
_enginesRoot.Target.QueueEntitySubmitOperation(
new EntitySubmitOperation(EntitySubmitOperationType.SwapGroup, new EGID(0, fromGroupID),
new EGID(0, toGroupID)));
#pragma warning restore 162
if (_enginesRoot.Target._groupEntityComponentsDB.TryGetValue(
fromGroupID.group, out FasterDictionary<RefWrapperType, ITypeSafeDictionary> entitiesInGroupPerType)
== true)
{
#if DEBUG && !PROFILE_SVELTO
IComponentBuilder[] components = EntityDescriptorTemplate<T>.descriptor.componentsToBuild;
var dictionary = entitiesInGroupPerType[new RefWrapperType(components[0].GetEntityComponentType())];

dictionary.KeysEvaluator((key) =>
{
_enginesRoot.Target.CheckRemoveEntityID(new EGID(key, fromGroupID), TypeCache<T>.type);
_enginesRoot.Target.CheckAddEntityID(new EGID(key, toGroupID), TypeCache<T>.type);
});

#endif
_enginesRoot.Target.QueueEntitySubmitOperation(
new EntitySubmitOperation(EntitySubmitOperationType.SwapGroup, new EGID(0, fromGroupID)
, new EGID(0, toGroupID)));
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(uint entityID, ExclusiveGroupStruct fromGroupID, ExclusiveGroupStruct toGroupID)
public void SwapEntityGroup<T>(uint entityID, BuildGroup fromGroupID,
BuildGroup toGroupID)
where T : IEntityDescriptor, new()
{
SwapEntityGroup<T>(new EGID(entityID, fromGroupID), toGroupID);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(EGID fromID, ExclusiveGroupStruct toGroupID)
public void SwapEntityGroup<T>(EGID fromID, BuildGroup toGroupID)
where T : IEntityDescriptor, new()
{
SwapEntityGroup<T>(fromID, new EGID(fromID.entityID, (uint) toGroupID));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(EGID fromID, ExclusiveGroupStruct toGroupID
, ExclusiveGroupStruct mustBeFromGroup)
public void SwapEntityGroup<T>(EGID fromID, BuildGroup toGroupID
, BuildGroup mustBeFromGroup)
where T : IEntityDescriptor, new()
{
if (fromID.groupID != mustBeFromGroup)
@@ -96,7 +125,7 @@ namespace Svelto.ECS

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(EGID fromID, EGID toID
, ExclusiveGroupStruct mustBeFromGroup)
, BuildGroup mustBeFromGroup)
where T : IEntityDescriptor, new()
{
if (fromID.groupID != mustBeFromGroup)
@@ -105,17 +134,17 @@ namespace Svelto.ECS
SwapEntityGroup<T>(fromID, toID);
}

#if UNITY_BURST
#if UNITY_NATIVE
public NativeEntityRemove ToNativeRemove<T>(string memberName) where T : IEntityDescriptor, new()
{
return _enginesRoot.Target.ProvideNativeEntityRemoveQueue<T>(memberName);
}
public NativeEntitySwap ToNativeSwap<T>(string memberName) where T : IEntityDescriptor, new()
{
return _enginesRoot.Target.ProvideNativeEntitySwapQueue<T>(memberName);
}
#endif
#endif

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(EGID fromID, EGID toID)
@@ -123,17 +152,20 @@ namespace Svelto.ECS
{
DBC.ECS.Check.Require(fromID.groupID != 0, "invalid group detected");
DBC.ECS.Check.Require(toID.groupID != 0, "invalid group detected");

var enginesRootTarget = _enginesRoot.Target;
var descriptorComponentsToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild;
_enginesRoot.Target.CheckRemoveEntityID(fromID, TypeCache<T>.type);
_enginesRoot.Target.CheckAddEntityID(toID, TypeCache<T>.type);
enginesRootTarget.CheckRemoveEntityID(fromID, TypeCache<T>.type);
enginesRootTarget.CheckAddEntityID(toID, TypeCache<T>.type);

_enginesRoot.Target.QueueEntitySubmitOperation<T>(
enginesRootTarget.QueueEntitySubmitOperation<T>(
new EntitySubmitOperation(EntitySubmitOperationType.Swap,
fromID, toID, EntityDescriptorTemplate<T>.descriptor.componentsToBuild));
fromID, toID, descriptorComponentsToBuild));
}

//enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside
//engines of other enginesRoot
//engines of other enginesRoot
readonly Svelto.DataStructures.WeakReference<EnginesRoot> _enginesRoot;
}

@@ -154,13 +186,13 @@ namespace Svelto.ECS
{
if (entitySubmitedOperation != entitySubmitOperation)
throw new ECSException("Only one entity operation per submission is allowed"
.FastConcat(" entityComponentType: ")
.FastConcat(typeof(T).Name)
.FastConcat(" submission type ", entitySubmitOperation.type.ToString(),
.FastConcat(" entityComponentType: ")
.FastConcat(typeof(T).Name)
.FastConcat(" submission type ", entitySubmitOperation.type.ToString(),
" from ID: ", entitySubmitOperation.fromID.entityID.ToString())
.FastConcat(" previous operation type: ",
.FastConcat(" previous operation type: ",
_entitiesOperations[(ulong) entitySubmitOperation.fromID].type
.ToString()));
.ToString()));
}
else
#endif


+ 28
- 8
Svelto.ECS/EnginesRoot.Submission.cs View File

@@ -27,11 +27,20 @@ namespace Svelto.ECS
}
}

/// <summary>
/// Todo: it would be probably better to split even further the logic between submission and callbacks
/// Something to do when I will optimize the callbacks
/// </summary>
/// <param name="profiler"></param>
void SingleSubmission(in PlatformProfiler profiler)
{
#if UNITY_BURST
#if UNITY_NATIVE
NativeOperationSubmission(profiler);
#endif
ClearChecks();

bool entitiesAreSubmitted = false;
if (_entitiesOperations.Count > 0)
{
using (profiler.Sample("Remove and Swap operations"))
@@ -56,7 +65,7 @@ namespace Svelto.ECS
entitiesOperations[i].fromID, null);
break;
case EntitySubmitOperationType.RemoveGroup:
RemoveGroupAndEntities(
RemoveEntitiesFromGroup(
entitiesOperations[i].fromID.groupID, profiler);
break;
case EntitySubmitOperationType.SwapGroup:
@@ -81,6 +90,8 @@ namespace Svelto.ECS
}
}
}

entitiesAreSubmitted = true;
}

_groupedEntityToAdd.Swap();
@@ -102,6 +113,15 @@ namespace Svelto.ECS
}
}
}
entitiesAreSubmitted = true;
}

if (entitiesAreSubmitted)
{
var enginesCount = _reactiveEnginesSubmission.count;
for (int i = 0; i < enginesCount; i++)
_reactiveEnginesSubmission[i].EntitiesSubmitted();
}
}

@@ -114,14 +134,14 @@ namespace Svelto.ECS
{
var groupID = groupOfEntitiesToSubmit.Key;
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> groupDB = GetOrCreateGroup(groupID, profiler);
var groupDB = GetOrCreateGroup(groupID, profiler);

//add the entityComponents in the group
foreach (var entityComponentsToSubmit in _groupedEntityToAdd.other[groupID])
{
var type = entityComponentsToSubmit.Key;
var targetTypeSafeDictionary = entityComponentsToSubmit.Value;
var wrapper = new RefWrapper<Type>(type);
var wrapper = new RefWrapperType(type);

ITypeSafeDictionary dbDic = GetOrCreateTypeSafeDictionary(groupID, groupDB, wrapper,
targetTypeSafeDictionary);
@@ -143,16 +163,16 @@ namespace Svelto.ECS

foreach (var entityComponentsToSubmit in _groupedEntityToAdd.other[groupID])
{
var realDic = groupDB[new RefWrapper<Type>(entityComponentsToSubmit.Key)];
var realDic = groupDB[new RefWrapperType(entityComponentsToSubmit.Key)];

entityComponentsToSubmit.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove, realDic,
new ExclusiveGroupStruct(groupToSubmit.Key), in profiler);
entityComponentsToSubmit.Value.ExecuteEnginesAddOrSwapCallbacks(_reactiveEnginesAddRemove, realDic,
null, new ExclusiveGroupStruct(groupToSubmit.Key), in profiler);
}
}
}
}

DoubleBufferedEntitiesToAdd _groupedEntityToAdd;
readonly DoubleBufferedEntitiesToAdd _groupedEntityToAdd;
readonly ThreadSafeDictionary<ulong, EntitySubmitOperation> _entitiesOperations;
}
}

+ 63
- 35
Svelto.ECS/EntitiesDB.FindGroups.cs View File

@@ -7,20 +7,12 @@ namespace Svelto.ECS
{
public partial class EntitiesDB
{
internal FasterDictionary<uint, ITypeSafeDictionary> FindGroups_INTERNAL<T1>() where T1 : IEntityComponent
{
if (_groupsPerEntity.ContainsKey(TypeRefWrapper<T1>.wrapper) == false)
return _emptyDictionary;

return _groupsPerEntity[TypeRefWrapper<T1>.wrapper];
}
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1>() where T1 : IEntityComponent
{
FasterList<ExclusiveGroupStruct> result = groups.Value;
result.FastClear();
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T1>.wrapper
, out FasterDictionary<uint, ITypeSafeDictionary> result1) == false)
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T1>.wrapper
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> result1) == false)
return result;
var result1Count = result1.count;
@@ -38,11 +30,11 @@ namespace Svelto.ECS
{
FasterList<ExclusiveGroupStruct> result = groups.Value;
result.FastClear();
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T1>.wrapper
, out FasterDictionary<uint, ITypeSafeDictionary> result1) == false)
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T1>.wrapper
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> result1) == false)
return result;
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T2>.wrapper
, out FasterDictionary<uint, ITypeSafeDictionary> result2) == false)
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T2>.wrapper
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> result2) == false)
return result;
var result1Count = result1.count;
@@ -52,12 +44,13 @@ namespace Svelto.ECS

for (int i = 0; i < result1Count; i++)
{
var groupID = fasterDictionaryNodes1[i].key;
for (int j = 0; j < result2Count; j++)
{
//if the same group is found used with both T1 and T2
if (fasterDictionaryNodes1[i].key == fasterDictionaryNodes2[j].key)
if (groupID == fasterDictionaryNodes2[j].key)
{
result.Add(new ExclusiveGroupStruct(fasterDictionaryNodes1[i].key));
result.Add(new ExclusiveGroupStruct(groupID));
break;
}
}
@@ -77,33 +70,68 @@ namespace Svelto.ECS
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1, T2, T3>()
where T1 : IEntityComponent where T2 : IEntityComponent where T3 : IEntityComponent
{
FindGroups<T1, T2>();
FasterList<ExclusiveGroupStruct> result = groups.Value;

if (result.count == 0)
result.FastClear();
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T1>.wrapper
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> groupOfEntities1) == false)
return result;
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T3>.wrapper
, out FasterDictionary<uint, ITypeSafeDictionary> result3) == false)
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T2>.wrapper
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> groupOfEntities2) == false)
return result;

var result3Count = result3.count;
var fasterDictionaryNodes3 = result3.unsafeKeys;

for (int j = 0; j < result3Count; j++)
for (int i = (int) 0; i < result.count; i++)
if (groupsPerEntity.TryGetValue(TypeRefWrapper<T3>.wrapper
, out FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> groupOfEntities3) == false)
return result;
var result1Count = groupOfEntities1.count;
var result2Count = groupOfEntities2.count;
var result3Count = groupOfEntities3.count;
var fasterDictionaryNodes1 = groupOfEntities1.unsafeKeys;
var fasterDictionaryNodes2 = groupOfEntities2.unsafeKeys;
var fasterDictionaryNodes3 = groupOfEntities3.unsafeKeys;
//
//TODO: I have to find once for ever a solution to be sure that the entities in the groups match
//Currently this returns group where the entities are found, but the entities may not match in these
//groups.
//Checking the size of the entities is an early check, needed, but not sufficient, as entities components may
//coincidentally match in number but not from which entities they are generated
//foreach group where T1 is found
for (int i = 0; i < result1Count; i++)
{
if (fasterDictionaryNodes3[j].key == result[i])
break;

result.UnorderedRemoveAt(i);
i--;
var groupT1 = fasterDictionaryNodes1[i].key;
//foreach group where T2 is found
for (int j = 0; j < result2Count; ++j)
{
if (groupT1 == fasterDictionaryNodes2[j].key)
{
//foreach group where T3 is found
for (int k = 0; k < result3Count; ++k)
{
if (groupT1 == fasterDictionaryNodes3[k].key)
{
result.Add(new ExclusiveGroupStruct(groupT1));
break;
}
}
break;
}
}
}

return result;
}

internal FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> FindGroups_INTERNAL(Type type)
{
if (groupsPerEntity.ContainsKey(new RefWrapperType(type)) == false)
return _emptyDictionary;

return groupsPerEntity[new RefWrapperType(type)];
}

struct GroupsList
{
static GroupsList()


+ 154
- 116
Svelto.ECS/EntitiesDB.cs View File

@@ -6,36 +6,31 @@ using System;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Hybrid;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public partial class EntitiesDB
{
internal EntitiesDB
(FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> groupEntityComponentsDB
, FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> groupsPerEntity
, EntitiesStream entityStream)
internal EntitiesDB(EnginesRoot enginesRoot)
{
_groupEntityComponentsDB = groupEntityComponentsDB;
_groupsPerEntity = groupsPerEntity;
_entityStream = entityStream;
_enginesRoot = enginesRoot;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T QueryUniqueEntity<T>(ExclusiveGroupStruct group) where T : struct, IEntityComponent
EntityCollection<T> InternalQueryEntities<T>(FasterDictionary<RefWrapperType, ITypeSafeDictionary> entitiesInGroupPerType)
where T : struct, IEntityComponent
{
var entities = QueryEntities<T>(group);
uint count = 0;
IBuffer<T> buffer;
if (SafeQueryEntityDictionary<T>(out var typeSafeDictionary, entitiesInGroupPerType) == false)
buffer = RetrieveEmptyEntityComponentArray<T>();
else
{
var safeDictionary = (typeSafeDictionary as ITypeSafeDictionary<T>);
buffer = safeDictionary.GetValues(out count);
}

#if DEBUG && !PROFILE_SVELTO
if (entities.count == 0)
throw new ECSException("Unique entity not found '".FastConcat(typeof(T).ToString()).FastConcat("'"));
if (entities.count != 1)
throw new ECSException("Unique entities must be unique! '".FastConcat(typeof(T).ToString())
.FastConcat("'"));
#endif
return ref entities[0];
return new EntityCollection<T>(buffer, count);
}

/// <summary>
@@ -49,33 +44,34 @@ namespace Svelto.ECS
public EntityCollection<T> QueryEntities<T>(ExclusiveGroupStruct groupStructId)
where T : struct, IEntityComponent
{
IBuffer<T> buffer;
uint count = 0;

if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false)
buffer = RetrieveEmptyEntityComponentArray<T>();
else
if (groupEntityComponentsDB.TryGetValue(groupStructId, out var entitiesInGroupPerType) == false)
{
var safeDictionary = (typeSafeDictionary as ITypeSafeDictionary<T>);
buffer = safeDictionary.GetValues(out count);
var buffer = RetrieveEmptyEntityComponentArray<T>();
return new EntityCollection<T>(buffer, 0);
}

return new EntityCollection<T>(buffer, count);
return InternalQueryEntities<T>(entitiesInGroupPerType);
}

public EntityCollection<T1, T2> QueryEntities<T1, T2>(ExclusiveGroupStruct groupStruct)
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
{
var T1entities = QueryEntities<T1>(groupStruct);
var T2entities = QueryEntities<T2>(groupStruct);
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
{
return new EntityCollection<T1, T2>(new EntityCollection<T1>(RetrieveEmptyEntityComponentArray<T1>(), 0),
new EntityCollection<T2>(RetrieveEmptyEntityComponentArray<T2>(), 0));
}
var T1entities = InternalQueryEntities<T1>(entitiesInGroupPerType);
var T2entities = InternalQueryEntities<T2>(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())));
#endif
.FastConcat(T1entities.count).FastConcat(" ", typeof(T1).ToString())
.FastConcat("'. Entity 2: ' count: ".FastConcat(T2entities.count)
.FastConcat(" ", typeof(T2).ToString())
.FastConcat("' group: ", groupStruct.ToName())));
#endif

return new EntityCollection<T1, T2>(T1entities, T2entities);
}
@@ -83,66 +79,87 @@ namespace Svelto.ECS
public EntityCollection<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroupStruct groupStruct)
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent
{
var T1entities = QueryEntities<T1>(groupStruct);
var T2entities = QueryEntities<T2>(groupStruct);
var T3entities = QueryEntities<T3>(groupStruct);
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
{
return new EntityCollection<T1, T2, T3>(
new EntityCollection<T1>(RetrieveEmptyEntityComponentArray<T1>(), 0),
new EntityCollection<T2>(RetrieveEmptyEntityComponentArray<T2>(), 0),
new EntityCollection<T3>(RetrieveEmptyEntityComponentArray<T3>(), 0));
}
var T1entities = InternalQueryEntities<T1>(entitiesInGroupPerType);
var T2entities = InternalQueryEntities<T2>(entitiesInGroupPerType);
var T3entities = InternalQueryEntities<T3>(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)));
#endif
.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)));
#endif

return new EntityCollection<T1, T2, T3>(T1entities, T2entities, T3entities);
}
public int IterateOverGroupsAndCount<T>
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T : struct, IEntityComponent
public EntityCollection<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4>(ExclusiveGroupStruct groupStruct)
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent where T4 : struct, IEntityComponent
{
int count = 0;
for (int i = 0; i < groups.count; i++)
if (groupEntityComponentsDB.TryGetValue(groupStruct, out var entitiesInGroupPerType) == false)
{
count += Count<T>(groups[i]);
return new EntityCollection<T1, T2, T3, T4>(
new EntityCollection<T1>(RetrieveEmptyEntityComponentArray<T1>(), 0),
new EntityCollection<T2>(RetrieveEmptyEntityComponentArray<T2>(), 0),
new EntityCollection<T3>(RetrieveEmptyEntityComponentArray<T3>(), 0),
new EntityCollection<T4>(RetrieveEmptyEntityComponentArray<T4>(), 0));
}
var T1entities = InternalQueryEntities<T1>(entitiesInGroupPerType);
var T2entities = InternalQueryEntities<T2>(entitiesInGroupPerType);
var T3entities = InternalQueryEntities<T3>(entitiesInGroupPerType);
var T4entities = InternalQueryEntities<T4>(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)));
#endif

return 0;
return new EntityCollection<T1, T2, T3, T4>(T1entities, T2entities, T3entities, T4entities);
}

public TupleRef<T> QueryEntities<T>
public GroupsEnumerable<T> QueryEntities<T>
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T : struct, IEntityComponent
{
return new TupleRef<T>(new EntityCollections<T>(this, groups), new GroupsEnumerable<T>(this, groups));
return new GroupsEnumerable<T>(this, groups);
}

public TupleRef<T1, T2> QueryEntities<T1, T2>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
public GroupsEnumerable<T1, T2> QueryEntities<T1, T2>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
{
return new TupleRef<T1, T2>(new EntityCollections<T1, T2>(this, groups)
, new GroupsEnumerable<T1, T2>(this, groups));
return new GroupsEnumerable<T1, T2>(this, groups);
}

public TupleRef<T1, T2, T3> QueryEntities<T1, T2, T3>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
public GroupsEnumerable<T1, T2, T3> QueryEntities<T1, T2, T3>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent
{
return new TupleRef<T1, T2, T3>(new EntityCollections<T1, T2, T3>(this, groups)
, new GroupsEnumerable<T1, T2, T3>(this, groups));
return new GroupsEnumerable<T1, T2, T3>(this, groups);
}

public TupleRef<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4>
public GroupsEnumerable<T1, T2, T3, T4> QueryEntities<T1, T2, T3, T4>
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
where T1 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T3 : struct, IEntityComponent
where T4 : struct, IEntityComponent
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
where T3 : struct, IEntityComponent where T4 : struct, IEntityComponent
{
return new TupleRef<T1, T2, T3, T4>(new EntityCollections<T1, T2, T3, T4>(this, groups)
, new GroupsEnumerable<T1, T2, T3, T4>(this, groups));
return new GroupsEnumerable<T1, T2, T3, T4>(this, groups);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -150,11 +167,11 @@ namespace Svelto.ECS
where T : struct, IEntityComponent
{
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false)
throw new EntityGroupNotFoundException(typeof(T));
throw new EntityGroupNotFoundException(typeof(T) , groupStructId.ToName());

return (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryQueryMappedEntities<T>
(ExclusiveGroupStruct groupStructId, out EGIDMapper<T> mapper) where T : struct, IEntityComponent
@@ -190,8 +207,8 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ExistsAndIsNotEmpty(ExclusiveGroupStruct gid)
{
if (_groupEntityComponentsDB.TryGetValue(
gid, out FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group) == true)
if (groupEntityComponentsDB.TryGetValue(
gid, out FasterDictionary<RefWrapperType, ITypeSafeDictionary> group) == true)
{
return group.count > 0;
}
@@ -210,50 +227,36 @@ namespace Svelto.ECS
{
if (SafeQueryEntityDictionary<T>(groupStruct, out var typeSafeDictionary) == false)
return 0;
return (int) typeSafeDictionary.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PublishEntityChange<T>(EGID egid) where T : unmanaged, IEntityComponent
public bool FoundInGroups<T1>() where T1 : IEntityComponent
{
_entityStream.PublishEntity(ref this.QueryEntity<T>(egid), egid);
return groupsPerEntity.ContainsKey(TypeRefWrapper<T1>.wrapper);
}

[Obsolete("<color=orange>This Method will be removed soon. please use QueryEntities instead</color>")]
public void ExecuteOnAllEntities<T>(ExecuteOnAllEntitiesAction<T> action) where T : struct, IEntityComponent
{
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T>.wrapper, out var dictionary))
foreach (var pair in dictionary)
{
IBuffer<T> entities = (pair.Value as ITypeSafeDictionary<T>).GetValues(out var count);

if (count > 0)
action(entities, new ExclusiveGroupStruct(pair.Key), count, this);
}
}
public bool IsDisposing => _enginesRoot._isDisposing;

[Obsolete("<color=orange>This Method will be removed soon. please use QueryEntities instead</color>")]
public void ExecuteOnAllEntities<T, W>(ref W value, ExecuteOnAllEntitiesAction<T, W> action)
where T : struct, IEntityComponent
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool SafeQueryEntityDictionary<T>(out ITypeSafeDictionary typeSafeDictionary,
FasterDictionary<RefWrapperType, ITypeSafeDictionary> entitiesInGroupPerType)
where T : IEntityComponent
{
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T>.wrapper, out var dic))
foreach (var pair in dic)
{
IBuffer<T> entities = (pair.Value as ITypeSafeDictionary<T>).GetValues(out var innerCount);
if (entitiesInGroupPerType.TryGetValue(new RefWrapperType(TypeCache<T>.type), out var safeDictionary) == false)
{
typeSafeDictionary = default;
return false;
}

if (innerCount > 0)
action(entities, new ExclusiveGroupStruct(pair.Key), innerCount, this, ref value);
}
}
//return the indexes entities if they exist
typeSafeDictionary = safeDictionary;

public bool FoundInGroups<T1>() where T1 : IEntityComponent
{
return _groupsPerEntity.ContainsKey(TypeRefWrapper<T1>.wrapper);
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool SafeQueryEntityDictionary<T>(uint group, out ITypeSafeDictionary typeSafeDictionary)
internal bool SafeQueryEntityDictionary<T>(ExclusiveGroupStruct group, out ITypeSafeDictionary typeSafeDictionary)
where T : IEntityComponent
{
if (UnsafeQueryEntityDictionary(group, TypeCache<T>.type, out var safeDictionary) == false)
@@ -269,17 +272,47 @@ namespace Svelto.ECS
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool UnsafeQueryEntityDictionary(uint group, Type type, out ITypeSafeDictionary typeSafeDictionary)
internal bool UnsafeQueryEntityDictionary(ExclusiveGroupStruct group, Type type, out ITypeSafeDictionary typeSafeDictionary)
{
//search for the group
if (_groupEntityComponentsDB.TryGetValue(group, out var entitiesInGroupPerType) == false)
if (groupEntityComponentsDB.TryGetValue(group, out var entitiesInGroupPerType) == false)
{
typeSafeDictionary = null;
return false;
}

//search for the indexed entities in the group
return entitiesInGroupPerType.TryGetValue(new RefWrapper<Type>(type), out typeSafeDictionary);
return entitiesInGroupPerType.TryGetValue(new RefWrapperType(type), out typeSafeDictionary);
}

internal bool FindIndex(uint entityID, ExclusiveGroupStruct @group, Type type, out uint index)
{
EGID entityGID = new EGID(entityID, @group);

index = default;
if (UnsafeQueryEntityDictionary(@group, type, out var safeDictionary) == false)
return false;

if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false)
return false;

return true;
}

internal uint GetIndex(uint entityID, ExclusiveGroupStruct @group, Type type)
{
EGID entityGID = new EGID(entityID, @group);
if (UnsafeQueryEntityDictionary(@group, type, out var safeDictionary) == false)
{
throw new EntityNotFoundException(entityGID, type);
}

if (safeDictionary.TryFindIndex(entityGID.entityID, out var index) == false)
throw new EntityNotFoundException(entityGID, type);

return index;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -309,20 +342,25 @@ namespace Svelto.ECS
}
}

readonly FasterDictionary<uint, ITypeSafeDictionary> _emptyDictionary =
new FasterDictionary<uint, ITypeSafeDictionary>();
static readonly FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary> _emptyDictionary =
new FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>();

readonly EntitiesStream _entityStream;
readonly EnginesRoot _enginesRoot;

EntitiesStreams _entityStream => _enginesRoot._entityStreams;

//grouped set of entity components, this is the standard way to handle entity components are grouped per
//group, then indexable per type, then indexable per EGID. however the TypeSafeDictionary can return an array of
//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.
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>
_groupEntityComponentsDB;

//needed to be able to track in which groups a specific entity type can be found.
//may change in future as it could be expanded to support queries
readonly FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> _groupsPerEntity;
FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>
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
// <EntityComponentType <groupID <entityID, EntityComponent>>>
FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>> groupsPerEntity =>
_enginesRoot._groupsPerEntity;
}
}

+ 23
- 171
Svelto.ECS/EntityCollection.cs View File

@@ -1,5 +1,4 @@
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

@@ -7,7 +6,7 @@ namespace Svelto.ECS
{
public readonly ref struct EntityCollection<T> where T : struct, IEntityComponent
{
static readonly bool IsUnmanaged = TypeSafeDictionary<T>._isUmanaged;
static readonly bool IsUnmanaged = TypeSafeDictionary<T>.IsUnmanaged;
public EntityCollection(IBuffer<T> buffer, uint count):this()
{
@@ -17,74 +16,19 @@ namespace Svelto.ECS
_managedBuffer = (MB<T>) buffer;
_count = count;
_buffer = buffer;
}

public uint count => _count;

internal readonly MB<T> _managedBuffer;
internal readonly NB<T> _nativedBuffer;
readonly uint _count;

//todo very likely remove this
public ref T this[uint i]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (IsUnmanaged)
return ref _nativedBuffer[i];
else
return ref _managedBuffer[i];
}
}

public ref T this[int i]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (IsUnmanaged)
return ref _nativedBuffer[i];
else
return ref _managedBuffer[i];
}
}

//TODO SOON: ALL THIS STUFF BELOW MUST DISAPPEAR
readonly IBuffer<T> _buffer;

//todo to remove
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityIterator GetEnumerator() { return new EntityIterator(_buffer, _count); }
//todo to remove
public ref struct EntityIterator
{
public EntityIterator(IBuffer<T> array, uint count) : this()
{
_array = array;
_count = count;
_index = -1;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext() { return ++_index < _count; }

public ref T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _array[_index];
}

readonly IBuffer<T> _array;
readonly uint _count;
int _index;
}
readonly uint _count;
}

public readonly ref struct EntityCollection<T1, T2> where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
{
public EntityCollection(in EntityCollection<T1> array1, in EntityCollection<T2> array2)
internal EntityCollection(in EntityCollection<T1> array1, in EntityCollection<T2> array2)
{
_array1 = array1;
_array2 = array2;
@@ -92,14 +36,13 @@ namespace Svelto.ECS

public uint count => _array1.count;

//todo to remove
public EntityCollection<T2> Item2
internal EntityCollection<T2> buffer2
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array2;
}
//todo to remove
public EntityCollection<T1> Item1
internal EntityCollection<T1> buffer1
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array1;
@@ -107,81 +50,52 @@ namespace Svelto.ECS

readonly EntityCollection<T1> _array1;
readonly EntityCollection<T2> _array2;
//todo to remove
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityIterator GetEnumerator() { return new EntityIterator(this); }
//todo to remove
public ref struct EntityIterator
{
public EntityIterator(in EntityCollection<T1, T2> array1) : this()
{
_array1 = array1;
_count = array1.count;
_index = -1;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext() { return ++_index < _count; }

public void Reset() { _index = -1; }

public ValueRef<T1, T2> Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new ValueRef<T1, T2>(_array1, (uint) _index);
}

readonly EntityCollection<T1, T2> _array1;
readonly uint _count;
int _index;
}

}

public readonly ref struct EntityCollection<T1, T2, T3> where T3 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T1 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T1 : struct, IEntityComponent
{
public EntityCollection
internal EntityCollection
(in EntityCollection<T1> array1, in EntityCollection<T2> array2, in EntityCollection<T3> array3)
{
_array1 = array1;
_array2 = array2;
_array3 = array3;
}
//todo to remove
public EntityCollection<T1> Item1

internal EntityCollection<T1> buffer1
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array1;
}
//todo to remove
public EntityCollection<T2> Item2

internal EntityCollection<T2> buffer2
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array2;
}
//todo to remove
public EntityCollection<T3> Item3

internal EntityCollection<T3> buffer3
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array3;
}

public uint count => Item1.count;
internal uint count => buffer1.count;

readonly EntityCollection<T1> _array1;
readonly EntityCollection<T2> _array2;
readonly EntityCollection<T3> _array3;
}
public readonly ref struct EntityCollection<T1, T2, T3, T4>
where T1 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T3 : struct, IEntityComponent
where T4 : struct, IEntityComponent
{
public EntityCollection
internal EntityCollection
(in EntityCollection<T1> array1, in EntityCollection<T2> array2, in EntityCollection<T3> array3, in EntityCollection<T4> array4)
{
_array1 = array1;
@@ -190,35 +104,31 @@ namespace Svelto.ECS
_array4 = array4;
}

//todo to remove
public EntityCollection<T1> Item1
internal EntityCollection<T1> Item1
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array1;
}

//todo to remove
public EntityCollection<T2> Item2
internal EntityCollection<T2> Item2
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array2;
}

//todo to remove
public EntityCollection<T3> Item3
internal EntityCollection<T3> Item3
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array3;
}
//todo to remove
public EntityCollection<T4> Item4
internal EntityCollection<T4> Item4
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array4;
}

public uint count => _array1.count;
internal uint count => _array1.count;

readonly EntityCollection<T1> _array1;
readonly EntityCollection<T2> _array2;
@@ -308,62 +218,4 @@ namespace Svelto.ECS
count = this.count;
}
}

public readonly ref struct ValueRef<T1, T2> where T2 : struct, IEntityComponent where T1 : struct, IEntityComponent
{
readonly EntityCollection<T1, T2> array1;

readonly uint index;

public ValueRef(in EntityCollection<T1, T2> entity2, uint i)
{
array1 = entity2;
index = i;
}

public ref T1 entityComponentA
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref array1.Item1[index];
}

public ref T2 entityComponentB
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref array1.Item2[index];
}
}

public readonly ref struct ValueRef<T1, T2, T3> where T2 : struct, IEntityComponent
where T1 : struct, IEntityComponent
where T3 : struct, IEntityComponent
{
readonly EntityCollection<T1, T2, T3> array1;

readonly uint index;

public ValueRef(in EntityCollection<T1, T2, T3> entity, uint i)
{
array1 = entity;
index = i;
}

public ref T1 entityComponentA
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref array1.Item1[index];
}

public ref T2 entityComponentB
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref array1.Item2[index];
}

public ref T3 entityComponentC
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref array1.Item3[index];
}
}
}

+ 0
- 266
Svelto.ECS/EntityCollections.cs View File

@@ -1,266 +0,0 @@
using System;
using System.Runtime.CompilerServices;
using Svelto.DataStructures;

namespace Svelto.ECS
{
public readonly ref struct EntityCollections<T1, T2, T3, T4> where T1 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T3 : struct, IEntityComponent
where T4 : struct, IEntityComponent
{
public EntityCollections(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this()
{
_db = db;
_groups = groups;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityGroupsIterator GetEnumerator()
{
throw new NotImplementedException("tell seb to finish this one");
#pragma warning disable 162
return new EntityGroupsIterator(_db, _groups);
#pragma warning restore 162
}

readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

public ref struct EntityGroupsIterator
{
public EntityGroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this()
{
_db = db;
_groups = groups;
_indexGroup = -1;
_index = -1;
}

public bool MoveNext()
{
//attention, the while is necessary to skip empty groups
while (_index + 1 >= _count && ++_indexGroup < _groups.count)
{
_index = -1;
_array1 = _db.QueryEntities<T1, T2, T3>(_groups[_indexGroup]);
_count = _array1.count;
}

return ++_index < _count;
}

public void Reset()
{
_index = -1;
_indexGroup = -1;

_array1 = _db.QueryEntities<T1, T2, T3>(_groups[0]);
_count = _array1.count;
}

public ValueRef<T1, T2, T3> Current
{
get
{
var valueRef = new ValueRef<T1, T2, T3>(_array1, (uint) _index);
return valueRef;
}
}

readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;
uint _count;
int _index;
int _indexGroup;

EntityCollection<T1, T2, T3> _array1;
}
}

public readonly ref struct EntityCollections<T1, T2, T3> where T1 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T3 : struct, IEntityComponent
{
public EntityCollections(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this()
{
_db = db;
_groups = groups;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityGroupsIterator GetEnumerator() { return new EntityGroupsIterator(_db, _groups); }

readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

public ref struct EntityGroupsIterator
{
public EntityGroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this()
{
_db = db;
_groups = groups;
_indexGroup = -1;
_index = -1;
}

public bool MoveNext()
{
//attention, the while is necessary to skip empty groups
while (_index + 1 >= _count && ++_indexGroup < _groups.count)
{
_index = -1;
_array1 = _db.QueryEntities<T1, T2, T3>(_groups[_indexGroup]);
_count = _array1.count;
}

return ++_index < _count;
}

public void Reset()
{
_index = -1;
_indexGroup = -1;

_array1 = _db.QueryEntities<T1, T2, T3>(_groups[0]);
_count = _array1.count;
}

public ValueRef<T1, T2, T3> Current
{
get
{
var valueRef = new ValueRef<T1, T2, T3>(_array1, (uint) _index);
return valueRef;
}
}

readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;
uint _count;
int _index;
int _indexGroup;

EntityCollection<T1, T2, T3> _array1;
}
}

public readonly ref struct EntityCollections<T1, T2>
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
{
public EntityCollections(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this()
{
_db = db;
_groups = groups;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityGroupsIterator GetEnumerator() { return new EntityGroupsIterator(_db, _groups); }

readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

public ref struct EntityGroupsIterator
{
public EntityGroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this()
{
_db = db;
_groups = groups;
_indexGroup = -1;
_index = -1;
}

public bool MoveNext()
{
//attention, the while is necessary to skip empty groups
while (_index + 1 >= _array1.count && ++_indexGroup < _groups.count)
{
_index = -1;
_array1 = _db.QueryEntities<T1, T2>(_groups[_indexGroup]);
}

return ++_index < _array1.count;
}

public void Reset()
{
_index = -1;
_indexGroup = -1;

_array1 = _db.QueryEntities<T1, T2>(_groups[0]);
}

public ValueRef<T1, T2> Current
{
get
{
var valueRef = new ValueRef<T1, T2>(_array1, (uint) _index);
return valueRef;
}
}

readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;
int _index;
int _indexGroup;

EntityCollection<T1, T2> _array1;
}
}

public readonly ref struct EntityCollections<T> where T : struct, IEntityComponent
{
public EntityCollections(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this()
{
_db = db;
_groups = groups;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityGroupsIterator GetEnumerator() { return new EntityGroupsIterator(_db, _groups); }

readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

public ref struct EntityGroupsIterator
{
public EntityGroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) : this()
{
_db = db;
_groups = groups;
_indexGroup = -1;
_index = -1;
}

public bool MoveNext()
{
//attention, the while is necessary to skip empty groups
while (_index + 1 >= _count && ++_indexGroup < _groups.count)
{
_index = -1;
_array = _db.QueryEntities<T>(_groups[_indexGroup]);
_count = _array.count;
}

return ++_index < _count;
}

public void Reset()
{
_index = -1;
_indexGroup = -1;
_count = 0;
}

public ref T Current => ref _array[(uint) _index];

readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

EntityCollection<T> _array;
uint _count;
int _index;
int _indexGroup;
}
}
}

+ 6
- 6
Svelto.ECS/EntityComponentInitializer.cs View File

@@ -6,7 +6,7 @@ namespace Svelto.ECS
{
public readonly ref struct EntityComponentInitializer
{
public EntityComponentInitializer(EGID id, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group)
public EntityComponentInitializer(EGID id, FasterDictionary<RefWrapperType, ITypeSafeDictionary> group)
{
_group = group;
_ID = id;
@@ -16,7 +16,7 @@ namespace Svelto.ECS

public void Init<T>(T initializer) where T : struct, IEntityComponent
{
if (_group.TryGetValue(new RefWrapper<Type>(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE),
if (_group.TryGetValue(new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE),
out var typeSafeDictionary) == false) return;

var dictionary = (ITypeSafeDictionary<T>) typeSafeDictionary;
@@ -30,7 +30,7 @@ namespace Svelto.ECS

public ref T GetOrCreate<T>() where T : struct, IEntityComponent
{
ref var entityDictionary = ref _group.GetOrCreate(new RefWrapper<Type>(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE)
ref var entityDictionary = ref _group.GetOrCreate(new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE)
, TypeSafeDictionaryFactory<T>.Create);
var dictionary = (ITypeSafeDictionary<T>) entityDictionary;

@@ -39,13 +39,13 @@ namespace Svelto.ECS
public ref T Get<T>() where T : struct, IEntityComponent
{
return ref (_group[new RefWrapper<Type>(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE)] as ITypeSafeDictionary<T>)[
return ref (_group[new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE)] as ITypeSafeDictionary<T>)[
_ID.entityID];
}
public bool Has<T>() where T : struct, IEntityComponent
{
if (_group.TryGetValue(new RefWrapper<Type>(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE),
if (_group.TryGetValue(new RefWrapperType(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE),
out var typeSafeDictionary))
{
var dictionary = (ITypeSafeDictionary<T>) typeSafeDictionary;
@@ -58,6 +58,6 @@ namespace Svelto.ECS
}
readonly EGID _ID;
readonly FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> _group;
readonly FasterDictionary<RefWrapperType, ITypeSafeDictionary> _group;
}
}

+ 11
- 2
Svelto.ECS/EntityDescriptorTemplate.cs View File

@@ -1,17 +1,26 @@
using System;

namespace Svelto.ECS
{
public interface IEntityDescriptor
{
IComponentBuilder[] componentsToBuild { get; }
}
public interface IDynamicEntityDescriptor: IEntityDescriptor
{
}

static class EntityDescriptorTemplate<TType> where TType : IEntityDescriptor, new()
{
static EntityDescriptorTemplate()
{
descriptor = new TType();
realDescriptor = new TType();
descriptor = realDescriptor;
}

public static IEntityDescriptor descriptor { get; }
public static TType realDescriptor { get; }
public static Type type => typeof(TType);
public static IEntityDescriptor descriptor { get; }
}
}

+ 14
- 14
Svelto.ECS/EntityFactory.cs View File

@@ -6,23 +6,23 @@ namespace Svelto.ECS.Internal
{
static class EntityFactory
{
public static FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> BuildGroupedEntities(EGID egid,
EnginesRoot.DoubleBufferedEntitiesToAdd groupEntitiesToAdd, IComponentBuilder[] componentsToBuild,
IEnumerable<object> implementors)
public static FasterDictionary<RefWrapperType, ITypeSafeDictionary> BuildGroupedEntities
(EGID egid, EnginesRoot.DoubleBufferedEntitiesToAdd groupEntitiesToAdd
, IComponentBuilder[] componentsToBuild, IEnumerable<object> implementors, Type implementorType)
{
var group = FetchEntityGroup(egid.groupID, groupEntitiesToAdd);

BuildEntitiesAndAddToGroup(egid, group, componentsToBuild, implementors);
BuildEntitiesAndAddToGroup(egid, group, componentsToBuild, implementors, implementorType);

return group;
}

static FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> FetchEntityGroup(uint groupID,
static FasterDictionary<RefWrapperType, ITypeSafeDictionary> FetchEntityGroup(ExclusiveGroupStruct groupID,
EnginesRoot.DoubleBufferedEntitiesToAdd groupEntityComponentsByType)
{
if (groupEntityComponentsByType.current.TryGetValue(groupID, out var group) == false)
{
group = new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>();
group = new FasterDictionary<RefWrapperType, ITypeSafeDictionary>();
groupEntityComponentsByType.current.Add(groupID, group);
}
@@ -35,9 +35,9 @@ namespace Svelto.ECS.Internal
return group;
}

static void BuildEntitiesAndAddToGroup(EGID entityID,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group,
IComponentBuilder[] componentBuilders, IEnumerable<object> implementors)
static void BuildEntitiesAndAddToGroup
(EGID entityID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> @group
, IComponentBuilder[] componentBuilders, IEnumerable<object> implementors, Type implementorType)
{
var count = componentBuilders.Length;

@@ -49,7 +49,7 @@ namespace Svelto.ECS.Internal
var entityComponentType = componentBuilders[index].GetEntityComponentType();
if (types.Contains(entityComponentType))
{
throw new ECSException("EntityBuilders must be unique inside an EntityDescriptor");
throw new ECSException($"EntityBuilders must be unique inside an EntityDescriptor. Descriptor Type {implementorType} Component Type: {entityComponentType}");
}

types.Add(entityComponentType);
@@ -58,17 +58,17 @@ namespace Svelto.ECS.Internal
for (var index = 0; index < count; ++index)
{
var entityComponentBuilder = componentBuilders[index];
var entityComponentType = entityComponentBuilder.GetEntityComponentType();
var entityComponentType = entityComponentBuilder.GetEntityComponentType();

BuildEntity(entityID, @group, entityComponentType, entityComponentBuilder, implementors);
}
}

static void BuildEntity(EGID entityID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group,
static void BuildEntity(EGID entityID, FasterDictionary<RefWrapperType, ITypeSafeDictionary> group,
Type entityComponentType, IComponentBuilder componentBuilder, IEnumerable<object> implementors)
{
var entityComponentsPoolWillBeCreated =
group.TryGetValue(new RefWrapper<Type>(entityComponentType), out var safeDictionary) == false;
group.TryGetValue(new RefWrapperType(entityComponentType), out var safeDictionary) == false;

//passing the undefined entityComponentsByType inside the entityComponentBuilder will allow it to be created with the
//correct type and casted back to the undefined list. that's how the list will be eventually of the target
@@ -76,7 +76,7 @@ namespace Svelto.ECS.Internal
componentBuilder.BuildEntityAndAddToList(ref safeDictionary, entityID, implementors);

if (entityComponentsPoolWillBeCreated)
group.Add(new RefWrapper<Type>(entityComponentType), safeDictionary);
group.Add(new RefWrapperType(entityComponentType), safeDictionary);
}
}
}

+ 2
- 2
Svelto.ECS/EntityGroupNotFoundException.cs View File

@@ -4,8 +4,8 @@ namespace Svelto.ECS.Internal
{
class EntityGroupNotFoundException : Exception
{
public EntityGroupNotFoundException(Type type)
: base("entity group not found ".FastConcat(type.ToString()))
public EntityGroupNotFoundException(Type type, string toName)
: base($"entity group {toName} not used for component type ".FastConcat(type.ToString()))
{
}
}

+ 0
- 11
Svelto.ECS/EntityHierarchyStruct.cs View File

@@ -1,11 +0,0 @@
namespace Svelto.ECS
{
public struct EntityHierarchyStruct: IEntityComponent, INeedEGID
{
public readonly ExclusiveGroupStruct parentGroup;
public EntityHierarchyStruct(ExclusiveGroup @group): this() { parentGroup = group; }
public EGID ID { get; set; }
}
}

+ 1
- 1
Svelto.ECS/EntityInfoView.cs View File

@@ -1,6 +1,6 @@
namespace Svelto.ECS
{
struct EntityInfoViewComponent: IEntityComponent
struct EntityInfoComponent: IEntityComponent
{
public IComponentBuilder[] componentsToBuild;
}

+ 0
- 205
Svelto.ECS/EntityStream.cs View File

@@ -1,205 +0,0 @@
using System;
using System.Runtime.InteropServices;
using Svelto.DataStructures;

namespace Svelto.ECS
{
/// <summary>
/// I eventually realised that, with the ECS design, no form of communication other than polling entity components can exist.
/// Using groups, you can have always an optimal set of entity components to poll. However EntityStreams
/// can be useful if:
/// - you need to react on seldom entity changes, usually due to user events
/// - you want engines to be able to track entity changes
/// - you want a thread-safe way to read entity states, which includes all the state changes and not the last
/// one only
/// - you want to communicate between EnginesRoots
/// </summary>
class EntitiesStream : IDisposable
{
internal Consumer<T> GenerateConsumer<T>(string name, uint capacity)
where T : unmanaged, IEntityComponent
{
if (_streams.ContainsKey(TypeRefWrapper<T>.wrapper) == false)
_streams[TypeRefWrapper<T>.wrapper] = new EntityStream<T>();

return (_streams[TypeRefWrapper<T>.wrapper] as EntityStream<T>).GenerateConsumer(name, capacity);
}

public Consumer<T> GenerateConsumer<T>(ExclusiveGroupStruct group, string name, uint capacity)
where T : unmanaged, IEntityComponent
{
if (_streams.ContainsKey(TypeRefWrapper<T>.wrapper) == false)
_streams[TypeRefWrapper<T>.wrapper] = new EntityStream<T>();

EntityStream<T> typeSafeStream = (EntityStream<T>) _streams[TypeRefWrapper<T>.wrapper];
return typeSafeStream.GenerateConsumer(group, name, capacity);
}

internal void PublishEntity<T>(ref T entity, EGID egid) where T : unmanaged, IEntityComponent
{
if (_streams.TryGetValue(TypeRefWrapper<T>.wrapper, out var typeSafeStream))
(typeSafeStream as EntityStream<T>).PublishEntity(ref entity, egid);
else
Console.LogDebug("No Consumers are waiting for this entity to change ", typeof(T));
}

readonly ThreadSafeDictionary<RefWrapper<Type>, ITypeSafeStream> _streams =
new ThreadSafeDictionary<RefWrapper<Type>, ITypeSafeStream>();

public void Dispose()
{
_streams.Clear();
}
}

interface ITypeSafeStream
{ }

public class EntityStream<T> : ITypeSafeStream where T : unmanaged, IEntityComponent
{
~EntityStream()
{
for (int i = 0; i < _consumers.Count; i++)
_consumers[i].Free();
}
internal EntityStream()
{
_consumers = new ThreadSafeFasterList<Consumer<T>>();
}
internal void PublishEntity(ref T entity, EGID egid)
{
for (int i = 0; i < _consumers.Count; i++)
{
unsafe
{
if (*(bool *)_consumers[i].mustBeDisposed)
{
_consumers[i].Free();
_consumers.UnorderedRemoveAt(i);
--i;
continue;
}
if (_consumers[i].hasGroup)
{
if (egid.groupID == _consumers[i].@group)
{
_consumers[i].Enqueue(entity, egid);
}
}
else
{
_consumers[i].Enqueue(entity, egid);
}
}
}
}

internal Consumer<T> GenerateConsumer(string name, uint capacity)
{
var consumer = new Consumer<T>(name, capacity);

_consumers.Add(consumer);

return consumer;
}

internal Consumer<T> GenerateConsumer(ExclusiveGroupStruct group, string name, uint capacity)
{
var consumer = new Consumer<T>(group, name, capacity);

_consumers.Add(consumer);

return consumer;
}

readonly ThreadSafeFasterList<Consumer<T>> _consumers;
}

public struct Consumer<T> :IDisposable where T : unmanaged, IEntityComponent
{
internal Consumer(string name, uint capacity) : this()
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
_name = name;
#endif
_ringBuffer = new RingBuffer<ValueTuple<T, EGID>>((int) capacity,
#if DEBUG && !PROFILE_SVELTO
_name
#else
string.Empty
#endif
);
mustBeDisposed = Marshal.AllocHGlobal(sizeof(bool));
*((bool*) mustBeDisposed) = false;
}
}

internal Consumer(ExclusiveGroupStruct group, string name, uint capacity) : this(name, capacity)
{
this.@group = @group;
hasGroup = true;
}

internal void Enqueue(in T entity, in EGID egid)
{
_ringBuffer.Enqueue((entity, egid));
}

public bool TryDequeue(out T entity)
{
var tryDequeue = _ringBuffer.TryDequeue(out var values);

entity = values.Item1;

return tryDequeue;
}

public bool TryDequeue(out T entity, out EGID id)
{
var tryDequeue = _ringBuffer.TryDequeue(out var values);

entity = values.Item1;
id = values.Item2;

return tryDequeue;
}

public void Flush()
{
_ringBuffer.Reset();
}

public void Dispose()
{
unsafe
{
*(bool *)mustBeDisposed = true;
}
}

public uint Count()
{
return (uint) _ringBuffer.Count;
}
public void Free()
{
Marshal.FreeHGlobal(mustBeDisposed);
}

readonly RingBuffer<ValueTuple<T, EGID>> _ringBuffer;

internal readonly ExclusiveGroupStruct @group;
internal readonly bool hasGroup;
internal IntPtr mustBeDisposed;

#if DEBUG && !PROFILE_SVELTO
readonly string _name;
#endif
}
}

+ 12
- 2
Svelto.ECS/EntitySubmissionScheduler.cs View File

@@ -4,8 +4,18 @@ namespace Svelto.ECS.Schedulers
{
public interface IEntitiesSubmissionScheduler: IDisposable
{
EnginesRoot.EntitiesSubmitter onTick { set; }

bool paused { get; set; }
}
public abstract class EntitiesSubmissionScheduler: IEntitiesSubmissionScheduler
{
protected internal abstract EnginesRoot.EntitiesSubmitter onTick { set; }
public abstract void Dispose();
public abstract bool paused { get; set; }
}
public abstract class ISimpleEntitiesSubmissionScheduler: EntitiesSubmissionScheduler
{
public abstract void SubmitEntities();
}
}

+ 14
- 2
Svelto.ECS/EntitySubmitOperation.cs View File

@@ -8,7 +8,7 @@ namespace Svelto.ECS
: IEquatable<EntitySubmitOperation>
{
public readonly EntitySubmitOperationType type;
public readonly IComponentBuilder[] builders;
public readonly IComponentBuilder[] builders;
public readonly EGID fromID;
public readonly EGID toID;
#if DEBUG && !PROFILE_SVELTO
@@ -26,7 +26,19 @@ namespace Svelto.ECS
trace = default;
#endif
}

public EntitySubmitOperation
(EntitySubmitOperationType operation, ExclusiveGroupStruct @group
, IComponentBuilder[] descriptorComponentsToBuild):this()
{
type = operation;
this.builders = descriptorComponentsToBuild;
fromID = new EGID(0, group);
#if DEBUG && !PROFILE_SVELTO
trace = default;
#endif
}

public static bool operator ==(EntitySubmitOperation obj1, EntitySubmitOperation obj2)
{
return obj1.Equals(obj2);


+ 1
- 1
Svelto.ECS/EntityViewUtility.cs View File

@@ -46,7 +46,7 @@ namespace Svelto.ECS
//efficient way to collect the fields of every EntityComponentType
var setters = FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>>.NoVirt.ToArrayFast(
entityComponentBlazingFastReflection, out var count);
//todo this should happen once per T, not once per Build<T>
if (implementors != null)
{


+ 1
- 0
Svelto.ECS/ExclusiveGroup.cs View File

@@ -65,6 +65,7 @@ namespace Svelto.ECS
return a._group + b;
}
//todo document the use case for this method
public static ExclusiveGroupStruct Search(string holderGroupName)
{
if (_knownGroups.ContainsKey(holderGroupName) == false)


+ 29
- 6
Svelto.ECS/ExclusiveGroupStruct.cs View File

@@ -1,10 +1,34 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Svelto.ECS
{
public readonly struct BuildGroup
{
internal BuildGroup(ExclusiveGroupStruct group)
{
this.group = group;
}

public static implicit operator BuildGroup(ExclusiveGroupStruct group)
{
return new BuildGroup(group);
}

public static implicit operator BuildGroup(ExclusiveGroup group)
{
return new BuildGroup(group);
}
public static implicit operator uint(BuildGroup groupStruct)
{
return groupStruct.group;
}

internal ExclusiveGroupStruct @group { get; }
}
[StructLayout(LayoutKind.Explicit, Size = 4)]
public struct ExclusiveGroupStruct : IEquatable<ExclusiveGroupStruct>, IComparable<ExclusiveGroupStruct>,
IEqualityComparer<ExclusiveGroupStruct>
@@ -65,7 +89,7 @@ namespace Svelto.ECS

return groupStruct;
}
internal ExclusiveGroupStruct(ExclusiveGroupStruct @group):this() { this = group; }

/// <summary>
@@ -107,10 +131,9 @@ namespace Svelto.ECS
return @group;
}

[FieldOffset(0)] uint _id;
[FieldOffset(0)] uint _id;
[FieldOffset(3)] byte _bytemask;
static uint _globalId = 1; //it starts from 1 because default EGID is considered not initalized value

static uint _globalId = 1; //it starts from 1 because default EGID is considered not initalized value
}
}

+ 1
- 1
Svelto.ECS/ExtendibleEntityDescriptor.cs View File

@@ -8,7 +8,7 @@ namespace Svelto.ECS
/// to swap and remove specialized entities from abstract engines
/// </summary>
/// <typeparam name="TType"></typeparam>
public class ExtendibleEntityDescriptor<TType> : IEntityDescriptor where TType : IEntityDescriptor, new()
public class ExtendibleEntityDescriptor<TType> : IDynamicEntityDescriptor where TType : IEntityDescriptor, new()
{
static ExtendibleEntityDescriptor()
{


+ 2
- 2
Svelto.ECS/Extensions/ProcessorCount.cs View File

@@ -4,8 +4,8 @@ namespace Svelto.ECS
{
internal static class ProcessorCount
{
static readonly int processorCount = Environment.ProcessorCount;
public static readonly int processorCount = Environment.ProcessorCount;
public static int BatchSize(uint totalIterations)
{
var iterationsPerBatch = totalIterations / processorCount;


+ 4
- 62
Svelto.ECS/Extensions/Svelto/AllGroupsEnumerable.cs View File

@@ -1,3 +1,4 @@
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

@@ -31,7 +32,7 @@ namespace Svelto.ECS
{
public GroupsIterator(EntitiesDB db) : this()
{
_db = db.FindGroups_INTERNAL<T1>().GetEnumerator();
_db = db.FindGroups_INTERNAL(TypeCache<T1>.type).GetEnumerator();
}

public bool MoveNext()
@@ -39,7 +40,7 @@ namespace Svelto.ECS
//attention, the while is necessary to skip empty groups
while (_db.MoveNext() == true)
{
FasterDictionary<uint, ITypeSafeDictionary>.KeyValuePairFast group = _db.Current;
FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>.KeyValuePairFast group = _db.Current;
ITypeSafeDictionary<T1> typeSafeDictionary = @group.Value as ITypeSafeDictionary<T1>;
if (typeSafeDictionary.count == 0) continue;
@@ -55,7 +56,7 @@ namespace Svelto.ECS

public GroupCollection Current => _array;

FasterDictionary<uint, ITypeSafeDictionary>.FasterDictionaryKeyValueEnumerator _db;
FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>.FasterDictionaryKeyValueEnumerator _db;
GroupCollection _array;
}

@@ -66,63 +67,4 @@ namespace Svelto.ECS

readonly EntitiesDB _db;
}
#if TO_BE_FINISHED
public struct NativeAllGroupsEnumerable<T1, T2>
where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent
{
public NativeAllGroupsEnumerable(EntitiesDB db)
{
_db = db;
}

public struct NativeGroupsIterator
{
public NativeGroupsIterator(EntitiesDB db) : this()
{
_db = db.FindGroups<T1, T2>().GetEnumerator();
}

public bool MoveNext()
{
//attention, the while is necessary to skip empty groups
while (_db.MoveNext() == true)
{
FasterDictionary<uint, ITypeSafeDictionary>.KeyValuePairFast group = _db.Current;

ITypeSafeDictionary<T1> typeSafeDictionary1 = @group.Value as ITypeSafeDictionary<T1>;
ITypeSafeDictionary<T2> typeSafeDictionary2 = @group.Value as ITypeSafeDictionary<T2>;

DBC.ECS.Check.Require(typeSafeDictionary1.Count != typeSafeDictionary2.Count
, "entities count do not match");
if (typeSafeDictionary1.Count == 0) continue;
_array = new BT<NB<T1>, NB<T2>>()(new EntityCollection<T1>(typeSafeDictionary1.GetValuesArray(out var count), count)
.ToBuffer();

return true;
}

return false;
}

public void Reset()
{
}

public BT<NB<T1>, NB<T2>> Current => _array;

FasterDictionary<uint, ITypeSafeDictionary>.FasterDictionaryKeyValueEnumerator _db;

BT<NB<T1>, NB<T2>> _array;
}

public NativeGroupsIterator GetEnumerator()
{
return new NativeGroupsIterator(_db);
}

readonly EntitiesDB _db;
}
#endif
}

+ 123
- 53
Svelto.ECS/Extensions/Svelto/EntityCollectionExtension.cs View File

@@ -8,32 +8,51 @@ namespace Svelto.ECS
public static class EntityCollectionExtension
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1>(in this EntityCollection<T1> ec, out NB<T1> buffer, out int count) where T1 : unmanaged, IEntityComponent
public static void Deconstruct<T1>
(in this EntityCollection<T1> ec, out NB<T1> buffer, out int count) where T1 : unmanaged, IEntityComponent
{
buffer = ec._nativedBuffer;
count = (int) ec.count;
count = (int) ec.count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2>(in this EntityCollection<T1, T2> ec, out NB<T1> buffer1, out NB<T2> buffer2, out int count) where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
public static void Deconstruct<T1, T2>
(in this EntityCollection<T1, T2> ec, out NB<T1> buffer1, out NB<T2> buffer2, out int count)
where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent
{
buffer1 = ec.Item1._nativedBuffer;
buffer2 = ec.Item2._nativedBuffer;
count = (int) ec.count;
buffer1 = ec.buffer1._nativedBuffer;
buffer2 = ec.buffer2._nativedBuffer;
count = (int) ec.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2, T3>
(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, out NB<T2> buffer2, out NB<T3> buffer3
, out int count) where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
{
buffer1 = ec.buffer1._nativedBuffer;
buffer2 = ec.buffer2._nativedBuffer;
buffer3 = ec.buffer3._nativedBuffer;
count = (int) ec.count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2, T3>(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, out NB<T2> buffer2, out NB<T3> buffer3, out int count) where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
public static void Deconstruct<T1, T2, T3, T4>
(in this EntityCollection<T1, T2, T3, T4> ec, out NB<T1> buffer1, out NB<T2> buffer2, out NB<T3> buffer3
, out NB<T4> buffer4, out int count) where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
where T4 : unmanaged, IEntityComponent
{
buffer1 = ec.Item1._nativedBuffer;
buffer2 = ec.Item2._nativedBuffer;
buffer3 = ec.Item3._nativedBuffer;
buffer4 = ec.Item4._nativedBuffer;
count = (int) ec.count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BT<NB<T1>> ToBuffer<T1>(in this EntityCollection<T1> ec) where T1 : unmanaged, IEntityComponent
{
@@ -45,7 +64,7 @@ namespace Svelto.ECS
(in this EntityCollection<T1, T2> ec)
where T2 : unmanaged, IEntityComponent where T1 : unmanaged, IEntityComponent
{
return new BT<NB<T1>, NB<T2>>(ec.Item1._nativedBuffer, ec.Item2._nativedBuffer, ec.count);
return new BT<NB<T1>, NB<T2>>(ec.buffer1._nativedBuffer, ec.buffer2._nativedBuffer, ec.count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -55,15 +74,16 @@ namespace Svelto.ECS
where T1 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
{
return new BT<NB<T1>, NB<T2>, NB<T3>>(ec.Item1._nativedBuffer, ec.Item2._nativedBuffer
, ec.Item3._nativedBuffer, ec.count);
return new BT<NB<T1>, NB<T2>, NB<T3>>(ec.buffer1._nativedBuffer, ec.buffer2._nativedBuffer
, ec.buffer3._nativedBuffer, ec.count);
}
}

public static class EntityCollectionExtensionB
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1>(in this EntityCollection<T1> ec, out MB<T1> buffer, out int count) where T1 : struct, IEntityViewComponent
public static void Deconstruct<T1>
(in this EntityCollection<T1> ec, out MB<T1> buffer, out int count) where T1 : struct, IEntityViewComponent
{
buffer = ec._managedBuffer;
count = (int) ec.count;
@@ -76,12 +96,13 @@ namespace Svelto.ECS
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2>(in this EntityCollection<T1, T2> ec, out MB<T1> buffer1, out MB<T2> buffer2, out int count) where T1 : struct, IEntityViewComponent
where T2 : struct, IEntityViewComponent
public static void Deconstruct<T1, T2>
(in this EntityCollection<T1, T2> ec, out MB<T1> buffer1, out MB<T2> buffer2, out int count)
where T1 : struct, IEntityViewComponent where T2 : struct, IEntityViewComponent
{
buffer1 = ec.Item1._managedBuffer;
buffer2 = ec.Item2._managedBuffer;
count = (int) ec.count;
buffer1 = ec.buffer1._managedBuffer;
buffer2 = ec.buffer2._managedBuffer;
count = (int) ec.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -89,18 +110,20 @@ namespace Svelto.ECS
(in this EntityCollection<T1, T2> ec)
where T2 : struct, IEntityViewComponent where T1 : struct, IEntityViewComponent
{
return (ec.Item1._managedBuffer, ec.Item2._managedBuffer, ec.count);
return (ec.buffer1._managedBuffer, ec.buffer2._managedBuffer, ec.count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2, T3>(in this EntityCollection<T1, T2, T3> ec, out MB<T1> buffer1, out MB<T2> buffer2, out MB<T3> buffer3, out int count) where T1 : struct, IEntityViewComponent
where T2 : struct, IEntityViewComponent
where T3 : struct, IEntityViewComponent
public static void Deconstruct<T1, T2, T3>
(in this EntityCollection<T1, T2, T3> ec, out MB<T1> buffer1, out MB<T2> buffer2, out MB<T3> buffer3
, out int count) where T1 : struct, IEntityViewComponent
where T2 : struct, IEntityViewComponent
where T3 : struct, IEntityViewComponent
{
buffer1 = ec.Item1._managedBuffer;
buffer2 = ec.Item2._managedBuffer;
buffer3 = ec.Item3._managedBuffer;
count = (int) ec.count;
buffer1 = ec.buffer1._managedBuffer;
buffer2 = ec.buffer2._managedBuffer;
buffer3 = ec.buffer3._managedBuffer;
count = (int) ec.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -110,7 +133,7 @@ namespace Svelto.ECS
where T1 : struct, IEntityViewComponent
where T3 : struct, IEntityViewComponent
{
return (ec.Item1._managedBuffer, ec.Item2._managedBuffer, ec.Item3._managedBuffer, ec.count);
return (ec.buffer1._managedBuffer, ec.buffer2._managedBuffer, ec.buffer3._managedBuffer, ec.count);
}
}

@@ -121,42 +144,73 @@ namespace Svelto.ECS
(in this EntityCollection<T1, T2> ec)
where T1 : unmanaged, IEntityComponent where T2 : struct, IEntityViewComponent
{
return (ec.Item1._nativedBuffer, ec.Item2._managedBuffer, ec.count);
return (ec.buffer1._nativedBuffer, ec.buffer2._managedBuffer, ec.count);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2>(in this EntityCollection<T1, T2> ec, out NB<T1> buffer1, out MB<T2> buffer2, out int count) where T1 : unmanaged, IEntityComponent
where T2 : struct, IEntityViewComponent
public static (NB<T1> buffer1, MB<T2> buffer2, MB<T3> buffer3, uint count) ToBuffers<T1, T2, T3>
(in this EntityCollection<T1, T2, T3> ec)
where T1 : unmanaged, IEntityComponent
where T2 : struct, IEntityViewComponent
where T3 : struct, IEntityViewComponent
{
buffer1 = ec.Item1._nativedBuffer;
buffer2 = ec.Item2._managedBuffer;
return (ec.buffer1._nativedBuffer, ec.buffer2._managedBuffer, ec.buffer3._managedBuffer, ec.count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2>
(in this EntityCollection<T1, T2> ec, out NB<T1> buffer1, out MB<T2> buffer2, out int count)
where T1 : unmanaged, IEntityComponent where T2 : struct, IEntityViewComponent
{
buffer1 = ec.buffer1._nativedBuffer;
buffer2 = ec.buffer2._managedBuffer;
count = (int) ec.count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (NB<T1> buffer1, MB<T2> buffer2, MB<T3> buffer3, uint count) ToBuffers<T1, T2, T3>
(in this EntityCollection<T1, T2, T3> ec)
public static void Deconstruct<T1, T2, T3>
(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, out MB<T2> buffer2, out MB<T3> buffer3, out int count)
where T1 : unmanaged, IEntityComponent
where T2 : struct, IEntityViewComponent
where T3 : struct, IEntityViewComponent
{
return (ec.Item1._nativedBuffer, ec.Item2._managedBuffer, ec.Item3._managedBuffer, ec.count);
buffer1 = ec.buffer1._nativedBuffer;
buffer2 = ec.buffer2._managedBuffer;
buffer3 = ec.buffer3._managedBuffer;
count = (int) ec.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2, T3, T4>
(in this EntityCollection<T1, T2, T3, T4> ec, out NB<T1> buffer1, out NB<T2> buffer2, out NB<T3> buffer3
, out MB<T4> buffer4, out int count) where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
where T4 : struct, IEntityViewComponent
{
buffer1 = ec.Item1._nativedBuffer;
buffer2 = ec.Item2._nativedBuffer;
buffer3 = ec.Item3._nativedBuffer;
buffer4 = ec.Item4._managedBuffer;
count = (int) ec.count;
}
}
public static class EntityCollectionExtensionD
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2, T3>(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, out NB<T2> buffer2, out MB<T3> buffer3, out int count) where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : struct, IEntityViewComponent
public static void Deconstruct<T1, T2, T3>
(in this EntityCollection<T1, T2, T3> ec, out NB<T1> buffer1, out NB<T2> buffer2, out MB<T3> buffer3
, out int count) where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : struct, IEntityViewComponent
{
buffer1 = ec.Item1._nativedBuffer;
buffer2 = ec.Item2._nativedBuffer;
buffer3 = ec.Item3._managedBuffer;
buffer1 = ec.buffer1._nativedBuffer;
buffer2 = ec.buffer2._nativedBuffer;
buffer3 = ec.buffer3._managedBuffer;
count = (int) ec.count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (NB<T1> buffer1, NB<T2> buffer2, MB<T3> buffer3, uint count) ToBuffers<T1, T2, T3>
(in this EntityCollection<T1, T2, T3> ec)
@@ -164,18 +218,34 @@ namespace Svelto.ECS
where T2 : unmanaged, IEntityComponent
where T3 : struct, IEntityViewComponent
{
return (ec.Item1._nativedBuffer, ec.Item2._nativedBuffer, ec.Item3._managedBuffer, ec.count);
return (ec.buffer1._nativedBuffer, ec.buffer2._nativedBuffer, ec.buffer3._managedBuffer, ec.count);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BT<NB<T1>, NB<T2>, NB<T3>, NB<T4> > ToBuffers<T1, T2, T3, T4>
public static BT<NB<T1>, NB<T2>, NB<T3>, NB<T4>> ToBuffers<T1, T2, T3, T4>
(in this EntityCollection<T1, T2, T3, T4> ec)
where T2 : unmanaged, IEntityComponent
where T1 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
where T4 : unmanaged, IEntityComponent
{
return new BT<NB<T1>, NB<T2>, NB<T3>, NB<T4>>(ec.Item1._nativedBuffer, ec.Item2._nativedBuffer, ec.Item3._nativedBuffer, ec.Item4._nativedBuffer, ec.count);
return new BT<NB<T1>, NB<T2>, NB<T3>, NB<T4>>(ec.Item1._nativedBuffer, ec.Item2._nativedBuffer
, ec.Item3._nativedBuffer, ec.Item4._nativedBuffer, ec.count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Deconstruct<T1, T2, T3, T4>
(in this EntityCollection<T1, T2, T3, T4> ec, out NB<T1> buffer1, out NB<T2> buffer2, out MB<T3> buffer3
, out MB<T4> buffer4, out int count) where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : struct, IEntityViewComponent
where T4 : struct, IEntityViewComponent
{
buffer1 = ec.Item1._nativedBuffer;
buffer2 = ec.Item2._nativedBuffer;
buffer3 = ec.Item3._managedBuffer;
buffer4 = ec.Item4._managedBuffer;
count = (int) ec.count;
}
}
}

Svelto.ECS/Extensions/Svelto/EntityDBExtensionsB.cs → Svelto.ECS/Extensions/Svelto/EntityManagedDBExtensions.cs View File

@@ -5,7 +5,7 @@ using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public static class EntityDBExtensionsB
public static class EntityManagedDBExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static MB<T> QueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index) where T : struct, IEntityViewComponent
@@ -65,5 +65,45 @@ namespace Svelto.ECS
{
return ref entitiesDb.QueryEntity<T>(new EGID(id, group));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T QueryUniqueEntity<T>(this EntitiesDB entitiesDb, ExclusiveGroupStruct group) where T : struct, IEntityViewComponent
{
var (entities, entitiescount) = entitiesDb.QueryEntities<T>(@group);

#if DEBUG && !PROFILE_SVELTO
if (entitiescount == 0)
throw new ECSException("Unique entity not found '".FastConcat(typeof(T).ToString()).FastConcat("'"));
if (entitiescount != 1)
throw new ECSException("Unique entities must be unique! '".FastConcat(typeof(T).ToString())
.FastConcat("'"));
#endif
return ref entities[0];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static MB<T> GetArrayAndEntityIndex<T>(this EGIDMapper<T> mapper, uint entityID, out uint index) where T : struct, IEntityViewComponent
{
if (mapper._map.TryFindIndex(entityID, out index))
{
return (MB<T>) mapper._map.GetValues(out _);
}

throw new ECSException("Entity not found");
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGetArrayAndEntityIndex<T>(this EGIDMapper<T> mapper, uint entityID, out uint index, out MB<T> array) where T : struct, IEntityViewComponent
{
index = default;
if (mapper._map != null && mapper._map.TryFindIndex(entityID, out index))
{
array = (MB<T>) mapper._map.GetValues(out _);
return true;
}

array = default;
return false;
}
}
}

Svelto.ECS/Extensions/Svelto/EntityDBExtensions.cs → Svelto.ECS/Extensions/Svelto/EntityNativeDBExtensions.cs View File

@@ -4,7 +4,7 @@ using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public static class EntityDBExtensions
public static class EntityNativeDBExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static AllGroupsEnumerable<T1> QueryEntities<T1>(this EntitiesDB db)
@@ -16,7 +16,7 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NB<T> QueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index) where T : unmanaged, IEntityComponent
{
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(entityGID, out index, out NB<T> array) == true)
if (entitiesDb.QueryEntitiesAndIndexInternal(entityGID, out index, out NB<T> array) == true)
return array;

throw new EntityNotFoundException(entityGID, typeof(T));
@@ -26,7 +26,7 @@ namespace Svelto.ECS
public static NB<T> QueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group, out uint index) where T : unmanaged, IEntityComponent
{
EGID entityGID = new EGID(id, group);
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(entityGID, out index, out NB<T> array) == true)
if (entitiesDb.QueryEntitiesAndIndexInternal(entityGID, out index, out NB<T> array) == true)
return array;

throw new EntityNotFoundException(entityGID, typeof(T));
@@ -36,7 +36,7 @@ namespace Svelto.ECS
public static bool TryQueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index, out NB<T> array)
where T : unmanaged, IEntityComponent
{
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(entityGID, out index, out array) == true)
if (entitiesDb.QueryEntitiesAndIndexInternal(entityGID, out index, out array) == true)
return true;

return false;
@@ -46,7 +46,7 @@ namespace Svelto.ECS
public static bool TryQueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group, out uint index, out NB<T> array)
where T : unmanaged, IEntityComponent
{
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(new EGID(id, group), out index, out array) == true)
if (entitiesDb.QueryEntitiesAndIndexInternal(new EGID(id, group), out index, out array) == true)
return true;

return false;
@@ -81,5 +81,45 @@ namespace Svelto.ECS
{
return ref entitiesDb.QueryEntity<T>(new EGID(id, group));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T QueryUniqueEntity<T>(this EntitiesDB entitiesDb, ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent
{
var (entities, entitiescount) = entitiesDb.QueryEntities<T>(@group);

#if DEBUG && !PROFILE_SVELTO
if (entitiescount == 0)
throw new ECSException("Unique entity not found '".FastConcat(typeof(T).ToString()).FastConcat("'"));
if (entitiescount != 1)
throw new ECSException("Unique entities must be unique! '".FastConcat(typeof(T).ToString())
.FastConcat("'"));
#endif
return ref entities[0];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NB<T> GetArrayAndEntityIndex<T>(this EGIDMapper<T> mapper, uint entityID, out uint index) where T : unmanaged, IEntityComponent
{
if (mapper._map.TryFindIndex(entityID, out index))
{
return (NB<T>) mapper._map.GetValues(out _);
}

throw new ECSException("Entity not found");
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGetArrayAndEntityIndex<T>(this EGIDMapper<T> mapper, uint entityID, out uint index, out NB<T> array) where T : unmanaged, IEntityComponent
{
index = default;
if (mapper._map != null && mapper._map.TryFindIndex(entityID, out index))
{
array = (NB<T>) mapper._map.GetValues(out _);
return true;
}

array = default;
return false;
}
}
}

+ 38
- 0
Svelto.ECS/Extensions/Svelto/ExclusiveGroupExtensions.cs View File

@@ -0,0 +1,38 @@
using System.Runtime.CompilerServices;
using Svelto.DataStructures;

namespace Svelto.ECS
{
public static class ExclusiveGroupExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool FoundIn(this in ExclusiveGroupStruct group, ExclusiveGroupStruct[] groups)
{
for (int i = 0; i < groups.Length; ++i)
if (groups[i] == group)
return true;

return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool FoundIn(this in ExclusiveGroupStruct group, FasterList<ExclusiveGroupStruct> groups)
{
for (int i = 0; i < groups.count; ++i)
if (groups[i] == group)
return true;

return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool FoundIn(this in ExclusiveGroupStruct group, LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
{
for (int i = 0; i < groups.count; ++i)
if (groups[i] == group)
return true;

return false;
}
}
}

+ 111
- 34
Svelto.ECS/Extensions/Svelto/GroupsEnumerable.cs View File

@@ -4,20 +4,20 @@ using Svelto.DataStructures;
namespace Svelto.ECS
{
/// <summary>
/// NOTE THESE ENUMERABLES EXIST TO AVOID BOILERPLATE CODE AS THEY SKIP 0 SIZED GROUPS
/// However if the normal pattern with the double foreach is used, this is not necessary
/// Note: atm cannot be ref structs because they are returned in a valuetuple
/// NOTE THESE ENUMERABLES EXIST TO AVOID BOILERPLATE CODE AS THEY SKIP 0 SIZED GROUPS
/// However if the normal pattern with the double foreach is used, this is not necessary
/// Note: atm cannot be ref structs because they are returned in a valuetuple
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <typeparam name="T3"></typeparam>
/// <typeparam name="T4"></typeparam>
public readonly ref struct GroupsEnumerable<T1, T2, T3, T4> where T1 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T3 : struct, IEntityComponent
where T4 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T3 : struct, IEntityComponent
where T4 : struct, IEntityComponent
{
readonly EntitiesDB _db;
readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
@@ -50,15 +50,14 @@ namespace Svelto.ECS
Check.Assert(entityCollection1.count == entityCollection2.count
, "congratulation, you found a bug in Svelto, please report it");

EntityCollection<T1, T2, T3> array = entityCollection1;
var array = entityCollection1;
var array2 = entityCollection2;
_buffers = new EntityCollection<T1, T2, T3, T4>(
array.Item1, array.Item2, array.Item3, array2);
_buffers = new EntityCollection<T1, T2, T3, T4>(array.buffer1, array.buffer2, array.buffer3, array2);
break;
}
var moveNext = _indexGroup < _groups.count;
if (moveNext == false)
Reset();

@@ -67,7 +66,7 @@ namespace Svelto.ECS

public void Reset() { _indexGroup = -1; }

public EntityCollection<T1, T2, T3, T4> Current => _buffers;
public RefCurrent<T1, T2, T3, T4> Current => new RefCurrent<T1, T2, T3, T4>(_buffers, _groups[_indexGroup]);

readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

@@ -79,17 +78,38 @@ namespace Svelto.ECS
public GroupsIterator GetEnumerator() { return new GroupsIterator(_db, _groups); }
}

public ref struct RefCurrent<T1, T2, T3, T4> where T1 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T3 : struct, IEntityComponent
where T4 : struct, IEntityComponent
{
public RefCurrent(in EntityCollection<T1, T2, T3, T4> buffers, ExclusiveGroupStruct group)
{
_buffers = buffers;
_group = group;
}

public void Deconstruct(out EntityCollection<T1, T2, T3, T4> buffers, out ExclusiveGroupStruct group)
{
buffers = _buffers;
group = _group;
}

public readonly EntityCollection<T1, T2, T3, T4> _buffers;
public readonly ExclusiveGroupStruct _group;
}

/// <summary>
/// ToDo source gen could return the implementation of IBuffer directly, but cannot be done manually
/// ToDo source gen could return the implementation of IBuffer directly, but cannot be done manually
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <typeparam name="T3"></typeparam>
public readonly ref struct GroupsEnumerable<T1, T2, T3> where T1 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T3 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T3 : struct, IEntityComponent
{
readonly EntitiesDB _db;
readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
@@ -112,16 +132,16 @@ namespace Svelto.ECS
//attention, the while is necessary to skip empty groups
while (++_indexGroup < _groups.count)
{
EntityCollection<T1, T2, T3> entityCollection = _entitiesDB.QueryEntities<T1, T2, T3>(_groups[_indexGroup]);
var entityCollection = _entitiesDB.QueryEntities<T1, T2, T3>(_groups[_indexGroup]);
if (entityCollection.count == 0)
continue;

_buffers = entityCollection;
break;
}
var moveNext = _indexGroup < _groups.count;
if (moveNext == false)
Reset();

@@ -130,19 +150,40 @@ namespace Svelto.ECS

public void Reset() { _indexGroup = -1; }

public EntityCollection<T1, T2, T3> Current => _buffers;
public RefCurrent<T1, T2, T3> Current => new RefCurrent<T1, T2, T3>(_buffers, _groups[_indexGroup]);

readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

int _indexGroup;
int _indexGroup;
EntityCollection<T1, T2, T3> _buffers;
readonly EntitiesDB _entitiesDB;
readonly EntitiesDB _entitiesDB;
}

public GroupsIterator GetEnumerator() { return new GroupsIterator(_db, _groups); }
}

public readonly ref struct GroupsEnumerable<T1, T2> where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
public ref struct RefCurrent<T1, T2, T3> where T1 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T3 : struct, IEntityComponent
{
public RefCurrent(in EntityCollection<T1, T2, T3> buffers, ExclusiveGroupStruct group)
{
_buffers = buffers;
_group = group;
}

public void Deconstruct(out EntityCollection<T1, T2, T3> buffers, out ExclusiveGroupStruct group)
{
buffers = _buffers;
group = _group;
}

public readonly EntityCollection<T1, T2, T3> _buffers;
public readonly ExclusiveGroupStruct _group;
}

public readonly ref struct GroupsEnumerable<T1, T2>
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
{
public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
{
@@ -173,30 +214,48 @@ namespace Svelto.ECS
}

var moveNext = _indexGroup < _groups.count;
if (moveNext == false)
Reset();
return moveNext;
}

public void Reset() { _indexGroup = -1; }

public EntityCollection<T1, T2> Current => _buffers;
public RefCurrent<T1, T2> Current => new RefCurrent<T1, T2>(_buffers, _groups[_indexGroup]);

readonly EntitiesDB _db;
readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

int _indexGroup;
int _indexGroup;
EntityCollection<T1, T2> _buffers;
}

public GroupsIterator GetEnumerator() { return new GroupsIterator(_db, _groups); }

readonly EntitiesDB _db;
readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;
}

public ref struct RefCurrent<T1, T2> where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
{
public RefCurrent(in EntityCollection<T1, T2> buffers, ExclusiveGroupStruct group)
{
_buffers = buffers;
_group = group;
}

public void Deconstruct(out EntityCollection<T1, T2> buffers, out ExclusiveGroupStruct group)
{
buffers = _buffers;
group = _group;
}

public readonly EntityCollection<T1, T2> _buffers;
public readonly ExclusiveGroupStruct _group;
}

public readonly ref struct GroupsEnumerable<T1> where T1 : struct, IEntityComponent
{
public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
@@ -232,18 +291,36 @@ namespace Svelto.ECS

public void Reset() { _indexGroup = -1; }

public EntityCollection<T1> Current => _buffer;
public RefCurrent<T1> Current => new RefCurrent<T1>(_buffer, _groups[_indexGroup]);

readonly EntitiesDB _db;
readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

int _indexGroup;
int _indexGroup;
EntityCollection<T1> _buffer;
}

public GroupsIterator GetEnumerator() { return new GroupsIterator(_db, _groups); }

readonly EntitiesDB _db;
readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;
}

public ref struct RefCurrent<T1> where T1 : struct, IEntityComponent
{
public RefCurrent(in EntityCollection<T1> buffers, ExclusiveGroupStruct group)
{
_buffers = buffers;
_group = group;
}

public void Deconstruct(out EntityCollection<T1> buffers, out ExclusiveGroupStruct group)
{
buffers = _buffers;
group = _group;
}

public readonly EntityCollection<T1> _buffers;
public readonly ExclusiveGroupStruct _group;
}
}

Svelto.ECS/Extensions/Unity/DOTS/DisposeJob.cs → Svelto.ECS/Extensions/Unity/DOTS/Jobs/DisposeJob.cs View File


Svelto.ECS/Extensions/Unity/DOTS/JobifedEnginesGroup.cs → Svelto.ECS/Extensions/Unity/DOTS/Jobs/JobifiedEnginesGroup.cs View File

@@ -7,35 +7,37 @@ namespace Svelto.ECS.Extensions.Unity
{
public interface IJobifiedEngine : IEngine
{
JobHandle Execute(JobHandle _jobHandle);
JobHandle Execute(JobHandle inputDeps);
string name { get; }
}
public interface IJobifiedGroupEngine : IJobifiedEngine
{ }
public interface IJobifiedEngine<T> : IEngine
{
JobHandle Execute(JobHandle _jobHandle, ref T _param);
JobHandle Execute(JobHandle inputDeps, ref T _param);
string name { get; }
}
public interface IJobifiedGroupEngine<T> : IJobifiedEngine<T>
{
}
{ }
/// <summary>
/// Note unsorted jobs run in parallel
/// </summary>
/// <typeparam name="Interface"></typeparam>
public abstract class JobifedEnginesGroup<Interface> : IJobifiedGroupEngine where Interface : class, IJobifiedEngine
public abstract class JobifiedEnginesGroup<Interface>:IJobifiedEngine where Interface : class, IJobifiedEngine
{
protected JobifedEnginesGroup(FasterList<Interface> engines)
protected JobifiedEnginesGroup(FasterList<Interface> engines)
{
_name = "JobifiedEnginesGroup - "+this.GetType().Name;
_engines = engines;
}
protected JobifiedEnginesGroup()
{
_name = "JobifiedEnginesGroup - "+this.GetType().Name;
_engines = new FasterList<Interface>();
}

public JobHandle Execute(JobHandle inputHandles)
{
@@ -48,7 +50,7 @@ namespace Svelto.ECS.Extensions.Unity
ref var engine = ref engines[index];
using (profiler.Sample(engine.name))
{
combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(inputHandles));
combinedHandles = engine.Execute(inputHandles);
}
}
}
@@ -56,16 +58,20 @@ namespace Svelto.ECS.Extensions.Unity
return combinedHandles;
}

protected internal void Add(Interface engine)
{
_engines.Add(engine);
}

public string name => _name;

readonly FasterReadOnlyList<Interface> _engines;
readonly bool _completeEachJob;
readonly string _name;
protected readonly FasterList<Interface> _engines;
readonly string _name;
}
public abstract class JobifedEnginesGroup<Interface, Param>: IJobifiedGroupEngine<Param> where Interface : class, IJobifiedEngine<Param>
public abstract class JobifiedEnginesGroup<Interface, Param>: IJobifiedGroupEngine<Param> where Interface : class, IJobifiedEngine<Param>
{
protected JobifedEnginesGroup(FasterList<Interface> engines)
protected JobifiedEnginesGroup(FasterList<Interface> engines)
{
_name = "JobifiedEnginesGroup - "+this.GetType().Name;
_engines = engines;
@@ -79,8 +85,7 @@ namespace Svelto.ECS.Extensions.Unity
for (var index = 0; index < engines.count; index++)
{
var engine = engines[index];
using (profiler.Sample(engine.name)) combinedHandles =
JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles, ref _param));
using (profiler.Sample(engine.name)) combinedHandles = engine.Execute(combinedHandles, ref _param);
}
}


Svelto.ECS/Extensions/Unity/DOTS/SortedJobifedEnginesGroup.cs → Svelto.ECS/Extensions/Unity/DOTS/Jobs/SortedJobifiedEnginesGroup.cs View File

@@ -7,15 +7,15 @@ namespace Svelto.ECS.Extensions.Unity
{
/// <summary>
/// Note sorted jobs run in serial
/// </summary>
/// </summary>
/// <typeparam name="Interface"></typeparam>
/// <typeparam name="SequenceOrder"></typeparam>
public abstract class SortedJobifedEnginesGroup<Interface, SequenceOrder> : IJobifiedGroupEngine
public abstract class SortedJobifiedEnginesGroup<Interface, SequenceOrder>
where SequenceOrder : struct, ISequenceOrder where Interface : class, IJobifiedEngine
{
protected SortedJobifedEnginesGroup(FasterList<Interface> engines)
protected SortedJobifiedEnginesGroup(FasterList<Interface> engines)
{
_name = "SortedJobifedEnginesGroup - "+this.GetType().Name;
_name = "SortedJobifiedEnginesGroup - "+this.GetType().Name;
_instancedSequence = new Sequence<Interface, SequenceOrder>(engines);
}

@@ -28,7 +28,7 @@ namespace Svelto.ECS.Extensions.Unity
for (var index = 0; index < sequenceItems.count; index++)
{
var engine = sequenceItems[index];
using (profiler.Sample(engine.name)) combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles));
using (profiler.Sample(engine.name)) combinedHandles = engine.Execute(combinedHandles);
}
}

@@ -41,12 +41,12 @@ namespace Svelto.ECS.Extensions.Unity
readonly Sequence<Interface, SequenceOrder> _instancedSequence;
}
public abstract class SortedJobifedEnginesGroup<Interface, Parameter, SequenceOrder>: IJobifiedGroupEngine<Parameter>
public abstract class SortedJobifiedEnginesGroup<Interface, Parameter, SequenceOrder>: IJobifiedGroupEngine<Parameter>
where SequenceOrder : struct, ISequenceOrder where Interface : class, IJobifiedEngine<Parameter>
{
protected SortedJobifedEnginesGroup(FasterList<Interface> engines)
protected SortedJobifiedEnginesGroup(FasterList<Interface> engines)
{
_name = "SortedJobifedEnginesGroup - "+this.GetType().Name;
_name = "SortedJobifiedEnginesGroup - "+this.GetType().Name;
_instancedSequence = new Sequence<Interface, SequenceOrder>(engines);
}

@@ -58,8 +58,7 @@ namespace Svelto.ECS.Extensions.Unity
for (var index = 0; index < sequenceItems.count; index++)
{
var engine = sequenceItems[index];
using (profiler.Sample(engine.name)) combinedHandles =
JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles, ref param));
using (profiler.Sample(engine.name)) combinedHandles = engine.Execute(combinedHandles, ref param);
}
}


+ 68
- 0
Svelto.ECS/Extensions/Unity/DOTS/Jobs/UnityJobExtensions.cs View File

@@ -0,0 +1,68 @@
#if UNITY_NATIVE
using System;
using Svelto.ECS.Extensions.Unity;
using Unity.Jobs;

namespace Svelto.ECS
{
#if UNITY_JOBS
public static class UnityJobExtensions
{
public static JobHandle ScheduleParallel
<JOB>(this JOB job, uint iterations, JobHandle inputDeps) where JOB: struct, IJobParallelForBatch
{
if (iterations == 0)
return inputDeps;
var innerloopBatchCount = ProcessorCount.BatchSize((uint) iterations);
return job.ScheduleBatch((int)iterations, innerloopBatchCount, inputDeps);
}
public static JobHandle ScheduleParallel
<JOB>(this JOB job, int iterations, JobHandle inputDeps) where JOB: struct, IJobParallelForBatch
{
if (iterations <= 0)
return inputDeps;
var innerloopBatchCount = ProcessorCount.BatchSize((uint) iterations);
return job.ScheduleBatch((int)iterations, innerloopBatchCount, inputDeps);
}
}
#endif
public static class UnityJobExtensions2
{
public static JobHandle ScheduleDispose
<T1>(this T1 disposable, JobHandle inputDeps) where T1 : struct, IDisposable
{
return new DisposeJob<T1>(disposable).Schedule(inputDeps);
}
public static JobHandle ScheduleDispose
<T1, T2>(this T1 disposable1, T2 disposable2, JobHandle inputDeps)
where T1 : struct, IDisposable where T2 : struct, IDisposable
{
return new DisposeJob<T1, T2>(disposable1, disposable2).Schedule(inputDeps);
}
public static JobHandle ScheduleParallel
<JOB>(this JOB job, int iterations, JobHandle inputDeps) where JOB: struct, IJobParallelFor
{
if (iterations <= 0)
return inputDeps;
var innerloopBatchCount = ProcessorCount.BatchSize((uint) iterations);
return job.Schedule((int)iterations, innerloopBatchCount, inputDeps);
}
public static JobHandle ScheduleParallel
<JOB>(this JOB job, uint iterations, JobHandle inputDeps) where JOB: struct, IJobParallelFor
{
if (iterations == 0)
return inputDeps;
var innerloopBatchCount = ProcessorCount.BatchSize(iterations);
return job.Schedule((int)iterations, innerloopBatchCount, inputDeps);
}
}
}
#endif

Svelto.ECS/Extensions/Unity/DOTS/EnginesRoot.NativeOperation.cs → Svelto.ECS/Extensions/Unity/DOTS/Native/EnginesRoot.NativeOperation.cs View File

@@ -1,9 +1,8 @@
#if UNITY_BURST
#if UNITY_NATIVE
using System;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.DataStructures.Unity;
using Unity.Jobs.LowLevel.Unsafe;
using Svelto.ECS.DataStructures;

namespace Svelto.ECS
{
@@ -11,13 +10,13 @@ namespace Svelto.ECS
{
//todo: I very likely don't need to create one for each native entity factory, the same can be reused
readonly AtomicNativeBags _addOperationQueue =
new AtomicNativeBags(Common.Allocator.Persistent, JobsUtility.MaxJobThreadCount + 1);
new AtomicNativeBags(Common.Allocator.Persistent);

readonly AtomicNativeBags _removeOperationQueue =
new AtomicNativeBags(Common.Allocator.Persistent, JobsUtility.MaxJobThreadCount + 1);
new AtomicNativeBags(Common.Allocator.Persistent);

readonly AtomicNativeBags _swapOperationQueue =
new AtomicNativeBags(Common.Allocator.Persistent, JobsUtility.MaxJobThreadCount + 1);
new AtomicNativeBags(Common.Allocator.Persistent);

NativeEntityRemove ProvideNativeEntityRemoveQueue<T>(string memberName) where T : IEntityDescriptor, new()
{
@@ -57,12 +56,13 @@ namespace Svelto.ECS

while (buffer.IsEmpty() == false)
{
var componentsIndex = buffer.Dequeue<uint>();
var entityEGID = buffer.Dequeue<EGID>();
CheckRemoveEntityID(entityEGID, _nativeRemoveOperations[componentsIndex].type);
var componentsIndex = buffer.Dequeue<uint>();
var entityEGID = buffer.Dequeue<EGID>();
var nativeRemoveOperation = _nativeRemoveOperations[componentsIndex];
CheckRemoveEntityID(entityEGID, nativeRemoveOperation.entityDescriptorType);
QueueEntitySubmitOperation(new EntitySubmitOperation(
EntitySubmitOperationType.Remove, entityEGID, entityEGID
, _nativeRemoveOperations[componentsIndex].components));
, nativeRemoveOperation.components));
}
}

@@ -75,12 +75,14 @@ namespace Svelto.ECS
var componentsIndex = buffer.Dequeue<uint>();
var entityEGID = buffer.Dequeue<DoubleEGID>();
CheckRemoveEntityID(entityEGID.@from, _nativeSwapOperations[componentsIndex].type, _nativeSwapOperations[componentsIndex].caller );
CheckAddEntityID(entityEGID.to, _nativeSwapOperations[componentsIndex].type, _nativeSwapOperations[componentsIndex].caller);
var componentBuilders = _nativeSwapOperations[componentsIndex].components;

CheckRemoveEntityID(entityEGID.@from, _nativeSwapOperations[componentsIndex].entityDescriptorType, _nativeSwapOperations[componentsIndex].caller );
CheckAddEntityID(entityEGID.to, _nativeSwapOperations[componentsIndex].entityDescriptorType, _nativeSwapOperations[componentsIndex].caller);

QueueEntitySubmitOperation(new EntitySubmitOperation(
EntitySubmitOperationType.Swap, entityEGID.@from, entityEGID.to
, _nativeSwapOperations[componentsIndex].components));
, componentBuilders));
}
}
}
@@ -98,7 +100,7 @@ namespace Svelto.ECS
var componentCounts = buffer.Dequeue<uint>();
EntityComponentInitializer init =
BuildEntity(egid, _nativeAddOperations[componentsIndex].components, _nativeAddOperations[componentsIndex].type);
BuildEntity(egid, _nativeAddOperations[componentsIndex].components, _nativeAddOperations[componentsIndex].entityDescriptorType);

//only called if Init is called on the initialized (there is something to init)
while (componentCounts > 0)
@@ -144,11 +146,11 @@ namespace Svelto.ECS
readonly struct NativeOperationBuild
{
internal readonly IComponentBuilder[] components;
internal readonly Type type;
internal readonly Type entityDescriptorType;

public NativeOperationBuild(IComponentBuilder[] descriptorComponentsToBuild, Type entityType)
public NativeOperationBuild(IComponentBuilder[] descriptorComponentsToBuild, Type entityDescriptorType)
{
type = entityType;
this.entityDescriptorType = entityDescriptorType;
components = descriptorComponentsToBuild;
}
}
@@ -156,28 +158,28 @@ namespace Svelto.ECS
readonly struct NativeOperationRemove
{
internal readonly IComponentBuilder[] components;
internal readonly Type type;
internal readonly Type entityDescriptorType;
internal readonly string caller;
public NativeOperationRemove(IComponentBuilder[] descriptorComponentsToRemove, Type entityType, string caller)
public NativeOperationRemove(IComponentBuilder[] descriptorComponentsToRemove, Type entityDescriptorType, string caller)
{
this.caller = caller;
components = descriptorComponentsToRemove;
type = entityType;
this.caller = caller;
components = descriptorComponentsToRemove;
this.entityDescriptorType = entityDescriptorType;
}
}

readonly struct NativeOperationSwap
{
internal readonly IComponentBuilder[] components;
internal readonly Type type;
internal readonly Type entityDescriptorType;
internal readonly string caller;

public NativeOperationSwap(IComponentBuilder[] descriptorComponentsToSwap, Type entityType, string caller)
public NativeOperationSwap(IComponentBuilder[] descriptorComponentsToSwap, Type entityDescriptorType, string caller)
{
this.caller = caller;
components = descriptorComponentsToSwap;
type = entityType;
this.caller = caller;
components = descriptorComponentsToSwap;
this.entityDescriptorType = entityDescriptorType;
}
}
}

+ 97
- 0
Svelto.ECS/Extensions/Unity/DOTS/Native/NativeEGIDMapper.cs View File

@@ -0,0 +1,97 @@
#if UNITY_NATIVE
using System;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;

namespace Svelto.ECS
{
public readonly struct NativeEGIDMapper<T>:IEGIDMapper where T : unmanaged, IEntityComponent
{
public NativeEGIDMapper
(ExclusiveGroupStruct groupStructId, SveltoDictionaryNative<uint, T> toNative) : this()
{
groupID = groupStructId;
_map = toNative;
}

public int count => _map.count;
public Type entityType => TypeCache<T>.type;
public ExclusiveGroupStruct groupID { get; }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Entity(uint entityID)
{
#if DEBUG && !PROFILE_SVELTO
if (_map.TryFindIndex(entityID, out var findIndex) == false)
throw new Exception("Entity not found in this group ".FastConcat(typeof(T).ToString()));
#else
_map.TryFindIndex(entityID, out var findIndex);
#endif
return ref _map.GetDirectValueByRef(findIndex);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetEntity(uint entityID, out T value)
{
if (_map.count > 0 && _map.TryFindIndex(entityID, out var index))
{
unsafe
{
value = Unsafe.AsRef<T>(Unsafe.Add<T>((void*) _map.GetValues(out _).ToNativeArray(out _)
, (int) index));
return true;
}
}

value = default;
return false;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NB<T> GetArrayAndEntityIndex(uint entityID, out uint index)
{
if (_map.TryFindIndex(entityID, out index))
{
return new NB<T>(_map.GetValues(out var count).ToNativeArray(out _), count);
}

throw new ECSException("Entity not found");
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out NB<T> array)
{
index = 0;
if (_map.count > 0 && _map.TryFindIndex(entityID, out index))
{
array = new NB<T>(_map.GetValues(out var count).ToNativeArray(out _), count);
return true;
}

array = default;
return false;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists(uint idEntityId)
{
return _map.count > 0 && _map.TryFindIndex(idEntityId, out _);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetIndex(uint entityID)
{
return _map.GetIndex(entityID);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool FindIndex(uint valueKey, out uint index)
{
return _map.TryFindIndex(valueKey, out index);
}
readonly ReadonlySveltoDictionaryNative<uint, T> _map;
}
}
#endif

+ 55
- 0
Svelto.ECS/Extensions/Unity/DOTS/Native/NativeEGIDMultiMapper.cs View File

@@ -0,0 +1,55 @@
#if UNITY_NATIVE
using System;
using Svelto.DataStructures;

namespace Svelto.ECS
{
public struct NativeEGIDMultiMapper<T>:IDisposable where T : unmanaged, IEntityComponent
{
SveltoDictionary<ExclusiveGroupStruct, SveltoDictionary<uint, T,
NativeStrategy<FasterDictionaryNode<uint>>,
NativeStrategy<T>,
NativeStrategy<int>>,
NativeStrategy<FasterDictionaryNode<ExclusiveGroupStruct>>,
NativeStrategy<SveltoDictionary<uint, T,
NativeStrategy<FasterDictionaryNode<uint>>,
NativeStrategy<T>,
NativeStrategy<int>>>,
NativeStrategy<int>> _dic;

public NativeEGIDMultiMapper
(SveltoDictionary<ExclusiveGroupStruct, SveltoDictionary<uint, T,
NativeStrategy<FasterDictionaryNode<uint>>,
NativeStrategy<T>,
NativeStrategy<int>>,
NativeStrategy<FasterDictionaryNode<ExclusiveGroupStruct>>,
NativeStrategy<SveltoDictionary<uint, T,
NativeStrategy<FasterDictionaryNode<uint>>,
NativeStrategy<T>,
NativeStrategy<int>>>,
NativeStrategy<int>> dictionary)
{
_dic = dictionary;
}

public int count => (int) _dic.count;

public void Dispose()
{
_dic.Dispose();
}

public ref T Entity(EGID entity)
{
ref var sveltoDictionary = ref _dic.GetValueByRef(entity.groupID);
return ref sveltoDictionary.GetValueByRef(entity.entityID);
}

public bool Exists(EGID entity)
{
return _dic.TryFindIndex(entity.groupID, out var index)
&& _dic.GetDirectValueByRef(index).ContainsKey(entity.entityID);
}
}
}
#endif

Svelto.ECS/Extensions/Unity/DOTS/NativeEntityComponentInitializer.cs → Svelto.ECS/Extensions/Unity/DOTS/Native/NativeEntityComponentInitializer.cs View File


Svelto.ECS/Extensions/Unity/DOTS/NativeEntityFactory.cs → Svelto.ECS/Extensions/Unity/DOTS/Native/NativeEntityFactory.cs View File

@@ -1,27 +1,26 @@
#if UNITY_BURST
#if UNITY_NATIVE
using Svelto.ECS.DataStructures;
using Svelto.ECS.DataStructures.Unity;

namespace Svelto.ECS
{
public readonly struct NativeEntityFactory
{
readonly AtomicNativeBags _addOperationQueue;
readonly uint _index;
readonly int _index;

internal NativeEntityFactory(AtomicNativeBags addOperationQueue, uint index)
internal NativeEntityFactory(AtomicNativeBags addOperationQueue, int index)
{
_index = index;
_addOperationQueue = addOperationQueue;
}

public NativeEntityComponentInitializer BuildEntity
(uint eindex, ExclusiveGroupStruct buildGroup, int threadIndex)
(uint eindex, BuildGroup BuildGroup, int threadIndex)
{
NativeBag unsafeBuffer = _addOperationQueue.GetBuffer(threadIndex + 1);

unsafeBuffer.Enqueue(_index);
unsafeBuffer.Enqueue(new EGID(eindex, buildGroup));
unsafeBuffer.Enqueue(new EGID(eindex, BuildGroup));
unsafeBuffer.ReserveEnqueue<uint>(out var index) = 0;

return new NativeEntityComponentInitializer(unsafeBuffer, index);

Svelto.ECS/Extensions/Unity/DOTS/NativeEntityRemove.cs → Svelto.ECS/Extensions/Unity/DOTS/Native/NativeEntityRemove.cs View File

@@ -1,14 +1,14 @@
#if UNITY_BURST
using Svelto.ECS.DataStructures.Unity;
#if UNITY_NATIVE
using Svelto.ECS.DataStructures;

namespace Svelto.ECS
{
public readonly struct NativeEntityRemove
{
readonly AtomicNativeBags _removeQueue;
readonly uint _indexRemove;
readonly int _indexRemove;

internal NativeEntityRemove(AtomicNativeBags EGIDsToRemove, uint indexRemove)
internal NativeEntityRemove(AtomicNativeBags EGIDsToRemove, int indexRemove)
{
_removeQueue = EGIDsToRemove;
_indexRemove = indexRemove;

Svelto.ECS/Extensions/Unity/DOTS/NativeEntitySwap.cs → Svelto.ECS/Extensions/Unity/DOTS/Native/NativeEntitySwap.cs View File

@@ -1,14 +1,14 @@
#if UNITY_BURST
using Svelto.ECS.DataStructures.Unity;
#if UNITY_NATIVE
using Svelto.ECS.DataStructures;

namespace Svelto.ECS
{
public readonly struct NativeEntitySwap
{
readonly AtomicNativeBags _swapQueue;
readonly uint _indexSwap;
readonly int _indexSwap;

internal NativeEntitySwap(AtomicNativeBags EGIDsToSwap, uint indexSwap)
internal NativeEntitySwap(AtomicNativeBags EGIDsToSwap, int indexSwap)
{
_swapQueue = EGIDsToSwap;
_indexSwap = indexSwap;
@@ -22,7 +22,7 @@ namespace Svelto.ECS
}

public void SwapEntity(EGID from, ExclusiveGroupStruct to, int threadIndex)
public void SwapEntity(EGID from, BuildGroup to, int threadIndex)
{
var simpleNativeBag = _swapQueue.GetBuffer(threadIndex);
simpleNativeBag.Enqueue(_indexSwap);

+ 73
- 0
Svelto.ECS/Extensions/Unity/DOTS/Native/UnityEntityDBExtensions.cs View File

@@ -0,0 +1,73 @@
#if UNITY_NATIVE
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public static class UnityEntityDBExtensions
{
internal static NativeEGIDMapper<T> ToNativeEGIDMapper<T>(this TypeSafeDictionary<T> dic,
ExclusiveGroupStruct groupStructId) where T : unmanaged, IEntityComponent
{
var mapper = new NativeEGIDMapper<T>(groupStructId, dic.implUnmgd);

return mapper;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NativeEGIDMapper<T> QueryNativeMappedEntities<T>(this EntitiesDB entitiesDb, ExclusiveGroupStruct groupStructId)
where T : unmanaged, IEntityComponent
{
if (entitiesDb.SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false)
throw new EntityGroupNotFoundException(typeof(T), groupStructId.ToName());

return (typeSafeDictionary as TypeSafeDictionary<T>).ToNativeEGIDMapper(groupStructId);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryQueryNativeMappedEntities<T>(this EntitiesDB entitiesDb, ExclusiveGroupStruct groupStructId,
out NativeEGIDMapper<T> mapper)
where T : unmanaged, IEntityComponent
{
mapper = default;
if (entitiesDb.SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false ||
typeSafeDictionary.count == 0)
return false;

mapper = (typeSafeDictionary as TypeSafeDictionary<T>).ToNativeEGIDMapper(groupStructId);

return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NativeEGIDMultiMapper<T> QueryNativeMappedEntities<T>(this EntitiesDB entitiesDb,
LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
where T : unmanaged, IEntityComponent
{
var dictionary =
new SveltoDictionary<ExclusiveGroupStruct, SveltoDictionary<uint, T,
NativeStrategy<FasterDictionaryNode<uint>>,
NativeStrategy<T>,
NativeStrategy<int>>,
NativeStrategy<FasterDictionaryNode<ExclusiveGroupStruct>>,
NativeStrategy<SveltoDictionary<uint, T,
NativeStrategy<FasterDictionaryNode<uint>>,
NativeStrategy<T>,
NativeStrategy<int>>>,
NativeStrategy<int>>
((uint) groups.count, Allocator.TempJob);
foreach (var group in groups)
{
if (entitiesDb.SafeQueryEntityDictionary<T>(group, out var typeSafeDictionary) == true)
if (typeSafeDictionary.count > 0)
dictionary.Add(group, ((TypeSafeDictionary<T>)typeSafeDictionary).implUnmgd);
}
return new NativeEGIDMultiMapper<T>(dictionary);
}
}
}
#endif

+ 0
- 83
Svelto.ECS/Extensions/Unity/DOTS/NativeEGIDMapper.cs View File

@@ -1,83 +0,0 @@
#if UNITY_BURST
using System;
using System.Runtime.CompilerServices;
using Svelto.DataStructures;

namespace Svelto.ECS
{
public readonly struct NativeEGIDMapper<T> where T : unmanaged, IEntityComponent
{
readonly SveltoDictionaryNative<uint, T> map;
public ExclusiveGroupStruct groupID { get; }

public NativeEGIDMapper
(ExclusiveGroupStruct groupStructId
, SveltoDictionary<uint, T, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<T>> toNative) : this()
{
groupID = groupStructId;
map = toNative;
}

public uint Count => map.count;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Entity(uint entityID)
{
#if DEBUG && !PROFILE_SVELTO
if (map.TryFindIndex(entityID, out var findIndex) == false)
throw new Exception("Entity not found in this group ".FastConcat(typeof(T).ToString()));
#else
map.TryFindIndex(entityID, out var findIndex);
#endif
return ref map.GetDirectValueByRef(findIndex);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetEntity(uint entityID, out T value)
{
if (map.count > 0 && map.TryFindIndex(entityID, out var index))
{
unsafe
{
value = Unsafe.AsRef<T>(Unsafe.Add<T>((void*) map.GetValues(out _).ToNativeArray(out _)
, (int) index));
return true;
}
}

value = default;
return false;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NB<T> GetArrayAndEntityIndex(uint entityID, out uint index)
{
if (map.TryFindIndex(entityID, out index))
{
return new NB<T>((IntPtr) map.GetValues(out var count).ToNativeArray(out _), count);
}

throw new ECSException("Entity not found");
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out NB<T> array)
{
index = 0;
if (map.count > 0 && map.TryFindIndex(entityID, out index))
{
array = new NB<T>((IntPtr) map.GetValues(out var count).ToNativeArray(out _), count);
return true;
}

array = default;
return false;
}

public bool Exists(uint idEntityId)
{
return map.count > 0 && map.TryFindIndex(idEntityId, out _);
}
}
}
#endif

+ 0
- 43
Svelto.ECS/Extensions/Unity/DOTS/NativeEGIDMultiMapper.cs View File

@@ -1,43 +0,0 @@
#if UNITY_BURST
using System;
using Svelto.DataStructures;

namespace Svelto.ECS
{
public struct NativeEGIDMultiMapper<T>:IDisposable where T : unmanaged, IEntityComponent
{
SveltoDictionary<ExclusiveGroupStruct,
SveltoDictionary<uint, T, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<T>>,
NativeStrategy<FasterDictionaryNode<ExclusiveGroupStruct>>, NativeStrategy<
SveltoDictionary<uint, T, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<T>>>> _dic;

public NativeEGIDMultiMapper
(SveltoDictionary<ExclusiveGroupStruct,
SveltoDictionary<uint, T, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<T>>,
NativeStrategy<FasterDictionaryNode<ExclusiveGroupStruct>>, NativeStrategy<
SveltoDictionary<uint, T, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<T>>>> dictionary)
{
_dic = dictionary;
}

public int count => (int) _dic.count;

public void Dispose()
{
_dic.Dispose();
}

public ref T Entity(EGID entity)
{
ref var sveltoDictionary = ref _dic.GetValueByRef(entity.groupID);
return ref sveltoDictionary.GetValueByRef(entity.entityID);
}

public bool Exists(EGID entity)
{
return _dic.TryFindIndex(entity.groupID, out var index)
&& _dic.GetDirectValueByRef(index).ContainsKey(entity.entityID);
}
}
}
#endif

+ 0
- 29
Svelto.ECS/Extensions/Unity/DOTS/PureUECSSystemsGroup.cs View File

@@ -1,29 +0,0 @@
#if UNITY_ECS
using Svelto.Common;
using Unity.Entities;
using Unity.Jobs;

namespace Svelto.ECS.Extensions.Unity
{
[Sequenced(nameof(JobifiedSveltoEngines.PureUECSSystemsGroup))]
[DisableAutoCreation]
public class PureUECSSystemsGroup : IJobifiedEngine
{
public PureUECSSystemsGroup(World world)
{
_world = world;
}

public JobHandle Execute(JobHandle _jobHandle)
{
_world.Update();

return _jobHandle;
}

public string name => nameof(PureUECSSystemsGroup);

readonly World _world;
}
}
#endif

+ 0
- 39
Svelto.ECS/Extensions/Unity/DOTS/SyncSveltoToUECSGroup.cs View File

@@ -1,39 +0,0 @@
#if UNITY_ECS
using Svelto.Common;
using Unity.Entities;
using Unity.Jobs;

namespace Svelto.ECS.Extensions.Unity
{
[Sequenced(nameof(JobifiedSveltoEngines.CopySveltoToUECSEnginesGroup))]
[DisableAutoCreation]
public class SyncSveltoToUECSGroup : ComponentSystemGroup, IJobifiedEngine
{
public JobHandle Execute(JobHandle _jobHandle)
{
foreach (var engine in Systems)
(engine as SyncSveltoToUECSEngine).externalHandle = _jobHandle;
Update();
return _jobHandle;
}
public string name => nameof(SyncSveltoToUECSGroup);

readonly SimulationSystemGroup _simulationSystemGroup;
}
public abstract class SyncSveltoToUECSEngine : SystemBase, IEngine
{
internal JobHandle externalHandle;
protected abstract void Execute();

protected sealed override void OnUpdate()
{
Dependency = JobHandle.CombineDependencies(Dependency, externalHandle);
Execute();
}
}
}
#endif

+ 12
- 0
Svelto.ECS/Extensions/Unity/DOTS/UECS/IUECSSubmissionEngine.cs View File

@@ -0,0 +1,12 @@
#if UNITY_ECS
using Unity.Entities;

namespace Svelto.ECS.Extensions.Unity
{
public interface IUECSSubmissionEngine : IJobifiedEngine
{
EntityCommandBuffer ECB { get; set;}
EntityManager EM { get; set;}
}
}
#endif

Svelto.ECS/Extensions/Unity/DOTS/JobifiedSveltoEngines.cs → Svelto.ECS/Extensions/Unity/DOTS/UECS/JobifiedSveltoEngines.cs View File

@@ -1,10 +1,9 @@
#if UNITY_BURST
#if UNITY_ECS
namespace Svelto.ECS.Extensions.Unity
{
public enum JobifiedSveltoEngines
{
CopySveltoToUECSEnginesGroup,
PureUECSSystemsGroup
SveltoOverUECS
}
}
#endif

+ 100
- 0
Svelto.ECS/Extensions/Unity/DOTS/UECS/SveltoOverUECSEnginesGroup.cs View File

@@ -0,0 +1,100 @@
#if UNITY_ECS
using Svelto.Common;
using Svelto.ECS.Schedulers;
using Unity.Entities;
using Unity.Jobs;

namespace Svelto.ECS.Extensions.Unity
{
[Sequenced(nameof(JobifiedSveltoEngines.SveltoOverUECS))]
public class SveltoOverUECSEnginesGroup: IJobifiedEngine
{
public SveltoOverUECSEnginesGroup(EnginesRoot enginesRoot)
{
DBC.ECS.Check.Require(enginesRoot.scheduler is ISimpleEntitiesSubmissionScheduler, "The Engines root must use a EntitiesSubmissionScheduler scheduler implementation");

CreateUnityECSWorldForSvelto(enginesRoot.scheduler as ISimpleEntitiesSubmissionScheduler, enginesRoot);
}

public World world { get; private set; }

void CreateUnityECSWorldForSvelto(ISimpleEntitiesSubmissionScheduler scheduler, EnginesRoot enginesRoot)
{
world = new World("Svelto<>UECS world");

var systems = DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.Default);
DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(world, systems);
World.DefaultGameObjectInjectionWorld = world;

//This is the UECS group that takes care of all the UECS systems that creates entities
//it also submits Svelto entities
_sveltoUecsEntitiesSubmissionGroup = new SveltoUECSEntitiesSubmissionGroup(scheduler, world);
enginesRoot.AddEngine(_sveltoUecsEntitiesSubmissionGroup);
//This is the group that handles the UECS sync systems that copy the svelto entities values to UECS entities
_syncSveltoToUecsGroup = new SyncSveltoToUECSGroup();
enginesRoot.AddEngine(_syncSveltoToUecsGroup);
_syncUecsToSveltoGroup = new SyncUECSToSveltoGroup();
enginesRoot.AddEngine(_syncUecsToSveltoGroup);
//This is the group that handles the UECS sync systems that copy the UECS entities values to svelto entities
//enginesRoot.AddEngine(new SveltoUECSEntitiesSubmissionGroup(scheduler, world));
enginesRoot.AddEngine(this);

_enginesRoot = enginesRoot;
}

public JobHandle Execute(JobHandle inputDeps)
{
//this is a sync point, there won't be pending jobs after this
_sveltoUecsEntitiesSubmissionGroup.Execute(inputDeps);

//Mixed explicit job dependency and internal automatic ECS dependency system
//Write in to UECS entities so the UECS dependencies react on the components touched
var handle = _syncSveltoToUecsGroup.Execute(default);

//As long as pure UECS systems do not use external containers (like native arrays and so) the Unity
//automatic dependencies system will guarantee that there won't be race conditions
world.Update();

//this svelto group of UECS SystemBase systems
return _syncUecsToSveltoGroup.Execute(handle);
}

public void AddUECSSubmissionEngine(IUECSSubmissionEngine spawnUnityEntityOnSveltoEntityEngine)
{
_sveltoUecsEntitiesSubmissionGroup.Add(spawnUnityEntityOnSveltoEntityEngine);
_enginesRoot.AddEngine(spawnUnityEntityOnSveltoEntityEngine);
}

public void AddSveltoToUECSEngine(SyncSveltoToUECSEngine engine)
{
//it's a Svelto Engine/UECS SystemBase so it must be added in the UECS world AND svelto enginesRoot
world.AddSystem(engine);
_enginesRoot.AddEngine(engine);

_syncSveltoToUecsGroup.Add(engine);
}

public void AddUECSToSveltoEngine(SyncUECSToSveltoEngine engine)
{
//it's a Svelto Engine/UECS SystemBase so it must be added in the UECS world AND svelto enginesRoot
world.AddSystem(engine);
_enginesRoot.AddEngine(engine);

_syncUecsToSveltoGroup.Add(engine);
}

public void Dispose()
{
world.Dispose();
}

public string name => nameof(SveltoOverUECSEnginesGroup);
SveltoUECSEntitiesSubmissionGroup _sveltoUecsEntitiesSubmissionGroup;
SyncSveltoToUECSGroup _syncSveltoToUecsGroup;
SyncUECSToSveltoGroup _syncUecsToSveltoGroup;
EnginesRoot _enginesRoot;

}
}
#endif

+ 61
- 0
Svelto.ECS/Extensions/Unity/DOTS/UECS/SveltoUECSEntitiesSubmissionGroup.cs View File

@@ -0,0 +1,61 @@
#if UNITY_ECS
using Svelto.ECS.Schedulers;
using Unity.Entities;
using Unity.Jobs;

namespace Svelto.ECS.Extensions.Unity
{
/// <summary>
/// Group of UECS/Svelto SystemBase engines that creates UECS entities.
/// Svelto entities are submitted
/// Svelto Add and remove callback are called
/// OnUpdate of the systems are called
/// finally the UECS command buffer is flushed
/// Note: I cannot use Unity ComponentSystemGroups nor I can rely on the SystemBase Dependency field to
/// solve external dependencies. External dependencies are tracked, but only linked to the UECS components operations
/// With Dependency I cannot guarantee that an external container is used before previous jobs working on it are completed
/// </summary>
public class SveltoUECSEntitiesSubmissionGroup : JobifiedEnginesGroup<IUECSSubmissionEngine>
{
public SveltoUECSEntitiesSubmissionGroup
(ISimpleEntitiesSubmissionScheduler submissionScheduler, World UECSWorld)
{
_submissionScheduler = submissionScheduler;
_ECBSystem = UECSWorld.CreateSystem<SubmissionEntitiesCommandBufferSystem>();
}

public new void Execute(JobHandle jobHandle)
{
//Sync Point as we must be sure that jobs that create/swap/remove entities are done
jobHandle.Complete();

if (_submissionScheduler.paused)
return;

//prepare the entity command buffer to be used by the registered engines
var entityCommandBuffer = _ECBSystem.CreateCommandBuffer();

foreach (var system in _engines)
{
system.ECB = entityCommandBuffer;
system.EM = _ECBSystem.EntityManager;
}

//Submit Svelto Entities, calls Add/Remove/MoveTo that can be used by the IUECSSubmissionEngines
_submissionScheduler.SubmitEntities();

//execute submission engines and complete jobs
base.Execute(default).Complete();

//flush command buffer
_ECBSystem.Update();
}

readonly ISimpleEntitiesSubmissionScheduler _submissionScheduler;
readonly SubmissionEntitiesCommandBufferSystem _ECBSystem;

[DisableAutoCreation]
class SubmissionEntitiesCommandBufferSystem : EntityCommandBufferSystem { }
}
}
#endif

+ 25
- 0
Svelto.ECS/Extensions/Unity/DOTS/UECS/SyncSveltoToUECSGroup.cs View File

@@ -0,0 +1,25 @@
#if UNITY_ECS
using Unity.Entities;
using Unity.Jobs;

namespace Svelto.ECS.Extensions.Unity
{
public class SyncSveltoToUECSGroup : JobifiedEnginesGroup<SyncSveltoToUECSEngine>
{
}

public abstract class SyncSveltoToUECSEngine : SystemBase, IJobifiedEngine
{
public JobHandle Execute(JobHandle inputDeps)
{
Dependency = JobHandle.CombineDependencies(Dependency, inputDeps);
Update();

return Dependency;
}

public abstract string name { get; }
}
}
#endif

+ 26
- 0
Svelto.ECS/Extensions/Unity/DOTS/UECS/SyncUECSToSveltoGroup.cs View File

@@ -0,0 +1,26 @@
#if UNITY_ECS
using Unity.Entities;
using Unity.Jobs;

namespace Svelto.ECS.Extensions.Unity
{
public class SyncUECSToSveltoGroup : JobifiedEnginesGroup<SyncUECSToSveltoEngine>
{
}

public abstract class SyncUECSToSveltoEngine : SystemBase, IJobifiedEngine
{
public JobHandle Execute(JobHandle inputDeps)
{
Dependency = JobHandle.CombineDependencies(Dependency, inputDeps);
Update();

return Dependency;
}

public abstract string name { get; }
}
}
#endif

Svelto.ECS/Extensions/Unity/DOTS/UECSSveltoEGID.cs → Svelto.ECS/Extensions/Unity/DOTS/UECS/UECSSveltoEGID.cs View File


+ 0
- 119
Svelto.ECS/Extensions/Unity/DOTS/UnityEntityDBExtensions.cs View File

@@ -1,119 +0,0 @@
#if UNITY_JOBS
using System;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Extensions.Unity;
using Svelto.ECS.Internal;
using Unity.Jobs;

namespace Svelto.ECS
{
public static class UnityEntityDBExtensions2
{
public static JobHandle ScheduleParallel
<JOB>(this JOB job, uint iterations, JobHandle inputDeps) where JOB: struct, IJobParallelForBatch
{
if (iterations == 0)
return inputDeps;
var innerloopBatchCount = ProcessorCount.BatchSize(iterations);
return job.ScheduleBatch((int)iterations, innerloopBatchCount, inputDeps);
}
public static JobHandle ScheduleParallel
<JOB>(this JOB job, int iterations, JobHandle inputDeps) where JOB: struct, IJobParallelForBatch
{
if (iterations <= 0)
return inputDeps;
var innerloopBatchCount = ProcessorCount.BatchSize((uint) iterations);
return job.ScheduleBatch((int)iterations, innerloopBatchCount, inputDeps);
}
}

public static class UnityEntityDBExtensions
{
internal static NativeEGIDMapper<T> ToNativeEGIDMapper<T>(this TypeSafeDictionary<T> dic,
ExclusiveGroupStruct groupStructId) where T : unmanaged, IEntityComponent
{
var mapper = new NativeEGIDMapper<T>(groupStructId, dic.implUnmgd);

return mapper;
}

public static JobHandle ScheduleDispose
<T1>(this T1 disposable, JobHandle inputDeps) where T1 : struct, IDisposable
{
return new DisposeJob<T1>(disposable).Schedule(inputDeps);
}
public static JobHandle ScheduleDispose
<T1, T2>(this T1 disposable1, T2 disposable2, JobHandle inputDeps)
where T1 : struct, IDisposable where T2 : struct, IDisposable
{
return new DisposeJob<T1, T2>(disposable1, disposable2).Schedule(inputDeps);
}
public static JobHandle ScheduleParallel
<JOB>(this JOB job, uint iterations, JobHandle inputDeps) where JOB: struct, IJobParallelFor
{
if (iterations == 0)
return inputDeps;
var innerloopBatchCount = ProcessorCount.BatchSize(iterations);
return job.Schedule((int)iterations, innerloopBatchCount, inputDeps);
}
public static JobHandle ScheduleParallel
<JOB>(this JOB job, int iterations, JobHandle inputDeps) where JOB: struct, IJobParallelFor
{
if (iterations <= 0)
return inputDeps;
var innerloopBatchCount = ProcessorCount.BatchSize((uint) iterations);
return job.Schedule((int)iterations, innerloopBatchCount, inputDeps);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NativeEGIDMapper<T> QueryNativeMappedEntities<T>(this EntitiesDB entitiesDb, ExclusiveGroupStruct groupStructId)
where T : unmanaged, IEntityComponent
{
if (entitiesDb.SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false)
throw new EntityGroupNotFoundException(typeof(T));

return (typeSafeDictionary as TypeSafeDictionary<T>).ToNativeEGIDMapper<T>(groupStructId);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryQueryNativeMappedEntities<T>(this EntitiesDB entitiesDb, ExclusiveGroupStruct groupStructId,
out NativeEGIDMapper<T> mapper)
where T : unmanaged, IEntityComponent
{
mapper = default;
if (entitiesDb.SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false ||
typeSafeDictionary.count == 0)
return false;

mapper = (typeSafeDictionary as TypeSafeDictionary<T>).ToNativeEGIDMapper(groupStructId);

return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NativeEGIDMultiMapper<T> QueryNativeMappedEntities<T>(this EntitiesDB entitiesDb, LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
where T : unmanaged, IEntityComponent
{
var dictionary =
new SveltoDictionary<ExclusiveGroupStruct, SveltoDictionary<uint, T, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<T>>,
NativeStrategy<FasterDictionaryNode<ExclusiveGroupStruct>>, NativeStrategy<SveltoDictionary<uint, T, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<T>>>>
(groups.count, Allocator.TempJob);
foreach (var group in groups)
{
if (entitiesDb.SafeQueryEntityDictionary<T>(group, out var typeSafeDictionary) == true)
if (typeSafeDictionary.count > 0)
dictionary.Add(group, ((TypeSafeDictionary<T>)typeSafeDictionary).implUnmgd);
}
return new NativeEGIDMultiMapper<T>(dictionary);
}
}
}
#endif

+ 30
- 0
Svelto.ECS/Extensions/Unity/EntityDescriptorHolderHelper.cs View File

@@ -0,0 +1,30 @@
#if UNITY_ECS
using Svelto.ECS.Hybrid;
using UnityEngine;

namespace Svelto.ECS.Extensions.Unity
{
public static class EntityDescriptorHolderHelper
{
public static EntityComponentInitializer CreateEntity<T>(this Transform contextHolder, EGID ID,
IEntityFactory factory, out T holder)
where T : MonoBehaviour, IEntityDescriptorHolder
{
holder = contextHolder.GetComponentInChildren<T>(true);
var implementors = holder.GetComponents<IImplementor>();

return factory.BuildEntity(ID, holder.GetDescriptor(), implementors);
}
public static EntityComponentInitializer Create<T>(this Transform contextHolder, EGID ID,
IEntityFactory factory)
where T : MonoBehaviour, IEntityDescriptorHolder
{
var holder = contextHolder.GetComponentInChildren<T>(true);
var implementors = holder.GetComponents<IImplementor>();

return factory.BuildEntity(ID, holder.GetDescriptor(), implementors);
}
}
}
#endif

+ 6
- 2
Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs View File

@@ -3,8 +3,7 @@ using UnityEngine;

namespace Svelto.ECS.Extensions.Unity
{
public abstract class GenericEntityDescriptorHolder<T>:
MonoBehaviour , IEntityDescriptorHolder
public abstract class GenericEntityDescriptorHolder<T>: MonoBehaviour , IEntityDescriptorHolder
where T: IEntityDescriptor, new()
{
public IEntityDescriptor GetDescriptor()
@@ -12,6 +11,11 @@ namespace Svelto.ECS.Extensions.Unity
return EntityDescriptorTemplate<T>.descriptor;
}

public T GetRealDescriptor()
{
return EntityDescriptorTemplate<T>.realDescriptor;
}
public string groupName => _groupName;
public ushort id => _id;



+ 74
- 51
Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs View File

@@ -1,34 +1,23 @@
#if UNITY_5 || UNITY_5_3_OR_NEWER
using System;
using Svelto.ECS.Hybrid;
using UnityEngine;

namespace Svelto.ECS.Extensions.Unity
{
public static class EntityDescriptorHolderHelper
{
public static EntityComponentInitializer CreateEntity<T>(this Transform contextHolder, EGID ID,
IEntityFactory factory, out T holder)
where T : MonoBehaviour, IEntityDescriptorHolder
{
holder = contextHolder.GetComponentInChildren<T>(true);
var implementors = holder.GetComponents<IImplementor>();

return factory.BuildEntity(ID, holder.GetDescriptor(), implementors);
}
public static EntityComponentInitializer Create<T>(this Transform contextHolder, EGID ID,
IEntityFactory factory)
where T : MonoBehaviour, IEntityDescriptorHolder
{
var holder = contextHolder.GetComponentInChildren<T>(true);
var implementors = holder.GetComponents<IImplementor>();

return factory.BuildEntity(ID, holder.GetDescriptor(), implementors);
}
}
public static class SveltoGUIHelper
{
/// <summary>
/// This is the suggested way to create GUIs from prefabs now.
/// </summary>
/// <param name="startIndex"></param>
/// <param name="contextHolder"></param>
/// <param name="factory"></param>
/// <param name="group"></param>
/// <param name="searchImplementorsInChildren"></param>
/// <param name="groupNamePostfix"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T CreateFromPrefab<T>(ref uint startIndex, Transform contextHolder, IEntityFactory factory,
ExclusiveGroup group, bool searchImplementorsInChildren = false, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder
{
@@ -37,64 +26,96 @@ namespace Svelto.ECS.Extensions.Unity

foreach (var child in children)
{
IImplementor[] childImplementors;
if (child.GetType() != typeof(T))
{
var monoBehaviour = child as MonoBehaviour;
var monoBehaviour = child as MonoBehaviour;
IImplementor[] childImplementors;
if (searchImplementorsInChildren == false)
childImplementors = monoBehaviour.GetComponents<IImplementor>();
else
childImplementors = monoBehaviour.GetComponentsInChildren<IImplementor>(true);
startIndex = InternalBuildAll(
startIndex,
child,
factory,
group,
childImplementors,
groupNamePostfix);
startIndex = InternalBuildAll(startIndex, child, factory, group, childImplementors,
groupNamePostfix);
}
}

return holder;
}

public static EntityComponentInitializer Create<T>(EGID ID, Transform contextHolder,
IEntityFactory factory, out T holder, bool searchImplementorsInChildren = false)
where T : MonoBehaviour, IEntityDescriptorHolder
/// <summary>
/// Creates all the entities in a hierarchy. This was commonly used to create entities from gameobjects
/// already present in the scene
/// </summary>
/// <param name="startIndex"></param>
/// <param name="group"></param>
/// <param name="contextHolder"></param>
/// <param name="factory"></param>
/// <param name="groupNamePostfix"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static uint CreateAll<T>(uint startIndex, ExclusiveGroup group,
Transform contextHolder, IEntityFactory factory, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder
{
holder = contextHolder.GetComponentInChildren<T>(true);
var implementors = searchImplementorsInChildren == false ? holder.GetComponents<IImplementor>() : holder.GetComponentsInChildren<IImplementor>(true) ;
var holders = contextHolder.GetComponentsInChildren<T>(true);

return factory.BuildEntity(ID, holder.GetDescriptor(), implementors);
foreach (var holder in holders)
{
var implementors = holder.GetComponents<IImplementor>();
try
{
startIndex = InternalBuildAll(startIndex, holder, factory, group, implementors, groupNamePostfix);
}
catch (Exception ex)
{
throw new Exception($"When building entity from game object {Path(holder.transform)}", ex);
}
}

return startIndex;
}

static string Path(Transform go)
{
string s = go.name;
while (go.parent != null)
{
go = go.parent;
s = go.name + "/" + s;
}
return s;
}
public static EntityComponentInitializer Create<T>(EGID ID, Transform contextHolder,
IEntityFactory factory, bool searchImplementorsInChildren = false)
public static EntityComponentInitializer Create<T>(EGID ID, Transform contextHolder, IEntityFactory factory,
out T holder, bool searchImplementorsInChildren = false)
where T : MonoBehaviour, IEntityDescriptorHolder
{
var holder = contextHolder.GetComponentInChildren<T>(true);
holder = contextHolder.GetComponentInChildren<T>(true);
if (holder == null)
{
throw new Exception($"Could not find holder {typeof(T).Name} in {contextHolder.name}");
}
var implementors = searchImplementorsInChildren == false ? holder.GetComponents<IImplementor>() : holder.GetComponentsInChildren<IImplementor>(true) ;

return factory.BuildEntity(ID, holder.GetDescriptor(), implementors);
}

public static uint CreateAll<T>(uint startIndex, ExclusiveGroup group,
Transform contextHolder, IEntityFactory factory, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder
public static EntityComponentInitializer Create<T>(EGID ID, Transform contextHolder,
IEntityFactory factory, bool searchImplementorsInChildren = false)
where T : MonoBehaviour, IEntityDescriptorHolder
{
var holders = contextHolder.GetComponentsInChildren<T>(true);

foreach (var holder in holders)
var holder = contextHolder.GetComponentInChildren<T>(true);
if (holder == null)
{
var implementors = holder.GetComponents<IImplementor>();

startIndex = InternalBuildAll(startIndex, holder, factory, group, implementors, groupNamePostfix);
throw new Exception($"Could not find holder {typeof(T).Name} in {contextHolder.name}");
}
var implementors = searchImplementorsInChildren == false ? holder.GetComponents<IImplementor>() : holder.GetComponentsInChildren<IImplementor>(true) ;

return startIndex;
return factory.BuildEntity(ID, holder.GetDescriptor(), implementors);
}

static uint InternalBuildAll(uint startIndex, IEntityDescriptorHolder descriptorHolder,
IEntityFactory factory, ExclusiveGroup group, IImplementor[] implementors, string groupNamePostfix)
IEntityFactory factory, ExclusiveGroup group, IImplementor[] implementors, string groupNamePostfix)
{
ExclusiveGroupStruct realGroup = group;

@@ -121,6 +142,8 @@ namespace Svelto.ECS.Extensions.Unity

/// <summary>
/// Works like CreateAll but only builds entities with holders that have the same group specified
/// This is a very specific case, basically used only by ControlsScreenRowFactory. Not sure what the real
/// use case is.
/// </summary>
/// <param name="startId"></param>
/// <param name="group">The group to match</param>


Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs → Svelto.ECS/Extensions/Unity/UnityEntitiesSubmissionScheduler.cs View File

@@ -7,7 +7,7 @@ namespace Svelto.ECS.Schedulers.Unity
{
//The EntitySubmissionScheduler has been introduced to make the entity components submission logic platform independent
//You can customize the scheduler if you wish
public class UnityEntitiesSubmissionScheduler : IEntitiesSubmissionScheduler
public class UnityEntitiesSubmissionScheduler : EntitiesSubmissionScheduler
{
class Scheduler : MonoBehaviour
{
@@ -37,14 +37,14 @@ namespace Svelto.ECS.Schedulers.Unity
public System.Action onTick;
}

public UnityEntitiesSubmissionScheduler(string name = "ECSScheduler")
public UnityEntitiesSubmissionScheduler(string name)
{
_scheduler = new GameObject(name).AddComponent<Scheduler>();
GameObject.DontDestroyOnLoad(_scheduler.gameObject);
_scheduler.onTick = SubmitEntities;
}

public void Dispose()
public override void Dispose()
{
if (_scheduler != null && _scheduler.gameObject != null)
{
@@ -52,18 +52,18 @@ namespace Svelto.ECS.Schedulers.Unity
}
}

public override bool paused { get; set; }

void SubmitEntities()
{
if (paused == false)
_onTick.Invoke();
}
EnginesRoot.EntitiesSubmitter IEntitiesSubmissionScheduler.onTick
protected internal override EnginesRoot.EntitiesSubmitter onTick
{
set => _onTick = value;
}
public bool paused { get; set; }

readonly Scheduler _scheduler;
EnginesRoot.EntitiesSubmitter _onTick;

+ 0
- 27
Svelto.ECS/FastGroup.cs View File

@@ -1,27 +0,0 @@
#if later
namespace Svelto.ECS
{
public class FastGroup
{
internal static uint entitiesCount;
public FastGroup()
{
_group = ExclusiveGroupStruct.Generate(1);
}

public static implicit operator ExclusiveGroupStruct(FastGroup group)
{
return group._group;
}

public static explicit operator uint(FastGroup group)
{
return group._group;
}

readonly ExclusiveGroupStruct _group;
public uint value => _group;
}
}
#endif

+ 213
- 0
Svelto.ECS/Filters/EntitiesDB.GroupFilters.cs View File

@@ -0,0 +1,213 @@
using System;
using Svelto.DataStructures;

namespace Svelto.ECS
{
/// <summary>
/// This feature must be eventually tied to the new ExclusiveGroup that won't allow the use of custom EntitiesID
/// The filters could be updated when entities buffer changes during the submission, while now this process
/// is completely manual.
/// Making this working properly is not in my priorities right now, as I will need to add the new group type
/// AND optimize the submission process to be able to add more overhead
/// </summary>
public partial class EntitiesDB
{
public readonly struct Filters
{
public Filters
(FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>> filters)
{
_filters = filters;
}

public ref FilterGroup CreateOrGetFilterForGroup<T>(int filterID, ExclusiveGroupStruct groupID)
where T : struct, IEntityComponent
{
var refWrapper = TypeRefWrapper<T>.wrapper;
return ref CreateOrGetFilterForGroup(filterID, groupID, refWrapper);
}

ref FilterGroup CreateOrGetFilterForGroup(int filterID, ExclusiveGroupStruct groupID, RefWrapperType refWrapper)
{
var fasterDictionary =
_filters.GetOrCreate(refWrapper, () => new FasterDictionary<ExclusiveGroupStruct, GroupFilters>());

GroupFilters filters =
fasterDictionary.GetOrCreate(
groupID, () => new GroupFilters(new SharedSveltoDictionaryNative<int, FilterGroup>(0), groupID));

return ref filters.CreateOrGetFilter(filterID);
}

public bool HasFiltersForGroup<T>(ExclusiveGroupStruct groupID) where T : struct, IEntityComponent
{
if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == false)
return false;
return fasterDictionary.ContainsKey(groupID);
}

public bool HasFilterForGroup<T>(int filterID, ExclusiveGroupStruct groupID)
where T : struct, IEntityComponent
{
if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == false)
return false;

if (fasterDictionary.TryGetValue(groupID, out var result))
return result.HasFilter(filterID);

return false;
}
public ref GroupFilters CreateOrGetFiltersForGroup<T>(ExclusiveGroupStruct groupID)
where T : struct, IEntityComponent
{
var fasterDictionary =
_filters.GetOrCreate(TypeRefWrapper<T>.wrapper, () => new FasterDictionary<ExclusiveGroupStruct, GroupFilters>());

return ref
fasterDictionary.GetOrCreate(
groupID, () => new GroupFilters(new SharedSveltoDictionaryNative<int, FilterGroup>(0), groupID));
}

public ref GroupFilters GetFiltersForGroup<T>(ExclusiveGroupStruct groupID)
where T : struct, IEntityComponent
{
#if DEBUG && !PROFILE_SVELTO
if (_filters.ContainsKey(TypeRefWrapper<T>.wrapper) == false)
throw new ECSException(
$"trying to fetch not existing filters, type {typeof(T)}");
if (_filters[TypeRefWrapper<T>.wrapper].ContainsKey(groupID) == false)
throw new ECSException(
$"trying to fetch not existing filters, type {typeof(T)} group {groupID.ToName()}");
#endif

return ref _filters[TypeRefWrapper<T>.wrapper].GetValueByRef(groupID);
}

public ref FilterGroup GetFilterForGroup<T>(int filterId, ExclusiveGroupStruct groupID)
where T : struct, IEntityComponent
{
#if DEBUG && !PROFILE_SVELTO
if (_filters.ContainsKey(TypeRefWrapper<T>.wrapper) == false)
throw new ECSException(
$"trying to fetch not existing filters, type {typeof(T)}");
if (_filters[TypeRefWrapper<T>.wrapper].ContainsKey(groupID) == false)
throw new ECSException(
$"trying to fetch not existing filters, type {typeof(T)} group {groupID.ToName()}");
#endif
return ref _filters[TypeRefWrapper<T>.wrapper][groupID].GetFilter(filterId);
}

public bool TryGetFilterForGroup<T>(int filterId, ExclusiveGroupStruct groupID, out FilterGroup groupFilter)
where T : struct, IEntityComponent
{
groupFilter = default;

if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == false)
return false;

if (fasterDictionary.TryGetValue(groupID, out var groupFilters) == false)
return false;

if (groupFilters.TryGetFilter(filterId, out groupFilter) == false)
return false;

return true;
}

public bool TryGetFiltersForGroup<T>(ExclusiveGroupStruct groupID, out GroupFilters groupFilters)
where T : struct, IEntityComponent
{
groupFilters = default;

if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == false)
return false;

return fasterDictionary.TryGetValue(groupID, out groupFilters);
}

public void ClearFilter<T>(int filterID, ExclusiveGroupStruct exclusiveGroupStruct)
{
if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == true)
{
DBC.ECS.Check.Require(fasterDictionary.ContainsKey(exclusiveGroupStruct), $"trying to clear filter not present in group {exclusiveGroupStruct}");
fasterDictionary[exclusiveGroupStruct].ClearFilter(filterID);
}
}

public void ClearFilters<T>(int filterID)
{
if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == true)
{
foreach (var filtersPerGroup in fasterDictionary)
filtersPerGroup.Value.ClearFilter(filterID);
}
}

public void DisposeFilters<T>(ExclusiveGroupStruct exclusiveGroupStruct)
{
if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == true)
{
fasterDictionary[exclusiveGroupStruct].DisposeFilters();
fasterDictionary.Remove(exclusiveGroupStruct);
}
}

public void DisposeFilters<T>()
{
if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == true)
{
foreach (var filtersPerGroup in fasterDictionary)
filtersPerGroup.Value.DisposeFilters();
}

_filters.Remove(TypeRefWrapper<T>.wrapper);
}

public void DisposeFilterForGroup<T>(int resetFilterID, ExclusiveGroupStruct @group)
{
if (_filters.TryGetValue(TypeRefWrapper<T>.wrapper, out var fasterDictionary) == true)
{
fasterDictionary[group].DisposeFilter(resetFilterID);
}
}

public bool TryRemoveEntityFromFilter<T>(int filtersID, EGID egid) where T : unmanaged, IEntityComponent
{
if (TryGetFilterForGroup<T>(filtersID, egid.groupID, out var filter))
{
return filter.TryRemove(egid.entityID);
}

return false;
}

public void RemoveEntityFromFilter<T>(int filtersID, EGID egid) where T : unmanaged, IEntityComponent
{
ref var filter = ref GetFilterForGroup<T>(filtersID, egid.groupID);

filter.Remove(egid.entityID);
}

public void AddEntityToFilter<N>(int filtersID, EGID egid, N mapper) where N:IEGIDMapper
{
ref var filter = ref CreateOrGetFilterForGroup(filtersID, egid.groupID, new RefWrapperType(mapper.entityType));

filter.Add(egid.entityID, mapper);
}

readonly FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>> _filters;
}

public Filters GetFilters()
{
return new Filters(_filters);
}

FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>> _filters
=> _enginesRoot._groupFilters;
}
}

+ 194
- 0
Svelto.ECS/Filters/FilterGroup.cs View File

@@ -0,0 +1,194 @@
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.DataStructures;

namespace Svelto.ECS
{
/// <summary>
/// In order to complete this feature, I need to be able to detect if the entity pointed by the filter
/// is still present.
/// This feature should work only with groups where entityID cannot be chosen by the user, so that
/// a real sparse set can be used like explained at: https://skypjack.github.io/2020-08-02-ecs-baf-part-9/
/// For a sparse set to work, the index in the sparse list must coincide with the ID of the entity
/// so that from the dense list (that holds unordered entity index), I can get to the sparse list index
/// sparse[0] = position in the dense list of the entity 0
/// dense[index] = entity ID but also index in the sparse list of the same entity ID
/// </summary>
public struct FilterGroup
{
internal FilterGroup(ExclusiveGroupStruct exclusiveGroupStruct, int ID)
{
_denseListOfIndicesToEntityComponentArray =
new NativeDynamicArrayCast<uint>(NativeDynamicArray.Alloc<uint>(Allocator.Persistent));
//from the index, find the entityID
_reverseEIDs = new NativeDynamicArrayCast<uint>(NativeDynamicArray.Alloc<uint>(Allocator.Persistent));
//from the entityID, find the index
_indexOfEntityInDenseList = new SharedSveltoDictionaryNative<uint, uint>(0, Allocator.Persistent);
_exclusiveGroupStruct = exclusiveGroupStruct;
_ID = ID;
}

/// <summary>
/// Todo: how to detect if the indices are still pointing to valid entities?
/// </summary>
public FilteredIndices filteredIndices => new FilteredIndices(_denseListOfIndicesToEntityComponentArray);

public void Add<N>(uint entityID, N mapper) where N:IEGIDMapper
{
#if DEBUG && !PROFILE_SVELTO
if (_denseListOfIndicesToEntityComponentArray.isValid == false)
throw new ECSException($"using an invalid filter");
if (_indexOfEntityInDenseList.ContainsKey(entityID) == true)
throw new ECSException(
$"trying to add an existing entity {entityID} to filter {mapper.entityType} - {_ID} with group {mapper.groupID}");
if (mapper.Exists(entityID) == false)
throw new ECSException(
$"trying adding an entity {entityID} to filter {mapper.entityType} - {_ID} with group {mapper.groupID}, but entity is not found! ");
#endif
//Get the index of the Entity in the component array
var indexOfEntityInBufferComponent = mapper.GetIndex(entityID);

//add the index in the list of filtered indices
_denseListOfIndicesToEntityComponentArray.Add(indexOfEntityInBufferComponent);

//inverse map: need to get the entityID from the index. This wouldn't be needed with a real sparseset
var lastIndex = (uint) (_denseListOfIndicesToEntityComponentArray.Count() - 1);
_reverseEIDs.AddAt(lastIndex) = entityID;

//remember the entities indices. This is needed to remove entities from the filter
_indexOfEntityInDenseList.Add(entityID, lastIndex);
}

public void Remove(uint entityID)
{
#if DEBUG && !PROFILE_SVELTO
if (_denseListOfIndicesToEntityComponentArray.isValid == false)
throw new ECSException($"invalid Filter");
if (_indexOfEntityInDenseList.ContainsKey(entityID) == false)
throw new ECSException(
$"trying to remove a not existing entity {new EGID(entityID, _exclusiveGroupStruct)} from filter");
#endif
InternalRemove(entityID);
}

public bool TryRemove(uint entityID)
{
#if DEBUG && !PROFILE_SVELTO
if (_denseListOfIndicesToEntityComponentArray.isValid == false)
throw new ECSException($"invalid Filter");
#endif
if (_indexOfEntityInDenseList.ContainsKey(entityID) == false)
return false;

InternalRemove(entityID);

return true;
}

/// <summary>
/// Filters were initially designed to be used for tagging operations within submissions of entities.
/// They were designed as a fast tagging mechanism to be used within the submission frame. However I then
/// extended it, but the extension includes a drawback:
///If filters are not in sync with the operations of remove and swap, filters may end up pointing to
///invalid indices. I need to put in place a way to be able to recognised an invalid filter.
///This is currently a disadvantage of the filters. The filters are not updated by the framework
///but they must be updated by the user.
///When to use this method: Add and Removed should be used to add and remove entities in the filters. This is
/// valid as long as no structural changes happen in the group of entities involved.
/// IF structural changes happen, the indices stored in the filters won't be valid anymore as they will possibly
/// point to entities that were not the original ones. On structural changes
/// (specifically entities swapped or removed)
/// the filters must then be rebuilt. It would be too slow to add this in the standard flow of Svelto in
/// the current state, so calling this method is a user responsibility.
/// </summary>
public void RebuildIndicesOnStructuralChange<N>(N mapper) where N:IEGIDMapper
{
#if DEBUG && !PROFILE_SVELTO
if (_denseListOfIndicesToEntityComponentArray.isValid == false)
throw new ECSException($"invalid Filter");
#endif
_denseListOfIndicesToEntityComponentArray.Clear();
_reverseEIDs.Clear();

foreach (var value in _indexOfEntityInDenseList)
if (mapper.FindIndex(value.Key, out var indexOfEntityInBufferComponent) == true)
{
_denseListOfIndicesToEntityComponentArray.Add(indexOfEntityInBufferComponent);
var lastIndex = (uint) (_denseListOfIndicesToEntityComponentArray.Count() - 1);
_reverseEIDs.AddAt(lastIndex) = value.Key;
}

_indexOfEntityInDenseList.Clear();

for (uint i = 0; i < _reverseEIDs.Count(); i++)
_indexOfEntityInDenseList[_reverseEIDs[i]] = i;
}

public void Clear()
{
#if DEBUG && !PROFILE_SVELTO
if (_denseListOfIndicesToEntityComponentArray.isValid == false)
throw new ECSException($"invalid Filter");
#endif
_indexOfEntityInDenseList.FastClear();
_reverseEIDs.Clear();
_denseListOfIndicesToEntityComponentArray.Clear();
}

internal void Dispose()
{
#if DEBUG && !PROFILE_SVELTO
if (_denseListOfIndicesToEntityComponentArray.isValid == false)
throw new ECSException($"invalid Filter");
#endif
_denseListOfIndicesToEntityComponentArray.Dispose();
_indexOfEntityInDenseList.Dispose();
_reverseEIDs.Dispose();
}

void InternalRemove(uint entityID)
{
var count = (uint) _denseListOfIndicesToEntityComponentArray.Count();
if (count > 0)
{
if (count > 1)
{
//get the index in the filter array of the entity to delete
var indexInDenseListFromEGID = _indexOfEntityInDenseList[entityID];
//get the entityID of the last entity in the filter array
uint entityIDToMove = _reverseEIDs[count - 1];
//the last index of the last entity is updated to the slot of the deleted entity
if (entityIDToMove != entityID)
{
_indexOfEntityInDenseList[entityIDToMove] = indexInDenseListFromEGID;
//the reverseEGID is updated accordingly
_reverseEIDs[indexInDenseListFromEGID] = entityIDToMove;
}
//
_reverseEIDs.UnorderedRemoveAt(count - 1);

//finally remove the deleted entity from the filters array
_denseListOfIndicesToEntityComponentArray.UnorderedRemoveAt(indexInDenseListFromEGID);
//remove the entity to delete from the tracked Entity
_indexOfEntityInDenseList.Remove(entityID);
}
else
{
_indexOfEntityInDenseList.FastClear();
_reverseEIDs.Clear();
_denseListOfIndicesToEntityComponentArray.Clear();
}
}
}

NativeDynamicArrayCast<uint> _denseListOfIndicesToEntityComponentArray;
NativeDynamicArrayCast<uint> _reverseEIDs; //forced to use this because it's not a real sparse set
SharedSveltoDictionaryNative<uint, uint> _indexOfEntityInDenseList;

readonly ExclusiveGroupStruct _exclusiveGroupStruct;
readonly int _ID;
}
}

+ 21
- 0
Svelto.ECS/Filters/FilteredIndices.cs View File

@@ -0,0 +1,21 @@
using System.Runtime.CompilerServices;
using Svelto.ECS.DataStructures;

namespace Svelto.ECS
{
public readonly struct FilteredIndices
{
public FilteredIndices(NativeDynamicArrayCast<uint> denseListOfIndicesToEntityComponentArray)
{
_denseListOfIndicesToEntityComponentArray = denseListOfIndicesToEntityComponentArray;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Count() => _denseListOfIndicesToEntityComponentArray.Count();

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint Get(uint index) => _denseListOfIndicesToEntityComponentArray[index];

readonly NativeDynamicArrayCast<uint> _denseListOfIndicesToEntityComponentArray;
}
}

+ 103
- 0
Svelto.ECS/Filters/GroupFilters.cs View File

@@ -0,0 +1,103 @@
using Svelto.DataStructures;

namespace Svelto.ECS
{
public struct GroupFilters
{
internal GroupFilters(SharedSveltoDictionaryNative<int, FilterGroup> filters, ExclusiveGroupStruct group)
{
this.filters = filters;
_group = @group;
}

public ref FilterGroup GetFilter(int filterIndex)
{
#if DEBUG && !PROFILE_SVELTO
if (filters.isValid == false)
throw new ECSException($"trying to fetch not existing filters {filterIndex} group {_group.ToName()}");
if (filters.ContainsKey(filterIndex) == false)
throw new ECSException($"trying to fetch not existing filters {filterIndex} group {_group.ToName()}");
#endif
return ref filters.GetValueByRef(filterIndex);
}

public bool HasFilter(int filterIndex) { return filters.ContainsKey(filterIndex); }

public void ClearFilter(int filterIndex)
{
if (filters.TryFindIndex(filterIndex, out var index))
filters.GetValues(out _)[index].Clear();
}

public void ClearFilters()
{
foreach (var filter in filters)
filter.Value.Clear();
}

public bool TryGetFilter(int filterIndex, out FilterGroup filter)
{
return filters.TryGetValue(filterIndex, out filter);
}

public SveltoDictionary<int, FilterGroup, NativeStrategy<FasterDictionaryNode<int>>, NativeStrategy<FilterGroup>
, NativeStrategy<int>>.SveltoDictionaryKeyValueEnumerator GetEnumerator()
{
return filters.GetEnumerator();
}
//Note the following methods are internal because I was pondering the idea to be able to return
//the list of GroupFilters linked to a specific filter ID. However this would mean to be able to
//maintain a revers map which at this moment seems too much and also would need the following
//method to be for ever internal (at this point in time I am not sure it's a good idea)
internal void DisposeFilter(int filterIndex)
{
if (filters.TryFindIndex(filterIndex, out var index))
{
ref var filterGroup = ref filters.GetValues(out _)[index];
filterGroup.Dispose();

filters.Remove(filterIndex);
}
}

internal void DisposeFilters()
{
//must release the native buffers!
foreach (var filter in filters)
filter.Value.Dispose();

filters.FastClear();
}

internal ref FilterGroup CreateOrGetFilter(int filterID)
{
if (filters.TryFindIndex(filterID, out var index) == false)
{
var orGetFilterForGroup = new FilterGroup(_group, filterID);

filters[filterID] = orGetFilterForGroup;

return ref filters.GetValueByRef(filterID);
}

return ref filters.GetValues(out _)[index];
}

internal void Dispose()
{
foreach (var filter in filters)
{
filter.Value.Dispose();
}

filters.Dispose();
}

readonly ExclusiveGroupStruct _group;

//filterID, filter
SharedSveltoDictionaryNative<int, FilterGroup> filters;
}
}

+ 2
- 2
Svelto.ECS/GlobalTypeID.cs View File

@@ -23,7 +23,7 @@ namespace Svelto.ECS
{
static Filler()
{
DBC.ECS.Check.Require(UnmanagedTypeExtensions.IsUnmanagedEx<T>() == true, "invalid type used");
DBC.ECS.Check.Require(TypeCache<T>.IsUnmanaged == true, "invalid type used");
}

//it's an internal interface
@@ -37,7 +37,7 @@ namespace Svelto.ECS

static class EntityComponentID<T>
{
#if UNITY_BURST
#if UNITY_NATIVE
internal static readonly Unity.Burst.SharedStatic<uint> ID =
Unity.Burst.SharedStatic<uint>.GetOrCreate<GlobalTypeID, T>();
#else


+ 219
- 120
Svelto.ECS/GroupCompound.cs View File

@@ -1,126 +1,225 @@
using System;
using System.Threading;
using Svelto.DataStructures;

namespace Svelto.ECS
{
public abstract class GroupCompound<G1, G2, G3>
where G1 : GroupTag<G1> where G2 : GroupTag<G2> where G3 : GroupTag<G3>
/// <summary>
/// Very naive fail safe, but at least it's simple to understand and safe
/// </summary>
static class GroupCompoundInitializer
{
static readonly FasterList<ExclusiveGroupStruct> _Groups;
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);

static GroupCompound()
{
if ((_Groups = GroupCompound<G3, G1, G2>._Groups) == null)
if ((_Groups = GroupCompound<G2, G3, G1>._Groups) == null)
if ((_Groups = GroupCompound<G3, G2, G1>._Groups) == null)
if ((_Groups = GroupCompound<G1, G3, G2>._Groups) == null)
if ((_Groups = GroupCompound<G2, G1, G3>._Groups) == null)
{
_Groups = new FasterList<ExclusiveGroupStruct>(1);

var Group = new ExclusiveGroup();
_Groups.Add(Group);
GroupCompound<G1, G2>.Add(Group); //<G1/G2> and <G2/G1> must share the same array
GroupCompound<G1, G3>.Add(Group);
GroupCompound<G2, G3>.Add(Group);
//This is done here to be sure that the group is added once per group tag
//(if done inside the previous group compound it would be added multiple times)
GroupTag<G1>.Add(Group);
GroupTag<G2>.Add(Group);
GroupTag<G3>.Add(Group);
#if DEBUG
GroupMap.idToName[(uint) Group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name}";
#endif
}
}
public static void Add(ExclusiveGroupStruct @group)
{
for (int i = 0; i < _Groups.count; ++i)
if (_Groups[i] == group)
throw new Exception("temporary must be transformed in unit test");
_Groups.Add(group);
// GroupCompound<G1, G2, G3>._Groups = _Groups;
}

public static ExclusiveGroupStruct BuildGroup => new ExclusiveGroupStruct(_Groups[0]);
}

public abstract class GroupCompound<G1, G2> where G1 : GroupTag<G1> where G2 : GroupTag<G2>
{
static FasterList<ExclusiveGroupStruct> _Groups;
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);

static GroupCompound()
{
_Groups = GroupCompound<G2, G1>._Groups;
if (_Groups == null)
{
_Groups = new FasterList<ExclusiveGroupStruct>(1);
var Group = new ExclusiveGroup();
_Groups.Add(Group);
//every abstract group preemptively adds this group, it may or may not be empty in future
GroupTag<G1>.Add(Group);
GroupTag<G2>.Add(Group);
#if DEBUG
GroupMap.idToName[(uint) Group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}";
#endif
}
}

public static ExclusiveGroupStruct BuildGroup => new ExclusiveGroupStruct(_Groups[0]);

public static void Add(ExclusiveGroupStruct @group)
{
for (int i = 0; i < _Groups.count; ++i)
if (_Groups[i] == group)
throw new Exception("temporary must be transformed in unit test");
_Groups.Add(group);
//unit test this to check if it's necessary
// GroupCompound<G2, G1>._Groups = _Groups;
}
}

//A Group Tag holds initially just a group, itself. However the number of groups can grow with the number of
//combinations of GroupTags including this one. This because a GroupTag is an adjective and different entities
//can use the same adjective together with other ones. However since I need to be able to iterate over all the
//groups with the same adjective, a group tag needs to hold all the groups sharing it.
public abstract class GroupTag<T> where T : GroupTag<T>
{
static FasterList<ExclusiveGroupStruct> _Groups = new FasterList<ExclusiveGroupStruct>(1);
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);

static GroupTag()
{
_Groups.Add(new ExclusiveGroup());
}

//Each time a new combination of group tags is found a new group is added.
internal static void Add(ExclusiveGroupStruct @group)
{
for (int i = 0; i < _Groups.count; ++i)
if (_Groups[i] == group)
throw new Exception("temporary must be transformed in unit test");

_Groups.Add(group);
#if DEBUG
GroupMap.idToName[(uint) group] = $"Compound: {typeof(T).Name}";
#endif
}

public static ExclusiveGroupStruct BuildGroup => new ExclusiveGroupStruct(_Groups[0]);
}
}
internal static readonly ThreadLocal<bool> isInitializing4 = new ThreadLocal<bool>();
internal static readonly ThreadLocal<bool> isInitializing3 = new ThreadLocal<bool>();
internal static readonly ThreadLocal<bool> isInitializing2 = new ThreadLocal<bool>();
}
public abstract class GroupCompound<G1, G2, G3, G4>
where G1 : GroupTag<G1> where G2 : GroupTag<G2> where G3 : GroupTag<G3> where G4 : GroupTag<G4>
{
static readonly FasterList<ExclusiveGroupStruct> _Groups;
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
public static BuildGroup BuildGroup => new BuildGroup(_Groups[0]);
static GroupCompound()
{
if (GroupCompoundInitializer.isInitializing4.Value == false)
{
_Groups = new FasterList<ExclusiveGroupStruct>(1);
var Group = new ExclusiveGroup();
_Groups.Add(Group);
GroupCompound<G1, G2, G3>.Add(Group);
GroupCompound<G1, G2, G4>.Add(Group);
GroupCompound<G1, G3, G4>.Add(Group);
GroupCompound<G2, G3, G4>.Add(Group);
GroupCompound<G1, G2>.Add(Group); //<G1/G2> and <G2/G1> must share the same array
GroupCompound<G1, G3>.Add(Group);
GroupCompound<G1, G4>.Add(Group);
GroupCompound<G2, G3>.Add(Group);
GroupCompound<G2, G4>.Add(Group);
GroupCompound<G3, G4>.Add(Group);
//This is done here to be sure that the group is added once per group tag
//(if done inside the previous group compound it would be added multiple times)
GroupTag<G1>.Add(Group);
GroupTag<G2>.Add(Group);
GroupTag<G3>.Add(Group);
GroupTag<G4>.Add(Group);
#if DEBUG
GroupMap.idToName[(uint) Group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name}-{typeof(G4).Name} ID {(uint)Group}";
#endif
GroupCompoundInitializer.isInitializing4.Value = true;
//all the combinations must share the same group
GroupCompound<G1, G2, G4, G3>._Groups = _Groups;
GroupCompound<G1, G3, G2, G4>._Groups = _Groups;
GroupCompound<G1, G3, G4, G2>._Groups = _Groups;
GroupCompound<G1, G4, G2, G3>._Groups = _Groups;
GroupCompound<G2, G1, G3, G4>._Groups = _Groups;
GroupCompound<G2, G3, G4, G1>._Groups = _Groups;
GroupCompound<G3, G1, G2, G4>._Groups = _Groups;
GroupCompound<G4, G1, G2, G3>._Groups = _Groups;
GroupCompound<G1, G4, G3, G2>._Groups = _Groups;
GroupCompound<G2, G1, G4, G3>._Groups = _Groups;
GroupCompound<G2, G4, G3, G1>._Groups = _Groups;
GroupCompound<G3, G1, G4, G2>._Groups = _Groups;
GroupCompound<G4, G1, G3, G2>._Groups = _Groups;
GroupCompound<G2, G3, G1, G4>._Groups = _Groups;
GroupCompound<G3, G4, G1, G2>._Groups = _Groups;
GroupCompound<G2, G4, G1, G3>._Groups = _Groups;
GroupCompound<G3, G2, G1, G4>._Groups = _Groups;
GroupCompound<G3, G2, G4, G1>._Groups = _Groups;
GroupCompound<G3, G4, G2, G1>._Groups = _Groups;
GroupCompound<G4, G2, G1, G3>._Groups = _Groups;
GroupCompound<G4, G2, G3, G1>._Groups = _Groups;
GroupCompound<G4, G3, G1, G2>._Groups = _Groups;
GroupCompound<G4, G3, G2, G1>._Groups = _Groups;
GroupCompoundInitializer.isInitializing4.Value = false;
}
}
public static void Add(ExclusiveGroupStruct @group)
{
for (int i = 0; i < _Groups.count; ++i)
if (_Groups[i] == group)
throw new Exception("temporary must be transformed in unit test");
_Groups.Add(group);
}
}
public abstract class GroupCompound<G1, G2, G3>
where G1 : GroupTag<G1> where G2 : GroupTag<G2> where G3 : GroupTag<G3>
{
static readonly FasterList<ExclusiveGroupStruct> _Groups;
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
public static BuildGroup BuildGroup => new BuildGroup(_Groups[0]);
public static void Add(ExclusiveGroupStruct group)
{
for (var i = 0; i < _Groups.count; ++i)
if (_Groups[i] == group)
throw new Exception("temporary must be transformed in unit test");
_Groups.Add(group);
}
static GroupCompound()
{
if (GroupCompoundInitializer.isInitializing3.Value == false)
{
_Groups = new FasterList<ExclusiveGroupStruct>(1);
var Group = new ExclusiveGroup();
_Groups.Add(Group);
GroupCompound<G1, G2>.Add(Group); //<G1/G2> and <G2/G1> must share the same array
GroupCompound<G1, G3>.Add(Group);
GroupCompound<G2, G3>.Add(Group);
//This is done here to be sure that the group is added once per group tag
//(if done inside the previous group compound it would be added multiple times)
GroupTag<G1>.Add(Group);
GroupTag<G2>.Add(Group);
GroupTag<G3>.Add(Group);
#if DEBUG
GroupMap.idToName[(uint) Group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name} ID {(uint)Group}";
#endif
//all the combinations must share the same group
GroupCompoundInitializer.isInitializing3.Value = true;
GroupCompound<G3, G1, G2>._Groups = _Groups;
GroupCompound<G2, G3, G1>._Groups = _Groups;
GroupCompound<G3, G2, G1>._Groups = _Groups;
GroupCompound<G1, G3, G2>._Groups = _Groups;
GroupCompound<G2, G1, G3>._Groups = _Groups;
GroupCompoundInitializer.isInitializing3.Value = false;
}
}
}
public abstract class GroupCompound<G1, G2> where G1 : GroupTag<G1> where G2 : GroupTag<G2>
{
static readonly FasterList<ExclusiveGroupStruct> _Groups;
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
public static BuildGroup BuildGroup => new BuildGroup(_Groups[0]);
public static void Add(ExclusiveGroupStruct group)
{
for (var i = 0; i < _Groups.count; ++i)
if (_Groups[i] == group)
throw new Exception("temporary must be transformed in unit test");
_Groups.Add(group);
}
static GroupCompound()
{
if (GroupCompoundInitializer.isInitializing2.Value == false)
{
var Group = new ExclusiveGroup();
_Groups = new FasterList<ExclusiveGroupStruct>(1);
_Groups.Add(Group);
//every abstract group preemptively adds this group, it may or may not be empty in future
GroupTag<G1>.Add(Group);
GroupTag<G2>.Add(Group);
#if DEBUG
GroupMap.idToName[(uint) Group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name} ID {(uint)Group}";
#endif
GroupCompoundInitializer.isInitializing2.Value = true;
GroupCompound<G2, G1>._Groups = _Groups;
GroupCompoundInitializer.isInitializing2.Value = false;
}
}
}
/// <summary>
///A Group Tag holds initially just a group, itself. However the number of groups can grow with the number of
///combinations of GroupTags including this one. This because a GroupTag is an adjective and different entities
///can use the same adjective together with other ones. However since I need to be able to iterate over all the
///groups with the same adjective, a group tag needs to hold all the groups sharing it.
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class GroupTag<T> where T : GroupTag<T>
{
static readonly FasterList<ExclusiveGroupStruct> _Groups = new FasterList<ExclusiveGroupStruct>(1);
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
public static BuildGroup BuildGroup => new BuildGroup(_Groups[0]);
static GroupTag()
{
var group = new ExclusiveGroup();
_Groups.Add(group);
#if DEBUG
GroupMap.idToName[(uint) group] = $"Compound: {typeof(T).Name} ID {(uint)group}";
#endif
}
//Each time a new combination of group tags is found a new group is added.
internal static void Add(ExclusiveGroupStruct group)
{
for (var i = 0; i < _Groups.count; ++i)
if (_Groups[i] == group)
throw new Exception("temporary must be transformed in unit test");
_Groups.Add(group);
}
}
}

+ 4
- 1
Svelto.ECS/Hybrid/IEntityViewComponent.cs View File

@@ -1,6 +1,9 @@
namespace Svelto.ECS.Hybrid
{
public interface IEntityViewComponent:IEntityComponent, INeedEGID
public interface IManagedComponent:IEntityComponent
{}
public interface IEntityViewComponent:IManagedComponent, INeedEGID
{}
}


+ 9
- 0
Svelto.ECS/IDisposingEngine.cs View File

@@ -0,0 +1,9 @@
using System;

namespace Svelto.ECS
{
public interface IDisposingEngine: IDisposable
{
bool isDisposing { set; }
}
}

+ 18
- 0
Svelto.ECS/IEngine.cs View File

@@ -1,3 +1,5 @@
using Svelto.ECS.Internal;

namespace Svelto.ECS.Internal
{
public interface IReactEngine: IEngine
@@ -14,4 +16,20 @@ namespace Svelto.ECS
{
public interface IEngine
{}
public interface IReactOnAddAndRemove<T> : IReactOnAddAndRemove where T : IEntityComponent
{
void Add(ref T entityComponent, EGID egid);
void Remove(ref T entityComponent, EGID egid);
}
public interface IReactOnSwap<T> : IReactOnSwap where T : IEntityComponent
{
void MovedTo(ref T entityComponent, ExclusiveGroupStruct previousGroup, EGID egid);
}

public interface IReactOnSubmission:IReactEngine
{
void EntitiesSubmitted();
}
}

+ 0
- 9
Svelto.ECS/IEntitiesDB.cs View File

@@ -1,9 +0,0 @@
using Svelto.DataStructures;

namespace Svelto.ECS
{
public delegate void ExecuteOnAllEntitiesAction<T, W>(IBuffer<T> prefabStruct, ExclusiveGroupStruct group,
uint count, EntitiesDB db, ref W instances);
public delegate void ExecuteOnAllEntitiesAction<T>(IBuffer<T> entities, ExclusiveGroupStruct group,
uint count, EntitiesDB db);
}

+ 2
- 2
Svelto.ECS/IEntityComponent.cs View File

@@ -1,12 +1,12 @@
namespace Svelto.ECS
{
///<summary>EntityComponent MUST implement IEntityComponent</summary>
///<summary>Entity Components MUST implement IEntityComponent</summary>
public interface IEntityComponent
{
}

/// <summary>
/// use INeedEGID on an IEntityComponent only if you need the EGID
/// use INeedEGID on an IEntityComponent only if you need the EGID. consider using EGIDComponent instead
/// </summary>
public interface INeedEGID
{


+ 3
- 11
Svelto.ECS/IEntityFactory.cs View File

@@ -43,22 +43,14 @@ namespace Svelto.ECS
/// <param name="groupStructId"></param>
/// <param name="ed"></param>
/// <param name="implementors"></param>
EntityComponentInitializer BuildEntity<T>(uint entityID, ExclusiveGroupStruct groupStructId,
EntityComponentInitializer BuildEntity<T>(uint entityID, BuildGroup groupStructId,
IEnumerable<object> implementors = null)
where T : IEntityDescriptor, new();

EntityComponentInitializer BuildEntity<T>(EGID egid, IEnumerable<object> implementors = null)
where T : IEntityDescriptor, new();

/// <summary>
/// When the type of the entity is not known (this is a special case!) an EntityDescriptorInfo
/// can be built in place of the generic parameter T.
/// </summary>
/// <param name="entityID"></param>
/// <param name="entityDescriptor"></param>
/// <param name="implementors"></param>
///
EntityComponentInitializer BuildEntity<T>(uint entityID, ExclusiveGroupStruct groupStructId,
EntityComponentInitializer BuildEntity<T>(uint entityID, BuildGroup groupStructId,
T descriptorEntity, IEnumerable<object> implementors = null)
where T : IEntityDescriptor;

@@ -68,7 +60,7 @@ namespace Svelto.ECS
EntityComponentInitializer BuildEntity
(EGID egid, IComponentBuilder[] componentsToBuild, Type type, IEnumerable<object> implementors = null);

#if UNITY_BURST
#if UNITY_NATIVE
NativeEntityFactory ToNative<T>(string memberName) where T : IEntityDescriptor, new();
#endif
}

+ 8
- 11
Svelto.ECS/IEntityFunctions.cs View File

@@ -5,29 +5,26 @@ namespace Svelto.ECS
//being entity ID globally not unique, the group must be specified when
//an entity is removed. Not specifying the group will attempt to remove
//the entity from the special standard group.
void RemoveEntity<T>(uint entityID, ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new();
void RemoveEntity<T>(uint entityID, BuildGroup groupID) where T : IEntityDescriptor, new();
void RemoveEntity<T>(EGID entityegid) where T : IEntityDescriptor, new();
void RemoveAllEntities<T>(ExclusiveGroupStruct group) where T : IEntityDescriptor, new();
void RemoveAllEntities<T>() where T : IEntityDescriptor, new();
void RemoveEntitiesFromGroup(BuildGroup groupID);

void RemoveGroupAndEntities(ExclusiveGroupStruct groupID);
void SwapEntitiesInGroup<T>(BuildGroup fromGroupID, BuildGroup toGroupID) where T : IEntityDescriptor, new();

void SwapEntitiesInGroup<T>(ExclusiveGroupStruct fromGroupID, ExclusiveGroupStruct toGroupID);

void SwapEntityGroup<T>(uint entityID, ExclusiveGroupStruct fromGroupID, ExclusiveGroupStruct toGroupID)
void SwapEntityGroup<T>(uint entityID, BuildGroup fromGroupID, BuildGroup toGroupID)
where T : IEntityDescriptor, new();

void SwapEntityGroup<T>(EGID fromID, ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new();
void SwapEntityGroup<T>(EGID fromID, BuildGroup toGroupID) where T : IEntityDescriptor, new();

void SwapEntityGroup<T>(EGID fromID, ExclusiveGroupStruct toGroupID, ExclusiveGroupStruct mustBeFromGroup)
void SwapEntityGroup<T>(EGID fromID, BuildGroup toGroupID, BuildGroup mustBeFromGroup)
where T : IEntityDescriptor, new();

void SwapEntityGroup<T>(EGID fromID, EGID toId) where T : IEntityDescriptor, new();

void SwapEntityGroup<T>(EGID fromID, EGID toId, ExclusiveGroupStruct mustBeFromGroup)
void SwapEntityGroup<T>(EGID fromID, EGID toId, BuildGroup mustBeFromGroup)
where T : IEntityDescriptor, new();
#if UNITY_BURST
#if UNITY_NATIVE
NativeEntityRemove ToNativeRemove<T>(string memberName) where T : IEntityDescriptor, new();
NativeEntitySwap ToNativeSwap<T>(string memberName) where T : IEntityDescriptor, new();
#endif


+ 0
- 10
Svelto.ECS/IReactOnAddAndRemove.cs View File

@@ -1,10 +0,0 @@
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public interface IReactOnAddAndRemove<T> : IReactOnAddAndRemove where T : IEntityComponent
{
void Add(ref T entityComponent, EGID egid);
void Remove(ref T entityComponent, EGID egid);
}
}

+ 0
- 9
Svelto.ECS/IReactOnSwap.cs View File

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

namespace Svelto.ECS
{
public interface IReactOnSwap<T> : IReactOnSwap where T : IEntityComponent
{
void MovedTo(ref T entityComponent, ExclusiveGroupStruct previousGroup, EGID egid);
}
}

+ 1
- 1
Svelto.ECS/NamedExclusiveGroup.cs View File

@@ -14,7 +14,7 @@ namespace Svelto.ECS
static NamedExclusiveGroup()
{
#if DEBUG
GroupMap.idToName[(uint) Group] = name;
GroupMap.idToName[(uint) Group] = $"{name} ID {(uint)Group}";
#endif
}
// protected NamedExclusiveGroup(string recognizeAs) : base(recognizeAs) {}


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

Loading…
Cancel
Save