Browse Source

Update Svelto.ECS 3.0

pull/49/head
sebas77 4 years ago
parent
commit
df8e6dd21c
100 changed files with 3791 additions and 2309 deletions
  1. +1
    -1
      Svelto.Common
  2. +0
    -3
      Svelto.ECS/.github/FUNDING.yml
  3. +0
    -3
      Svelto.ECS/.gitignore
  4. +0
    -3
      Svelto.ECS/.gitmodules
  5. +23
    -29
      Svelto.ECS/CheckEntityUtilities.cs
  6. +5
    -5
      Svelto.ECS/ComponentBuilder.CheckFields.cs
  7. +28
    -18
      Svelto.ECS/ComponentBuilder.cs
  8. +8
    -8
      Svelto.ECS/DataStructures/AtomicNativeBags.cs
  9. +0
    -5
      Svelto.ECS/DataStructures/FastTypeSafeDictionary.cs
  10. +4
    -11
      Svelto.ECS/DataStructures/ITypeSafeDictionary.cs
  11. +9
    -29
      Svelto.ECS/DataStructures/NativeBag.cs
  12. +179
    -39
      Svelto.ECS/DataStructures/NativeDynamicArray.cs
  13. +23
    -0
      Svelto.ECS/DataStructures/NativeDynamicArrayUnityExtension.cs
  14. +1
    -1
      Svelto.ECS/DataStructures/SharedNativeInt.cs
  15. +0
    -70
      Svelto.ECS/DataStructures/SharedNativeUInt.cs
  16. +395
    -170
      Svelto.ECS/DataStructures/TypeSafeDictionary.cs
  17. +0
    -8
      Svelto.ECS/DataStructures/TypeSafeDictionaryUtilities.cs
  18. +31
    -16
      Svelto.ECS/DataStructures/UnsafeArray.cs
  19. +108
    -114
      Svelto.ECS/DataStructures/UnsafeBlob.cs
  20. +69
    -0
      Svelto.ECS/Debugger/ExclusiveGroupDebugger.cs
  21. +6
    -6
      Svelto.ECS/DynamicEntityDescriptor.cs
  22. +4
    -0
      Svelto.ECS/ECSResources/ECSResources.cs
  23. +49
    -9
      Svelto.ECS/ECSResources/ECSString.cs
  24. +1
    -2
      Svelto.ECS/EGID.cs
  25. +24
    -23
      Svelto.ECS/EGIDMapper.cs
  26. +148
    -0
      Svelto.ECS/EnginesRoot.DoubleBufferedEntitiesToAdd.cs
  27. +0
    -93
      Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs
  28. +63
    -36
      Svelto.ECS/EnginesRoot.Engines.cs
  29. +152
    -129
      Svelto.ECS/EnginesRoot.Entities.cs
  30. +17
    -13
      Svelto.ECS/EnginesRoot.GenericEntityFactory.cs
  31. +17
    -12
      Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs
  32. +12
    -10
      Svelto.ECS/EnginesRoot.Submission.cs
  33. +126
    -0
      Svelto.ECS/EntitiesDB.FindGroups.cs
  34. +123
    -174
      Svelto.ECS/EntitiesDB.cs
  35. +144
    -336
      Svelto.ECS/EntityCollection.cs
  36. +266
    -0
      Svelto.ECS/EntityCollections.cs
  37. +3
    -1
      Svelto.ECS/EntityComponentInitializer.cs
  38. +6
    -6
      Svelto.ECS/EntityFactory.cs
  39. +11
    -0
      Svelto.ECS/EntityHierarchyStruct.cs
  40. +1
    -1
      Svelto.ECS/EntityInfoView.cs
  41. +1
    -1
      Svelto.ECS/EntityNotFoundException.cs
  42. +9
    -10
      Svelto.ECS/EntityStream.cs
  43. +2
    -0
      Svelto.ECS/EntitySubmissionScheduler.cs
  44. +25
    -21
      Svelto.ECS/EntityViewUtility.cs
  45. +1
    -17
      Svelto.ECS/ExclusiveGroup.cs
  46. +5
    -3
      Svelto.ECS/ExclusiveGroupStruct.cs
  47. +2
    -2
      Svelto.ECS/Extensions/ProcessorCount.cs
  48. +31
    -32
      Svelto.ECS/Extensions/Svelto/AllGroupsEnumerable.cs
  49. +162
    -0
      Svelto.ECS/Extensions/Svelto/EntityCollectionExtension.cs
  50. +72
    -64
      Svelto.ECS/Extensions/Svelto/EntityDBExtensions.cs
  51. +69
    -0
      Svelto.ECS/Extensions/Svelto/EntityDBExtensionsB.cs
  52. +249
    -0
      Svelto.ECS/Extensions/Svelto/GroupsEnumerable.cs
  53. +0
    -229
      Svelto.ECS/Extensions/Svelto/NativeGroupsEnumerable.cs
  54. +86
    -0
      Svelto.ECS/Extensions/Svelto/SortedEnginesGroup.cs
  55. +0
    -30
      Svelto.ECS/Extensions/Unity/DOTS/CopySveltoToUECSEnginesGroup.cs
  56. +35
    -116
      Svelto.ECS/Extensions/Unity/DOTS/EnginesRoot.NativeOperation.cs
  57. +0
    -9
      Svelto.ECS/Extensions/Unity/DOTS/IJobifiedEngine.cs
  58. +77
    -11
      Svelto.ECS/Extensions/Unity/DOTS/JobifedEnginesGroup.cs
  59. +1
    -1
      Svelto.ECS/Extensions/Unity/DOTS/JobifiedSveltoEngines.cs
  60. +83
    -0
      Svelto.ECS/Extensions/Unity/DOTS/NativeEGIDMapper.cs
  61. +43
    -0
      Svelto.ECS/Extensions/Unity/DOTS/NativeEGIDMultiMapper.cs
  62. +26
    -0
      Svelto.ECS/Extensions/Unity/DOTS/NativeEntityComponentInitializer.cs
  63. +42
    -0
      Svelto.ECS/Extensions/Unity/DOTS/NativeEntityFactory.cs
  64. +26
    -0
      Svelto.ECS/Extensions/Unity/DOTS/NativeEntityRemove.cs
  65. +33
    -0
      Svelto.ECS/Extensions/Unity/DOTS/NativeEntitySwap.cs
  66. +2
    -0
      Svelto.ECS/Extensions/Unity/DOTS/PureUECSSystemsGroup.cs
  67. +51
    -7
      Svelto.ECS/Extensions/Unity/DOTS/SortedJobifedEnginesGroup.cs
  68. +39
    -0
      Svelto.ECS/Extensions/Unity/DOTS/SyncSveltoToUECSGroup.cs
  69. +90
    -2
      Svelto.ECS/Extensions/Unity/DOTS/UnityEntityDBExtensions.cs
  70. +32
    -0
      Svelto.ECS/Extensions/Unity/EGIDHolderImplementor.cs
  71. +44
    -22
      Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs
  72. +22
    -18
      Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs
  73. +18
    -18
      Svelto.ECS/GenericEntityDescriptor.cs
  74. +2
    -2
      Svelto.ECS/GenericentityStreamConsumerFactory.cs
  75. +27
    -73
      Svelto.ECS/GlobalTypeID.cs
  76. +61
    -47
      Svelto.ECS/GroupCompound.cs
  77. +4
    -2
      Svelto.ECS/IEntitiesDB.cs
  78. +7
    -4
      Svelto.ECS/IEntityFactory.cs
  79. +3
    -3
      Svelto.ECS/IEntityFunctions.cs
  80. +0
    -3
      Svelto.ECS/IReactOnSwap.cs
  81. +23
    -0
      Svelto.ECS/NamedExclusiveGroup.cs
  82. +0
    -79
      Svelto.ECS/NativeEGIDMapper.cs
  83. +53
    -14
      Svelto.ECS/QueryGroups.cs
  84. +5
    -2
      Svelto.ECS/Serialization/DefaultSerializer.cs
  85. +12
    -15
      Svelto.ECS/Serialization/DefaultVersioningFactory.cs
  86. +19
    -22
      Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs
  87. +9
    -4
      Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs
  88. +1
    -1
      Svelto.ECS/Serialization/HashNameAttribute.cs
  89. +4
    -3
      Svelto.ECS/Serialization/IDeserializationFactory.cs
  90. +3
    -0
      Svelto.ECS/Serialization/ISerializableEntityDescriptor.cs
  91. +5
    -3
      Svelto.ECS/Serialization/PartialSerializer.cs
  92. +3
    -5
      Svelto.ECS/Serialization/SerializableComponentBuilder.cs
  93. +19
    -18
      Svelto.ECS/Serialization/SerializableEntityDescriptor.cs
  94. +2
    -4
      Svelto.ECS/Serialization/SerializerExt.cs
  95. +13
    -4
      Svelto.ECS/SetEGIDWithoutBoxing.cs
  96. +4
    -1
      Svelto.ECS/SimpleEntitiesSubmissionScheduler.cs
  97. +16
    -0
      Svelto.ECS/Svelto.ECS.asmdef
  98. +1
    -1
      Svelto.ECS/Svelto.ECS.csproj
  99. +58
    -0
      Svelto.ECS/TupleRef.cs
  100. +2
    -2
      Svelto.ECS/TypeSafeDictionaryFactory.cs

+ 1
- 1
Svelto.Common

@@ -1 +1 @@
Subproject commit 800c1a9abe35986fabb6562178e27d3b17c34b5c
Subproject commit 7d7aaba893e8aa0a24b8c36b46a8ed805a8f514c

+ 0
- 3
Svelto.ECS/.github/FUNDING.yml View File

@@ -1,3 +0,0 @@
# These are supported funding model platforms

custom: https://www.paypal.me/smandala

+ 0
- 3
Svelto.ECS/.gitignore View File

@@ -1,3 +0,0 @@
/obj
/bin/Release/netstandard2.0
/bin/Debug/netstandard2.0

+ 0
- 3
Svelto.ECS/.gitmodules View File

@@ -1,3 +0,0 @@
[submodule "Svelto.Common"]
path = Svelto.Common
url = https://github.com/sebas77/Svelto.Common

+ 23
- 29
Svelto.ECS/CheckEntityUtilities.cs View File

@@ -1,29 +1,30 @@
#if DEBUG && !PROFILE_SVELTO
using System;
using System.Collections.Generic;
using Svelto.DataStructures;
#else
using System;
using System.Diagnostics;
#endif

namespace Svelto.ECS
{
/// <summary>
/// Note: this check doesn't catch the case when an add and remove is done on the same entity before the next
/// submission. Two operations on the same entity are not allowed between submissions.
/// Note: this check doesn't catch the case when an add and remove is done on the same entity before the nextI am
/// submission. Two operations on the same entity are not allowed between submissions.
/// </summary>
public partial class EnginesRoot
{
#if DEBUG && !PROFILE_SVELTO
void CheckRemoveEntityID(EGID egid)
#if DEBUG && !PROFILE_SVELTO
void CheckRemoveEntityID(EGID egid, Type entityComponent, string caller = "")
{
// Console.LogError("<color=orange>removed</color>".FastConcat(egid.ToString()));
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(egid.entityID)
.FastConcat(" groupid: ")
.FastConcat(egid.groupID));
.FastConcat(" caller: ", caller, " ")
.FastConcat(egid.entityID).FastConcat(" groupid: ").FastConcat(egid.groupID.ToName())
.FastConcat(" type: ").FastConcat(entityComponent != null ? entityComponent.Name : "not available"));

hash.Remove(egid.entityID);

@@ -33,46 +34,39 @@ namespace Svelto.ECS
else
{
throw new ECSException("Entity with not found ID is about to be removed: id: "
.FastConcat(egid.entityID)
.FastConcat(" groupid: ")
.FastConcat(egid.groupID));
.FastConcat(" caller: ", caller, " ")
.FastConcat(egid.entityID).FastConcat(" groupid: ").FastConcat(egid.groupID.ToName())
.FastConcat(" type: ").FastConcat(entityComponent != null ? entityComponent.Name : "not available"));
}
}

void CheckAddEntityID(EGID egid)
void CheckAddEntityID(EGID egid, Type entityComponent, 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)
.FastConcat("'"));
}
.FastConcat("' id: '").FastConcat(egid.entityID).FastConcat("' groupid: '")
.FastConcat(egid.groupID.ToName()).FastConcat(" ", entityComponent != null ? entityComponent.Name : "not available")
.FastConcat("'"));

hash.Add(egid.entityID);
}
void RemoveGroupID(ExclusiveGroupStruct groupID)
{
_idCheckers.Remove(groupID);
}

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)
void CheckRemoveEntityID(EGID egid, Type entityComponent, string caller = "")
{
}

[Conditional("_CHECKS_DISABLED")]
void CheckAddEntityID(EGID egid)
void CheckAddEntityID(EGID egid, Type entityComponen, string caller = "")
{
}
@@ -82,4 +76,4 @@ namespace Svelto.ECS
}
#endif
}
}
}

Svelto.ECS/EntityBuilder.CheckFields.cs → Svelto.ECS/ComponentBuilder.CheckFields.cs View File

@@ -7,7 +7,7 @@ using System.Reflection;

namespace Svelto.ECS
{
internal static class EntityBuilderUtilities
internal static class ComponentBuilderUtilities
{
const string MSG = "Entity Structs field and Entity View Struct components must hold value types.";

@@ -48,16 +48,16 @@ namespace Svelto.ECS

if (fields.Length < 1)
{
ProcessError("Entity View Structs must hold only entity components interfaces.", entityComponentType);
ProcessError("No valid fields found in Entity View Struct", entityComponentType);
}

for (int i = fields.Length - 1; i >= 0; --i)
{
FieldInfo fieldInfo = fields[i];

if (fieldInfo.FieldType.IsInterfaceEx() == false)
if (fieldInfo.FieldType.IsInterfaceEx() == false && fieldInfo.FieldType.IsValueTypeEx() == false)
{
ProcessError("Entity View Structs must hold only entity components interfaces.",
ProcessError("Entity View Structs must hold only public interfaces or value type fields.",
entityComponentType);
}

@@ -120,7 +120,7 @@ namespace Svelto.ECS
static readonly Type SERIALIZABLE_ENTITY_STRUCT = typeof(SerializableEntityComponent);
static readonly Type STRINGTYPE = typeof(string);

internal static readonly Type ENTITY_STRUCT_INFO_VIEW = typeof(EntityInfoComponentView);
internal static readonly Type ENTITY_STRUCT_INFO_VIEW = typeof(EntityInfoViewComponent);
}

public class EntityComponentException : Exception

+ 28
- 18
Svelto.ECS/ComponentBuilder.cs View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Hybrid;
using Svelto.ECS.Internal;
@@ -13,11 +14,6 @@ namespace Svelto.ECS
public ComponentBuilder()
{
_initializer = DEFAULT_IT;

EntityBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT);

if (IS_ENTITY_VIEW_COMPONENT)
EntityViewComponentCache.InitCache();
}

public ComponentBuilder(in T initializer) : this()
@@ -36,8 +32,6 @@ namespace Svelto.ECS
T entityComponent = default;
if (IS_ENTITY_VIEW_COMPONENT)
{
DBC.ECS.Check.Require(implementors != null,
$"Implementors not found while building an EntityComponent `{typeof(T)}`");
DBC.ECS.Check.Require(castedDic.ContainsKey(egid.entityID) == false,
$"building an entity with already used entity id! id: '{(ulong) egid}', {ENTITY_COMPONENT_NAME}");

@@ -82,21 +76,35 @@ namespace Svelto.ECS
IS_ENTITY_VIEW_COMPONENT = typeof(IEntityViewComponent).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_COMPONENT_TYPE);
ENTITY_COMPONENT_NAME = ENTITY_COMPONENT_TYPE.ToString();
#if UNITY_ECS
EntityComponentIDMap.Register<T>(new Filler<T>());
#endif
var IS_UNMANAGED = ENTITY_COMPONENT_TYPE.IsUnmanaged();

if (IS_UNMANAGED)
EntityComponentIDMap.Register<T>(new Filler<T>());
SetEGIDWithoutBoxing<T>.Warmup();
ComponentBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT);

if (IS_ENTITY_VIEW_COMPONENT)
EntityViewComponentCache.InitCache();
else
{
if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW && ENTITY_COMPONENT_TYPE.IsUnmanaged() == false)
throw new Exception($"Entity Component check failed, unexpected struct type (must be unmanaged) {ENTITY_COMPONENT_TYPE}");
}
}


readonly T _initializer;

internal static readonly Type ENTITY_COMPONENT_TYPE;
public static readonly bool HAS_EGID;
public static readonly bool HAS_EGID;
internal static readonly bool IS_ENTITY_VIEW_COMPONENT;

static readonly T DEFAULT_IT;
static readonly bool IS_ENTITY_VIEW_COMPONENT;
static readonly string ENTITY_COMPONENT_NAME;
static readonly T DEFAULT_IT;
static readonly string ENTITY_COMPONENT_NAME;

static class EntityViewComponentCache
{
internal static readonly FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>> cachedFields;
@@ -116,11 +124,13 @@ namespace Svelto.ECS
for (var i = fields.Length - 1; i >= 0; --i)
{
var field = fields[i];
DBC.ECS.Check.Require(field.FieldType.IsInterface == true, "Entity View Components must hold only public interfaces");
var setter = FastInvoke<T>.MakeSetter(field);
if (field.FieldType.IsInterface == true)
{
var setter = FastInvoke<T>.MakeSetter(field);

//for each interface, cache the setter for this type
cachedFields.Add(new KeyValuePair<Type, FastInvokeActionCast<T>>(field.FieldType, setter));
//for each interface, cache the setter for this type
cachedFields.Add(new KeyValuePair<Type, FastInvokeActionCast<T>>(field.FieldType, setter));
}
}

cachedTypes = new Dictionary<Type, Type[]>();


+ 8
- 8
Svelto.ECS/DataStructures/AtomicNativeBags.cs View File

@@ -12,13 +12,13 @@ namespace Svelto.ECS.DataStructures.Unity
public unsafe struct AtomicNativeBags:IDisposable
{
public const int DefaultThreadIndex = -1;
public const int MinThreadIndex = DefaultThreadIndex;
const int MinThreadIndex = DefaultThreadIndex;

#if UNITY_ECS
#if UNITY_COLLECTIONS
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
NativeBag* _data;
public readonly Allocator Allocator;
readonly NativeBag* _data;
readonly Allocator _allocator;
readonly uint _threadsCount;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -26,20 +26,20 @@ namespace Svelto.ECS.DataStructures.Unity

public AtomicNativeBags(Common.Allocator allocator, uint threadsCount)
{
Allocator = allocator;
_allocator = allocator;
_threadsCount = threadsCount;

var bufferSize = MemoryUtilities.SizeOf<NativeBag>();
var bufferCount = _threadsCount;
var allocationSize = bufferSize * bufferCount;

var ptr = (byte*)MemoryUtilities.Alloc<int>((uint) allocationSize, allocator);
var ptr = (byte*)MemoryUtilities.Alloc((uint) allocationSize, allocator);
MemoryUtilities.MemClear((IntPtr) ptr, (uint) allocationSize);

for (int i = 0; i < bufferCount; i++)
{
var bufferPtr = (NativeBag*)(ptr + bufferSize * i);
var buffer = new NativeBag((uint) i, allocator);
var buffer = new NativeBag(allocator);
MemoryUtilities.CopyStructureToPtr(ref buffer, (IntPtr) bufferPtr);
}

@@ -60,7 +60,7 @@ namespace Svelto.ECS.DataStructures.Unity
{
GetBuffer(i).Dispose();
}
MemoryUtilities.Free((IntPtr) _data, Allocator);
MemoryUtilities.Free((IntPtr) _data, _allocator);
}

public void Clear()


+ 0
- 5
Svelto.ECS/DataStructures/FastTypeSafeDictionary.cs View File

@@ -264,11 +264,6 @@ namespace Svelto.ECS.Internal
get => _implementation.unsafeValues;
}

public object GenerateSentinel()
{
throw new NotImplementedException();
}

public SetDictionary<TValue> implementation => _implementation;

[MethodImpl(MethodImplOptions.AggressiveInlining)]


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

@@ -7,34 +7,27 @@ namespace Svelto.ECS.Internal
public interface ITypeSafeDictionary<TValue> : ITypeSafeDictionary where TValue : IEntityComponent
{
void Add(uint egidEntityId, in TValue entityComponent);
ref TValue GetValueByRef(uint key);
ref TValue this[uint idEntityId] { get; }
bool TryGetValue(uint entityId, out TValue item);
ref TValue GetOrCreate(uint idEntityId);

TValue[] GetValuesArray(out uint count);
TValue[] unsafeValues { get; }
object GenerateSentinel();
IBuffer<TValue> GetValues(out uint count);
ref TValue GetDirectValueByRef(uint key);
}

public interface ITypeSafeDictionary
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,
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);

void SetCapacity(uint size);


+ 9
- 29
Svelto.ECS/DataStructures/NativeBag.cs View File

@@ -12,12 +12,12 @@ namespace Svelto.ECS.DataStructures
/// 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 NativeRingBuffer because it can also be used as
/// 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
{
#if UNITY_ECS
#if UNITY_COLLECTIONS
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
unsafe UnsafeBlob* _queue;
@@ -73,31 +73,11 @@ namespace Svelto.ECS.DataStructures
unsafe
{
var sizeOf = MemoryUtilities.SizeOf<UnsafeBlob>();
var listData = (UnsafeBlob*) MemoryUtilities.Alloc<UnsafeBlob>((uint) sizeOf, allocator);
var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator);

//clear to nullify the pointers
MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf);
listData->allocator = allocator;
#if DEBUG && !PROFILE_SVELTO
listData->id = 0xDEADBEEF;
#endif
_queue = listData;
}
}

public NativeBag(uint bufferID, Allocator allocator)
{
unsafe
{
var sizeOf = MemoryUtilities.SizeOf<UnsafeBlob>();
var listData = (UnsafeBlob*) MemoryUtilities.Alloc<UnsafeBlob>((uint) sizeOf, allocator);

//clear to nullify the pointers
MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf);
listData->allocator = allocator;
#if DEBUG && !PROFILE_SVELTO
listData->id = bufferID;
#endif
_queue = listData;
}
}
@@ -113,7 +93,7 @@ namespace Svelto.ECS.DataStructures
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T ReserveEnqueue<T>(out UnsafeArrayIndex index) where T : unmanaged
public ref T ReserveEnqueue<T>(out UnsafeArrayIndex index) where T : struct
{
unsafe
{
@@ -123,14 +103,14 @@ namespace Svelto.ECS.DataStructures
#endif
var sizeOf = MemoryUtilities.SizeOf<T>();
if (_queue->space - sizeOf < 0)
_queue->Realloc<int>((uint) ((_queue->capacity + sizeOf) * 1.5f));
_queue->Realloc((uint) ((_queue->capacity + sizeOf) * 2.0f));

return ref _queue->Reserve<T>(out index);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Enqueue<T>(in T item) where T : unmanaged
public void Enqueue<T>(in T item) where T : struct
{
unsafe
{
@@ -140,7 +120,7 @@ namespace Svelto.ECS.DataStructures
#endif
var sizeOf = MemoryUtilities.SizeOf<T>();
if (_queue->space - sizeOf < 0)
_queue->Realloc<int>((uint) ((_queue->capacity + sizeOf) * 1.5f));
_queue->Realloc((uint) ((_queue->capacity + MemoryUtilities.Align4((uint) sizeOf)) * 2.0f));

_queue->Write(item);
}
@@ -159,7 +139,7 @@ namespace Svelto.ECS.DataStructures
}
}

public T Dequeue<T>() where T : unmanaged
public T Dequeue<T>() where T : struct
{
unsafe
{
@@ -167,7 +147,7 @@ namespace Svelto.ECS.DataStructures
}
}

public ref T AccessReserved<T>(UnsafeArrayIndex reserverIndex) where T : unmanaged
public ref T AccessReserved<T>(UnsafeArrayIndex reserverIndex) where T : struct
{
unsafe
{


+ 179
- 39
Svelto.ECS/DataStructures/NativeDynamicArray.cs View File

@@ -7,8 +7,8 @@ namespace Svelto.ECS.DataStructures
{
public struct NativeDynamicArray : IDisposable
{
#if UNITY_ECS
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#if UNITY_COLLECTIONS
[global::Unity.Burst.NoAlias] [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
unsafe UnsafeArray* _list;
#if DEBUG && !PROFILE_SVELTO
@@ -16,7 +16,7 @@ namespace Svelto.ECS.DataStructures
#endif

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint Count<T>() where T:unmanaged
public int Count<T>() where T : struct
{
unsafe
{
@@ -26,13 +26,13 @@ namespace Svelto.ECS.DataStructures
if (hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");

#endif
return (uint) (_list->count / MemoryUtilities.SizeOf<T>());
#endif
return (_list->count / MemoryUtilities.SizeOf<T>());
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint Capacity<T>() where T:unmanaged
public int Capacity<T>() where T : struct
{
unsafe
{
@@ -42,12 +42,12 @@ namespace Svelto.ECS.DataStructures
if (hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");

#endif
return (uint) (_list->capacity / MemoryUtilities.SizeOf<T>());
#endif
return (_list->capacity / MemoryUtilities.SizeOf<T>());
}
}

public static NativeDynamicArray Alloc<T>(Allocator allocator, uint newLength = 0) where T : unmanaged
public static NativeDynamicArray Alloc<T>(Allocator allocator, uint newLength = 0) where T : struct
{
unsafe
{
@@ -55,17 +55,16 @@ namespace Svelto.ECS.DataStructures
#if DEBUG && !PROFILE_SVELTO
rtnStruc.hashType = TypeHash<T>.hash;
#endif
var sizeOf = MemoryUtilities.SizeOf<T>();
var sizeOf = MemoryUtilities.SizeOf<T>();

uint pointerSize = (uint) MemoryUtilities.SizeOf<UnsafeArray>();
UnsafeArray* listData = (UnsafeArray*) MemoryUtilities.Alloc(pointerSize, allocator);

uint pointerSize = (uint) MemoryUtilities.SizeOf<UnsafeArray>();
UnsafeArray* listData =
(UnsafeArray*) MemoryUtilities.Alloc<UnsafeArray>(pointerSize, allocator);
//clear to nullify the pointers
MemoryUtilities.MemClear((IntPtr) listData, pointerSize);

listData->allocator = allocator;
listData->Realloc<T>((uint) (newLength * sizeOf));
listData->Realloc((uint) (newLength * sizeOf));

rtnStruc._list = listData;

@@ -74,7 +73,7 @@ namespace Svelto.ECS.DataStructures
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Get<T>(uint index) where T : unmanaged
public ref T Get<T>(uint index) where T : struct
{
unsafe
{
@@ -91,7 +90,7 @@ namespace Svelto.ECS.DataStructures
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Set<T>(uint index, in T value) where T : unmanaged
public void Set<T>(uint index, in T value) where T : struct
{
unsafe
{
@@ -101,24 +100,24 @@ namespace Svelto.ECS.DataStructures
if (hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");
if (index >= Capacity<T>())
throw new Exception($"NativeDynamicArray: out of bound access, index {index} count {Count<T>()}");
#endif
throw new Exception($"NativeDynamicArray: out of bound access, index {index} capacity {Capacity<T>()}");
#endif
_list->Set(index, value);
}
}

public unsafe void Dispose()
{
#if DEBUG && !PROFILE_SVELTO
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("NativeDynamicArray: null-access");
#endif
_list->Dispose();
_list = null;
_list->Dispose();
_list = null;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add<T>(in T item) where T : unmanaged
public void Add<T>(in T item) where T : struct
{
unsafe
{
@@ -129,16 +128,52 @@ namespace Svelto.ECS.DataStructures
throw new Exception("NativeDynamicArray: not excepted type used");
#endif
var structSize = (uint) MemoryUtilities.SizeOf<T>();
if (_list->space - (int)structSize < 0)
_list->Realloc<T>((uint) ((Count<T>() + 1) * structSize * 1.5f));
if (_list->space - (int) structSize < 0)
_list->Realloc((uint) (((uint) ((Count<T>() + 1) * 1.5f) * (float) structSize)));
_list->Add(item);
}
}

public void Grow<T>(uint newCapacity) 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 excepted 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);
}
}

public void SetCount<T>(uint count) 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 excepted type used");
#endif
uint structSize = (uint) MemoryUtilities.SizeOf<T>();
uint size = (uint) (count * structSize);

_list->SetCountTo((uint) size);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddWithoutGrow<T>(in T item) where T : unmanaged
public void AddWithoutGrow<T>(in T item) where T : struct
{
unsafe
{
@@ -157,6 +192,29 @@ namespace Svelto.ECS.DataStructures
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void UnorderedRemoveAt<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 excepted type used");
if (Count<T>() == 0)
throw new Exception("NativeDynamicArray: empty array invalid operation");
#endif
var count = Count<T>() - 1;
if (index < count)
{
Set<T>(index, Get<T>((uint) count));
}

_list->Pop<T>();
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
@@ -182,6 +240,21 @@ namespace Svelto.ECS.DataStructures
return (T*) _list->ptr;
}

public IntPtr ToIntPTR<T>() 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 excepted type used");

#endif
return (IntPtr) _list->ptr;
}
}

public T[] ToManagedArray<T>() where T : unmanaged
{
unsafe
@@ -193,17 +266,19 @@ namespace Svelto.ECS.DataStructures
throw new Exception("NativeDynamicArray: not excepted type used");

#endif
var ret = new T[Count<T>()];
var count = Count<T>();
var ret = new T[count];
var lengthToCopyInBytes = count * MemoryUtilities.SizeOf<T>();

fixed (void * handle = ret)
fixed (void* handle = ret)
{
Buffer.MemoryCopy(_list->ptr, handle, _list->count, _list->count);
Unsafe.CopyBlock(handle, _list->ptr, (uint) lengthToCopyInBytes);
}

return ret;
}
}
public T[] ToManagedArrayUntrimmed<T>() where T : unmanaged
{
unsafe
@@ -213,17 +288,82 @@ namespace Svelto.ECS.DataStructures
throw new Exception("NativeDynamicArray: null-access");
if (hashType != TypeHash<T>.hash)
throw new Exception("NativeDynamicArray: not excepted type used");

#endif
var ret = new T[Capacity<T>()];
var capacity = Capacity<T>();
var lengthToCopyInBytes = capacity * MemoryUtilities.SizeOf<T>();
var ret = new T[capacity];

fixed (void * handle = ret)
fixed (void* handle = ret)
{
Buffer.MemoryCopy(_list->ptr, handle, _list->capacity, _list->capacity);
Unsafe.CopyBlock(handle, _list->ptr, (uint) lengthToCopyInBytes);
}

return ret;
}
}

public void RemoveAt<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 excepted type used");
#endif

var sizeOf = MemoryUtilities.SizeOf<T>();
//Unsafe.CopyBlock may not be memory overlapping safe (memcpy vs memmove)
Buffer.MemoryCopy(_list->ptr + (index + 1) * sizeOf, _list->ptr + index * sizeOf, _list->count
, (uint) ((Count<T>() - (index + 1)) * sizeOf));
_list->Pop<T>();
}
}

public void MemClear()
{
unsafe
{
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(); }
}
}
}

+ 23
- 0
Svelto.ECS/DataStructures/NativeDynamicArrayUnityExtension.cs View File

@@ -0,0 +1,23 @@
#if UNITY_COLLECTIONS
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;

namespace Svelto.ECS.DataStructures
{
public static class NativeDynamicArrayUnityExtension
{
public static NativeArray<T> ToNativeArray<T>(this NativeDynamicArray array) where T : struct
{
unsafe
{
var nativeArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<T>(
(void*) array.ToIntPTR<T>(), (int) array.Count<T>(), Allocator.None);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref nativeArray, AtomicSafetyHandle.Create());
#endif
return nativeArray;
}
}
}
}
#endif

+ 1
- 1
Svelto.ECS/DataStructures/SharedNativeInt.cs View File

@@ -6,7 +6,7 @@ namespace Svelto.ECS.DataStructures
{
public struct SharedNativeInt: IDisposable
{
#if UNITY_ECS
#if UNITY_COLLECTIONS
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
unsafe int* data;


+ 0
- 70
Svelto.ECS/DataStructures/SharedNativeUInt.cs View File

@@ -1,70 +0,0 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

namespace Svelto.ECS.DataStructures
{
public struct SharedNativeUInt: IDisposable
{
#if UNITY_ECS
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
unsafe uint* data;

public SharedNativeUInt(uint t)
{
unsafe
{
data = (uint*) Marshal.AllocHGlobal(sizeof(uint));
*data = t;
}
}
public static implicit operator uint(SharedNativeUInt t)
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (t.data == null)
throw new Exception("using disposed SharedUInt");
#endif

return *t.data;
}
}

public void Dispose()
{
unsafe
{
if (data != null)
{
Marshal.FreeHGlobal((IntPtr) data);
data = null;
}
}
}

public void Decrement()
{
unsafe
{
int result = Interlocked.Decrement(ref Unsafe.As<uint, int>(ref *data));
#if DEBUG && !PROFILE_SVELTO
if (result < 0)
throw new Exception("can't have negative numbers");
#endif
}
}
public void Increment()
{
unsafe
{
Interlocked.Increment(ref Unsafe.As<uint, int>(ref *data));
}
}
}
}

+ 395
- 170
Svelto.ECS/DataStructures/TypeSafeDictionary.cs View File

@@ -2,202 +2,468 @@
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Hybrid;

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 =
_type.IsUnmanaged() && (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;

public TypeSafeDictionary(uint size)
{
_implementation = new FasterDictionary<uint, TValue>(size);
if (_isUmanaged)
implUnmgd = new SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, NativeStrategy<TValue>>(size);
else
{
implMgd = new SveltoDictionary<uint, TValue, NativeStrategy<FasterDictionaryNode<uint>>, ManagedStrategy<TValue>>(size);
}
}

public TypeSafeDictionary()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(uint egidEntityId, in TValue entityComponent)
{
_implementation = new FasterDictionary<uint, TValue>(1);
if (_isUmanaged)
implUnmgd.Add(egidEntityId, entityComponent);
else
implMgd.Add(egidEntityId, entityComponent);
}

/// <summary>
/// Add entities from external typeSafeDictionary
/// Add entities from external typeSafeDictionary
/// </summary>
/// <param name="entitiesToSubmit"></param>
/// <param name="groupId"></param>
/// <exception cref="TypeSafeDictionaryException"></exception>
public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId)
public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId)
{
var typeSafeDictionary = entitiesToSubmit as TypeSafeDictionary<TValue>;
if (_isUmanaged)
{
var typeSafeDictionary = (entitiesToSubmit as TypeSafeDictionary<TValue>).implUnmgd;

foreach (var tuple in typeSafeDictionary)
foreach (var tuple in typeSafeDictionary)
try
{
if (_hasEgid)
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(
ref tuple.Value, new EGID(tuple.Key, groupId));

implUnmgd.Add(tuple.Key, tuple.Value);
}
catch (Exception e)
{
Console.LogException(
e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key));

throw;
}
}
else
{
try
{
if (_hasEgid)
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref tuple.Value, new EGID(tuple.Key, groupId));
var typeSafeDictionary = (entitiesToSubmit as TypeSafeDictionary<TValue>).implMgd;

_implementation.Add(tuple.Key, tuple.Value);
}
catch (Exception e)
{
Svelto.Console.LogException(e,
"trying to add an EntityComponent with the same ID more than once Entity: "
.FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId)
.FastConcat(", id ").FastConcat(tuple.Key));
foreach (var tuple in typeSafeDictionary)
try
{
if (_hasEgid)
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(
ref tuple.Value, new EGID(tuple.Key, groupId));

throw;
}
implMgd.Add(tuple.Key, tuple.Value);
}
catch (Exception e)
{
Console.LogException(
e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key));

throw;
}
}
}

public void AddEntitiesToEngines
(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB
, ITypeSafeDictionary realDic, ExclusiveGroupStruct group, in PlatformProfiler profiler)
{
if (_isUmanaged)
{
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));
}
else
{
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 implMgd)
AddEntityComponentToEngines(entityComponentEnginesDB, ref typeSafeDictionary[value.Key]
, null, in profiler, new EGID(value.Key, group));
}
}

public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup)
{
var valueIndex = _implementation.GetIndex(fromEntityGid.entityID);
if (_isUmanaged)
{
var valueIndex = implUnmgd.GetIndex(fromEntityGid.entityID);

DBC.ECS.Check.Require(toGroup != null
, "Invalid To Group"); //todo check this, if it's right merge GetIndex
{
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
ref var entity = ref implUnmgd.GetDirectValueByRef(valueIndex);

if (_hasEgid)
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID);

if (toGroup != null)
toGroupCasted.Add(fromEntityGid.entityID, entity);
}
}
else
{
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
ref var entity = ref _implementation.unsafeValues[(int) valueIndex];
var valueIndex = implMgd.GetIndex(fromEntityGid.entityID);

if (_hasEgid) SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID);
DBC.ECS.Check.Require(toGroup != null
, "Invalid To Group"); //todo check this, if it's right merge GetIndex
{
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
ref var entity = ref implMgd.GetDirectValueByRef(valueIndex);

if (_hasEgid)
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID);

toGroupCasted.Add(fromEntityGid.entityID, entity);
toGroupCasted.Add(fromEntityGid.entityID, entity);
}
}
}

public void AddEntitiesToEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB,
ITypeSafeDictionary realDic,
ExclusiveGroupStruct @group,
in PlatformProfiler profiler)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
var typeSafeDictionary = realDic as ITypeSafeDictionary<TValue>;
if (_isUmanaged)
{
implUnmgd.Clear();
}
else
{
implMgd.Clear();
}
}

//this can be optimized, should pass all the entities and not restart the process for each one
foreach (var value in _implementation)
AddEntityComponentToEngines(entityComponentEnginesDB, ref typeSafeDictionary.GetValueByRef(value.Key), null,
in profiler, new EGID(value.Key, group));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FastClear()
{
if (_isUmanaged)
{
implUnmgd.FastClear();
}
else
{
implMgd.FastClear();
}
}

public void RemoveEntitiesFromEngines(
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB, in PlatformProfiler profiler,
ExclusiveGroupStruct @group)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ContainsKey(uint egidEntityId)
{
foreach (var value in _implementation)
RemoveEntityComponentFromEngines(entityComponentEnginesDB, ref _implementation.GetValueByRef(value.Key), null,
in profiler, new EGID(value.Key, group));
if (_isUmanaged)
{
return implUnmgd.ContainsKey(egidEntityId);
}
else
{
return implMgd.ContainsKey(egidEntityId);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FastClear() { _implementation.FastClear(); }
public ITypeSafeDictionary Create()
{
return TypeSafeDictionaryFactory<TValue>.Create(1);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(uint key) { return _implementation.ContainsKey(key); }
public uint GetIndex(uint valueEntityId)
{
if (_isUmanaged)
{
return this.implUnmgd.GetIndex(valueEntityId);
}
else
{
return this.implMgd.GetIndex(valueEntityId);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntityFromDictionary(EGID fromEntityGid)
public ref TValue GetOrCreate(uint idEntityId)
{
_implementation.Remove(fromEntityGid.entityID);
if (_isUmanaged)
{
return ref this.implUnmgd.GetOrCreate(idEntityId);
}
else
{
return ref this.implMgd.GetOrCreate(idEntityId);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetCapacity(uint size) { _implementation.SetCapacity(size); }
public IBuffer<TValue> GetValues(out uint count)
{
if (_isUmanaged)
{
return this.implUnmgd.GetValues(out count);
}
else
{
return this.implMgd.GetValues(out count);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Trim() { _implementation.Trim(); }
public ref TValue GetDirectValueByRef(uint key)
{
if (_isUmanaged)
{
return ref this.implUnmgd.GetDirectValueByRef(key);
}
else
{
return ref this.implMgd.GetDirectValueByRef(key);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() { _implementation.Clear(); }
public bool Has(uint key)
{
if (_isUmanaged)
{
return this.implUnmgd.ContainsKey(key);
}
else
{
return this.implMgd.ContainsKey(key);
}
}

public void MoveEntityFromEngines(EGID fromEntityGid,
EGID? toEntityID, ITypeSafeDictionary toGroup,
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines,
in PlatformProfiler profiler)
public void MoveEntityFromEngines
(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup
, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, in PlatformProfiler profiler)
{
var valueIndex = _implementation.GetIndex(fromEntityGid.entityID);
if (_isUmanaged)
{
var valueIndex = this.implUnmgd.GetIndex(fromEntityGid.entityID);

ref var entity = ref this.implUnmgd.GetDirectValueByRef(valueIndex);

if (toGroup != null)
{
RemoveEntityComponentFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler
, fromEntityGid);

ref var entity = ref _implementation.unsafeValues[(int) valueIndex];
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
var previousGroup = fromEntityGid.groupID;

if (toGroup != null)
if (_hasEgid)
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID.Value);

var index = toGroupCasted.GetIndex(toEntityID.Value.entityID);

AddEntityComponentToEngines(engines, ref toGroupCasted.GetDirectValueByRef(index), previousGroup
, in profiler, toEntityID.Value);
}
else
{
RemoveEntityComponentFromEngines(engines, ref entity, null, in profiler, fromEntityGid);
}
}
else
{
RemoveEntityComponentFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler, fromEntityGid);
var valueIndex = this.implMgd.GetIndex(fromEntityGid.entityID);

var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
var previousGroup = fromEntityGid.groupID;
ref var entity = ref this.implMgd.GetDirectValueByRef(valueIndex);

if (_hasEgid) SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID.Value);
if (toGroup != null)
{
RemoveEntityComponentFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler
, fromEntityGid);

var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
var previousGroup = fromEntityGid.groupID;

if (_hasEgid)
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID.Value);

var index = toGroupCasted.GetIndex(toEntityID.Value.entityID);
var index = toGroupCasted.GetIndex(toEntityID.Value.entityID);

AddEntityComponentToEngines(engines, ref toGroupCasted.unsafeValues[(int) index], previousGroup, in profiler,
toEntityID.Value);
AddEntityComponentToEngines(engines, ref toGroupCasted.GetDirectValueByRef(index), previousGroup
, in profiler, toEntityID.Value);
}
else
{
RemoveEntityComponentFromEngines(engines, ref entity, null, in profiler, fromEntityGid);
}
}
}

public void RemoveEntitiesFromEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines
, in PlatformProfiler profiler, ExclusiveGroupStruct group)
{
if (_isUmanaged)
{
foreach (var value in implUnmgd)
RemoveEntityComponentFromEngines(engines, ref implUnmgd.GetValueByRef(value.Key), null
, in profiler, new EGID(value.Key, group));
}
else
RemoveEntityComponentFromEngines(engines, ref entity, null, in profiler, fromEntityGid);
{
foreach (var value in implMgd)
RemoveEntityComponentFromEngines(engines, ref implMgd.GetValueByRef(value.Key), null
, in profiler, new EGID(value.Key, group));
}
}

public uint Count
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntityFromDictionary(EGID fromEntityGid)
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _implementation.count;
if (_isUmanaged)
{
this.implUnmgd.Remove(fromEntityGid.entityID);
}
else
{
this.implMgd.Remove(fromEntityGid.entityID);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ITypeSafeDictionary Create() { return new TypeSafeDictionary<TValue>(); }
public void SetCapacity(uint size)
{
if (_isUmanaged)
{
this.implUnmgd.SetCapacity(size);
}
else
{
this.implMgd.SetCapacity(size);
}
}

void AddEntityComponentToEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB,
ref TValue entity,
ExclusiveGroupStruct? previousGroup,
in PlatformProfiler profiler,
EGID egid)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Trim()
{
//get all the engines linked to TValue
if (!entityComponentEnginesDB.TryGetValue(new RefWrapper<Type>(_type), out var entityComponentsEngines)) return;
if (_isUmanaged)
{
this.implUnmgd.Trim();
}
else
{
this.implMgd.Trim();
}
}

if (previousGroup == null)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryFindIndex(uint entityId, out uint index)
{
if (_isUmanaged)
{
for (var i = 0; i < entityComponentsEngines.count; i++)
try
{
using (profiler.Sample(entityComponentsEngines[i], _typeName))
{
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Add(ref entity, egid);
}
}
catch (Exception e)
{
throw new
ECSException("Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()), e);
}
return implUnmgd.TryFindIndex(entityId, out index);
}
else
{
return implMgd.TryFindIndex(entityId, out index);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(uint entityId, out TValue item)
{
if (_isUmanaged)
{
return this.implUnmgd.TryGetValue(entityId, out item);
}
else
{
return this.implMgd.TryGetValue(entityId, out item);
}
}

public uint count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (_isUmanaged)
{
return this.implUnmgd.count;
}
else
{
return this.implMgd.count;
}
}
}

public ref TValue this[uint idEntityId]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (_isUmanaged)
{
return ref this.implUnmgd.GetValueByRef(idEntityId);
}
else
{
return ref this.implMgd.GetValueByRef(idEntityId);
}
}
}

static void RemoveEntityComponentFromEngines
(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines, ref TValue entity, uint? previousGroup
, in PlatformProfiler profiler, EGID egid)
{
if (!engines.TryGetValue(new RefWrapper<Type>(_type), out var entityComponentsEngines))
return;

if (previousGroup == null)
for (var i = 0; i < entityComponentsEngines.count; i++)
try
{
using (profiler.Sample(entityComponentsEngines[i], _typeName))
{
(entityComponentsEngines[i] as IReactOnSwap<TValue>).MovedTo(ref entity, previousGroup.Value,
egid);
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity, egid);
}
}
catch (Exception e)
catch
{
throw new
ECSException("Code crashed inside MovedTo callback ".FastConcat(typeof(TValue).ToString()),
e);
Svelto.Console.LogError("Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()));
throw;
}
}
}

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

if (previousGroup == null)
{
@@ -205,86 +471,45 @@ namespace Svelto.ECS.Internal
try
{
using (profiler.Sample(entityComponentsEngines[i], _typeName))
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity, egid);
{
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Add(ref entity, egid);
}
}
catch (Exception e)
catch
{
throw new
ECSException("Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()),
e);
Svelto.Console.LogError("Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()));
throw;
}
}
#if SEEMS_UNNECESSARY
else
{
for (var i = 0; i < entityComponentsEngines.Count; i++)
for (var i = 0; i < entityComponentsEngines.count; i++)
try
{
using (profiler.Sample(entityComponentsEngines[i], _typeName))
(entityComponentsEngines[i] as IReactOnSwap<TValue>).MovedFrom(ref entity, egid);
{
(entityComponentsEngines[i] as IReactOnSwap<TValue>).MovedTo(
ref entity, previousGroup.Value, egid);
}
}
catch (Exception e)
catch (Exception)
{
throw new ECSException(
"Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e);
Svelto.Console.LogError("Code crashed inside MoveTo callback ".FastConcat(typeof(TValue).ToString()));

throw;
}
}
#endif
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TValue[] GetValuesArray(out uint count)
{
var managedBuffer = _implementation.GetValuesArray(out count);
return managedBuffer;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ContainsKey(uint egidEntityId) { return _implementation.ContainsKey(egidEntityId); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(uint egidEntityId, in TValue entityComponent) { _implementation.Add(egidEntityId, entityComponent); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public FasterDictionary<uint, TValue>.FasterDictionaryKeyValueEnumerator GetEnumerator()
{
return _implementation.GetEnumerator();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref TValue GetValueByRef(uint key) { return ref _implementation.GetValueByRef(key); }

public ref TValue this[uint idEntityId]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _implementation.GetValueByRef(idEntityId);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetIndex(uint valueEntityId) { return _implementation.GetIndex(valueEntityId); }

public TValue[] unsafeValues
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _implementation.unsafeValues;
}

public object GenerateSentinel()
public void Dispose()
{
return default;
if (_isUmanaged)
implUnmgd.Dispose();
else
implMgd.Dispose();
GC.SuppressFinalize(this);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(uint entityId, out TValue item) { return _implementation.TryGetValue(entityId, out item); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref TValue GetOrCreate(uint idEntityId) { return ref _implementation.GetOrCreate(idEntityId); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryFindIndex(uint entityId, out uint index) { return _implementation.TryFindIndex(entityId, out index); }

internal FasterDictionary<uint, TValue> implementation => _implementation;
readonly FasterDictionary<uint, TValue> _implementation;
}
}

+ 0
- 8
Svelto.ECS/DataStructures/TypeSafeDictionaryUtilities.cs View File

@@ -9,13 +9,5 @@ namespace Svelto.ECS.Internal

return mapper;
}

internal static NativeEGIDMapper<T> ToNativeEGIDMapper<T>(this TypeSafeDictionary<T> dic,
ExclusiveGroupStruct groupStructId) where T : unmanaged, IEntityComponent
{
var mapper = new NativeEGIDMapper<T>(groupStructId, dic.implementation.ToNative<uint, T>());

return mapper;
}
}
}

+ 31
- 16
Svelto.ECS/DataStructures/UnsafeArray.cs View File

@@ -9,12 +9,13 @@ namespace Svelto.ECS.DataStructures
internal unsafe byte* ptr => _ptr;

//expressed in bytes
internal uint capacity => _capacity;
internal int capacity => (int) _capacity;

//expressed in bytes
internal uint count => _writeIndex;
internal int count => (int) _writeIndex;

//expressed in bytes
internal uint space => capacity - count;
internal int space => capacity - count;

/// <summary>
/// </summary>
@@ -26,37 +27,36 @@ namespace Svelto.ECS.DataStructures
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Get<T>(uint index) where T : unmanaged
public ref T Get<T>(uint index) where T : struct
{
unsafe
{
T* buffer = (T*) ptr;
return ref buffer[index];
return ref Unsafe.AsRef<T>(Unsafe.Add<T>(ptr, (int) index));
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Set<T>(uint index, in T value) where T : unmanaged
public void Set<T>(uint index, in T value) where T : struct
{
unsafe
{
int sizeOf = MemoryUtilities.SizeOf<T>();
uint sizeOf = (uint) MemoryUtilities.SizeOf<T>();
uint writeIndex = (uint) (index * sizeOf);
#if DEBUG && !PROFILE_SVELTO
if (_capacity < writeIndex + sizeOf)
throw new Exception("no writing authorized");
#endif
T* buffer = (T*) ptr;
buffer[index] = value;
#endif
Unsafe.AsRef<T>(Unsafe.Add<T>(_ptr, (int) index)) = value;

if (_writeIndex < writeIndex + sizeOf)
_writeIndex = (uint) (writeIndex + sizeOf);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add<T>(in T value) where T : unmanaged
public void Add<T>(in T value) where T : struct
{
unsafe
{
@@ -73,7 +73,15 @@ namespace Svelto.ECS.DataStructures
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Realloc<T>(uint newCapacity) where T : unmanaged
public void Pop<T>() where T : struct
{
var structSize = MemoryUtilities.SizeOf<T>();
_writeIndex -= (uint)structSize;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Realloc(uint newCapacity)
{
unsafe
{
@@ -84,9 +92,9 @@ namespace Svelto.ECS.DataStructures
#endif
if (newCapacity >= 0)
{
newPointer = (byte*) MemoryUtilities.Alloc<T>(newCapacity, allocator);
newPointer = (byte*) MemoryUtilities.Alloc(newCapacity, allocator);
if (count > 0)
Unsafe.CopyBlock(newPointer, ptr, count);
Unsafe.CopyBlock(newPointer, ptr, (uint) count);
}

if (ptr != null)
@@ -117,10 +125,17 @@ namespace Svelto.ECS.DataStructures
_writeIndex = 0;
}
#if UNITY_ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetCountTo(uint count)
{
_writeIndex = count;
}
#if UNITY_COLLECTIONS
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
unsafe byte* _ptr;
uint _writeIndex;
uint _capacity;
}

+ 108
- 114
Svelto.ECS/DataStructures/UnsafeBlob.cs View File

@@ -1,10 +1,10 @@
using System;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Unity.Collections.LowLevel.Unsafe;

namespace Svelto.ECS.DataStructures
{
//ToDO to complete in future version of svelto, maybe removed
public struct UnsafeArrayIndex
{
internal uint index;
@@ -12,116 +12,119 @@ namespace Svelto.ECS.DataStructures
}

/// <summary>
/// Note: this must work inside burst, so it must follow burst restrictions
/// Note: All the svelto native structures
/// Note: this must work inside burst, so it must follow burst restrictions
/// Note: All the svelto native structures
/// </summary>
struct UnsafeBlob : IDisposable
{
internal unsafe byte* ptr => _ptr;
internal unsafe byte* ptr { get; set; }

//expressed in bytes
internal uint capacity { get; private set; }

//expressed in bytes
internal uint size => _writeIndex - _readIndex;

//expressed in bytes
internal uint space => capacity - size;

/// <summary>
/// </summary>
internal Allocator allocator;
#if DEBUG && !PROFILE_SVELTO
internal uint id;
#endif

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Write<T>(in T item) where T : unmanaged
internal void Write<T>(in T item) 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
#if DEBUG && !PROFILE_SVELTO
if (space - (int)structSize < 0)
#if DEBUG && !PROFILE_SVELTO
if (space - (int) structSize < 0)
throw new Exception("no writing authorized");
#endif
var writeHead = _writeIndex % capacity;

if (writeHead + structSize <= capacity)
{
Unsafe.Write(_ptr + writeHead, item);
Unsafe.Write(ptr + writeHead, item);
}
else
//copy with wrap, will start to copy and wrap for the reminder
//copy with wrap, will start to copy and wrap for the reminder
{
var byteCountToEnd = capacity - writeHead;

fixed (T* readFrom = &item)
{
//read and copy the first portion of Item until the end of the stream
Unsafe.CopyBlock(_ptr + writeHead, readFrom, byteCountToEnd);
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;
var restCount = structSize - byteCountToEnd;

//read and copy the remainder
var @from = (byte*) readFrom;
Unsafe.CopyBlock(_ptr, @from + byteCountToEnd, restCount);
}
//read and copy the remainder
Unsafe.CopyBlock(ptr, (byte*) Unsafe.AsPointer(ref localCopyToAvoidGcIssues) + byteCountToEnd
, restCount);
}

uint paddedStructSize = Align4(structSize);
//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);

_writeIndex += paddedStructSize;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void WriteUnaligned<T>(in T item) where T : unmanaged
{
unsafe
{
uint structSize = (uint) MemoryUtilities.SizeOf<T>();
//the idea is, considering the wrap, a read pointer must always be behind a writer pointer
#if DEBUG && !PROFILE_SVELTO
if (space - (int)structSize < 0)
throw new Exception("no writing authorized");
#endif
var pointer = _writeIndex % capacity;

if (pointer + structSize <= capacity)
Unsafe.Write(_ptr + pointer, item);
else
{
var byteCount = capacity - pointer;
fixed (T* readFrom = &item)
{
Unsafe.CopyBlockUnaligned(_ptr + pointer, readFrom, byteCount);

var restCount = structSize - byteCount;
var @from = (byte*) readFrom;
Unsafe.CopyBlockUnaligned(_ptr, @from + byteCount, restCount);
}
}
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
// //ToDo: remove this and create an UnsafeBlobUnaligned, used on NativeRingBuffer where T cannot change
// internal void WriteUnaligned<T>(in T item) 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
// #if DEBUG && !PROFILE_SVELTO
// if (space - (int) structSize < 0)
// throw new Exception("no writing authorized");
// #endif
// var pointer = _writeIndex % capacity;
//
// if (pointer + structSize <= capacity)
// {
// Unsafe.Write(ptr + pointer, item);
// }
// else
// {
// var byteCount = capacity - pointer;
//
// var localCopyToAvoidGCIssues = item;
//
// Unsafe.CopyBlockUnaligned(ptr + pointer, Unsafe.AsPointer(ref localCopyToAvoidGCIssues), byteCount);
//
// var restCount = structSize - byteCount;
// Unsafe.CopyBlockUnaligned(ptr, (byte*) Unsafe.AsPointer(ref localCopyToAvoidGCIssues) + byteCount
// , restCount);
// }
//
// _writeIndex += structSize;
// }
// }

_writeIndex += structSize;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal T Read<T>() where T : unmanaged
internal T Read<T>() where T : struct
{
unsafe
{
var structSize = (uint) MemoryUtilities.SizeOf<T>();
#if DEBUG && !PROFILE_SVELTO
#if DEBUG && !PROFILE_SVELTO
if (size < structSize) //are there enough bytes to read?
throw new Exception("dequeuing empty queue or unexpected type dequeued");
if (_readIndex > _writeIndex)
throw new Exception("unexpected read");
#endif
var head = _readIndex % capacity;
uint paddedStructSize = Align4(structSize);
var head = _readIndex % capacity;
var paddedStructSize = MemoryUtilities.Align4(structSize);
_readIndex += paddedStructSize;

if (_readIndex == _writeIndex)
@@ -134,78 +137,78 @@ namespace Svelto.ECS.DataStructures
}

if (head + paddedStructSize <= capacity)
{
return Unsafe.Read<T>(_ptr + head);
}
return Unsafe.Read<T>(ptr + head);

T item = default;
T* destination = &item;
var byteCountToEnd = capacity - head;
Unsafe.CopyBlock(destination, _ptr + head, byteCountToEnd);
Unsafe.CopyBlock(Unsafe.AsPointer(ref item), ptr + head, byteCountToEnd);

var restCount = structSize - byteCountToEnd;
Unsafe.CopyBlock((byte*) destination + byteCountToEnd, ptr, restCount);
Unsafe.CopyBlock((byte*) Unsafe.AsPointer(ref item) + byteCountToEnd, ptr, restCount);

return item;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref T Reserve<T>(out UnsafeArrayIndex index) where T : unmanaged
internal ref T Reserve<T>(out UnsafeArrayIndex index) where T : struct
{
unsafe
{
var sizeOf = (uint) MemoryUtilities.SizeOf<T>();
T* buffer = (T *)(_ptr + _writeIndex);

ref var buffer = ref Unsafe.AsRef<T>(ptr + _writeIndex);

#if DEBUG && !PROFILE_SVELTO
if (_writeIndex > capacity) throw new Exception($"can't reserve if the writeIndex wrapped around the capacity, writeIndex {_writeIndex} capacity {capacity}");
if (_writeIndex + sizeOf > capacity) throw new Exception("out of bound reserving");
#endif
if (_writeIndex > capacity)
throw new Exception(
$"can't reserve if the writeIndex wrapped around the capacity, writeIndex {_writeIndex} capacity {capacity}");
if (_writeIndex + sizeOf > capacity)
throw new Exception("out of bound reserving");
#endif
index = new UnsafeArrayIndex
{
capacity = capacity
, index = _writeIndex
};

var align4 = Align4(sizeOf);
var align4 = MemoryUtilities.Align4(sizeOf);
_writeIndex += align4;
return ref buffer[0];
return ref buffer;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref T AccessReserved<T>(UnsafeArrayIndex index) where T : unmanaged
internal ref T AccessReserved<T>(UnsafeArrayIndex index) where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
var size = MemoryUtilities.SizeOf<T>();
if (index.index + size > capacity) throw new Exception($"out of bound access, index {index.index} size {size} capacity {capacity}");
#endif
T* buffer = (T*) (_ptr + index.index);
return ref buffer[0];
if (index.index + size > capacity)
throw new Exception($"out of bound access, index {index.index} size {size} capacity {capacity}");
#endif
return ref Unsafe.AsRef<T>(ptr + index.index);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Realloc<T>(uint newCapacity) where T : unmanaged
internal void Realloc(uint newCapacity)
{
unsafe
{
//be sure it's multiple of 4. Assuming that what we write is aligned to 4, then we will always have aligned wrapped heads
newCapacity = Align4(newCapacity);
newCapacity = MemoryUtilities.Align4(newCapacity);
byte* newPointer = null;
#if DEBUG && !PROFILE_SVELTO
#if DEBUG && !PROFILE_SVELTO
if (newCapacity <= capacity)
throw new Exception("new capacity must be bigger than current");
#endif
#endif
if (newCapacity > 0)
{
newPointer = (byte*) MemoryUtilities.Alloc<T>(newCapacity, allocator);
newPointer = (byte*) MemoryUtilities.Alloc(newCapacity, allocator);
if (size > 0)
{
var readerHead = _readIndex % capacity;
@@ -214,8 +217,8 @@ namespace Svelto.ECS.DataStructures
if (readerHead < writerHead)
{
//copy to the new pointer, from th reader position
uint currentSize = _writeIndex - _readIndex;
Unsafe.CopyBlock(newPointer, _ptr + readerHead, currentSize);
var currentSize = _writeIndex - _readIndex;
Unsafe.CopyBlock(newPointer, ptr + readerHead, 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
@@ -224,19 +227,19 @@ namespace Svelto.ECS.DataStructures
{
var byteCountToEnd = capacity - readerHead;

Unsafe.CopyBlock(newPointer, _ptr + readerHead, byteCountToEnd);
Unsafe.CopyBlock(newPointer + byteCountToEnd, _ptr, writerHead);
Unsafe.CopyBlock(newPointer, ptr + readerHead, byteCountToEnd);
Unsafe.CopyBlock(newPointer + byteCountToEnd, ptr, writerHead);
}
}
}

if (_ptr != null)
MemoryUtilities.Free((IntPtr) _ptr, allocator);
if (ptr != null)
MemoryUtilities.Free((IntPtr) ptr, allocator);

_writeIndex = size;
_readIndex = 0;
_ptr = newPointer;
ptr = newPointer;
capacity = newCapacity;
}
}
@@ -246,12 +249,12 @@ namespace Svelto.ECS.DataStructures
{
unsafe
{
if (_ptr != null)
MemoryUtilities.Free((IntPtr) _ptr, allocator);
if (ptr != null)
MemoryUtilities.Free((IntPtr) ptr, allocator);

_ptr = null;
ptr = null;
_writeIndex = 0;
capacity = 0;
capacity = 0;
}
}

@@ -259,18 +262,9 @@ namespace Svelto.ECS.DataStructures
public void Clear()
{
_writeIndex = 0;
_readIndex = 0;
_readIndex = 0;
}

uint Align4(uint input)
{
return (uint)(Math.Ceiling(input / 4.0) * 4);
}
#if UNITY_ECS
[NativeDisableUnsafePtrRestriction]
#endif
unsafe byte* _ptr;
uint _writeIndex, _readIndex;
}
}
}

+ 69
- 0
Svelto.ECS/Debugger/ExclusiveGroupDebugger.cs View File

@@ -0,0 +1,69 @@
using Svelto.ECS;

#if DEBUG
using System;
using System.Collections.Generic;
using System.Reflection;

public static class ExclusiveGroupDebugger
{
static ExclusiveGroupDebugger()
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
Type[] types = assembly.GetTypes();

foreach (Type type in types)
{
if (type != null && type.IsClass && type.IsSealed && type.IsAbstract) //this means only static classes
{
var fields = type.GetFields();
foreach (var field in fields)
{
if (field.IsStatic && typeof(ExclusiveGroup).IsAssignableFrom(field.FieldType))
{
string name = $"{type.FullName}.{field.Name}";
var group = (ExclusiveGroup) field.GetValue(null);
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);
GroupMap.idToName[@group] = name;
}
}
}
}
}
}
public static string ToName(this in ExclusiveGroupStruct group)
{
if (GroupMap.idToName.TryGetValue(group, out var name) == false)
name = $"<undefined:{((uint)group).ToString()}>";

return name;
}
}

public static class GroupMap
{
static GroupMap()
{
GroupMap.idToName = new Dictionary<uint, string>();
}

internal static readonly Dictionary<uint, string> idToName;
}
#else
public static class ExclusiveGroupDebugger
{
public static string ToName(this in ExclusiveGroupStruct group)
{
return ((uint)group).ToString();
}
}
#endif

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

@@ -4,7 +4,7 @@ using Svelto.DataStructures;
namespace Svelto.ECS
{
/// <summary>
/// DynamicEntityDescriptor can be used to add entity views to an existing EntityDescriptor that act as flags,
/// DynamicEntityDescriptor can be used to add entity components to an existing EntityDescriptor that act as flags,
/// at building time.
/// This method allocates, so it shouldn't be abused
/// </summary>
@@ -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<EntityInfoComponentView>
ComponentsToBuild[length] = new ComponentBuilder<EntityInfoViewComponent>
(
new EntityInfoComponentView
new EntityInfoViewComponent
{
componentsToBuild = ComponentsToBuild
}
@@ -80,9 +80,9 @@ namespace Svelto.ECS

//assign it after otherwise the previous copy will overwrite the value in case the item
//is already present
localEntitiesToBuild[index] = new ComponentBuilder<EntityInfoComponentView>
localEntitiesToBuild[index] = new ComponentBuilder<EntityInfoViewComponent>
(
new EntityInfoComponentView
new EntityInfoViewComponent
{
componentsToBuild = localEntitiesToBuild
}
@@ -100,7 +100,7 @@ namespace Svelto.ECS
for (var i = 0; i < length; i++)
{
//the special entity already exists
if (defaultEntities[i].GetEntityComponentType() == EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW)
if (defaultEntities[i].GetEntityComponentType() == ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW)
{
index = i;
break;


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

@@ -9,6 +9,10 @@ namespace Svelto.ECS.Experimental
public static implicit operator T(ECSResources<T> ecsString) { return ResourcesECSDB<T>.FromECS(ecsString.id); }
}
/// <summary>
/// To do. Or we reuse the ID or we need to clear this
/// </summary>
/// <typeparam name="T"></typeparam>
static class ResourcesECSDB<T>
{
static readonly FasterList<T> _resources = new FasterList<T>();


+ 49
- 9
Svelto.ECS/ECSResources/ECSString.cs View File

@@ -1,18 +1,25 @@
using System;
using System.Runtime.InteropServices;

namespace Svelto.ECS.Experimental
{
[Serialization.DoNotSerialize]
[StructLayout(LayoutKind.Explicit)]
///
/// Note: I should extend this to reuse unused id
///
public struct ECSString:IEquatable<ECSString>
{
uint _id;
[FieldOffset(0)] uint _id;
[FieldOffset(4)] uint _versioning;
[FieldOffset(0)] long _realID;

public ECSString(string newText)
public ECSString(string newText):this()
{
_id = ResourcesECSDB<string>.ToECS(newText);
}
ECSString(uint id)
ECSString(uint id):this()
{
_id = id;
}
@@ -21,11 +28,24 @@ namespace Svelto.ECS.Experimental
{
return ResourcesECSDB<string>.FromECS(ecsString._id);
}

/// <summary>
/// Note: Setting null String could be a good way to signal a disposing of the ID so that
/// it can be recycled.
/// Zero id must be a null string
/// </summary>
/// <param name="newText"></param>
public void Set(string newText)
{
if (_id != 0)
ResourcesECSDB<string>.resources(_id) = newText;
{
if (ResourcesECSDB<string>.resources(_id).Equals(newText) == false)
{
ResourcesECSDB<string>.resources(_id) = newText;
_versioning++;
}
}
else
_id = ResourcesECSDB<string>.ToECS(newText);
}
@@ -39,14 +59,34 @@ namespace Svelto.ECS.Experimental
return new ECSString(id);
}

public override string ToString()
{
return ResourcesECSDB<string>.FromECS(_id);
}

public bool Equals(ECSString other)
{
return other._id == _id;
return _realID == other._realID;
}

public override string ToString()
public static bool operator==(ECSString options1, ECSString options2)
{
return ResourcesECSDB<string>.FromECS(_id);
return options1._realID == options2._realID;
}

public static bool operator!=(ECSString options1, ECSString options2)
{
return options1._realID != options2._realID;
}

public override bool Equals(object obj)
{
throw new NotSupportedException(); //this is on purpose
}

public override int GetHashCode()
{
return _realID.GetHashCode();
}
}
}

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

@@ -5,7 +5,6 @@ using System.Runtime.InteropServices;

namespace Svelto.ECS
{
//todo: add debug map
[Serialization.DoNotSerialize]
[Serializable]
[StructLayout(LayoutKind.Explicit)]
@@ -75,7 +74,7 @@ namespace Svelto.ECS

public override string ToString()
{
return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(groupID);
return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(groupID.ToName());
}
}
}

+ 24
- 23
Svelto.ECS/EGIDMapper.cs View File

@@ -1,66 +1,67 @@
using System;
using System.Runtime.CompilerServices;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public readonly struct EGIDMapper<T> where T : struct, IEntityComponent
{
internal readonly ITypeSafeDictionary<T> map;
public uint Length => map.Count;
public ExclusiveGroupStruct groupID { get; }
public uint length => _map.count;
public ExclusiveGroupStruct groupID { get; }

public EGIDMapper(ExclusiveGroupStruct groupStructId, ITypeSafeDictionary<T> dic):this()
public EGIDMapper(ExclusiveGroupStruct groupStructId, ITypeSafeDictionary<T> dic) : this()
{
groupID = groupStructId;
map = dic;
_map = dic;
}

[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()));
if (_map.TryFindIndex(entityID, out var findIndex) == false)
throw new System.Exception("Entity not found in this group ".FastConcat(typeof(T).ToString()));
#else
map.TryFindIndex(entityID, out var findIndex);
_map.TryFindIndex(entityID, out var findIndex);
#endif
return ref map.unsafeValues[(int) findIndex];
return ref _map.GetDirectValueByRef(findIndex);
}
public bool TryGetEntity(uint entityID, out T value)
{
if (map.TryFindIndex(entityID, out var index))
if (_map != null && _map.TryFindIndex(entityID, out var index))
{
value = map.unsafeValues[index];
value = _map.GetDirectValueByRef(index);
return true;
}

value = default;
return false;
}
public T[] GetArrayAndEntityIndex(uint entityID, out uint index)
public IBuffer<T> GetArrayAndEntityIndex(uint entityID, out uint index)
{
if (map.TryFindIndex(entityID, out index))
if (_map.TryFindIndex(entityID, out index))
{
return map.unsafeValues;
return _map.GetValues(out _);
}

throw new ECSException("Entity not found");
}
public bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out T[] array)
public bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out IBuffer<T> array)
{
if (map.TryFindIndex(entityID, out index))
index = default;
if (_map != null && _map.TryFindIndex(entityID, out index))
{
array = map.unsafeValues;
array = _map.GetValues(out _);
return true;
}

array = default;
return false;
}
readonly ITypeSafeDictionary<T> _map;
}
}

}

+ 148
- 0
Svelto.ECS/EnginesRoot.DoubleBufferedEntitiesToAdd.cs View File

@@ -0,0 +1,148 @@
using System;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public partial class EnginesRoot
{
internal class DoubleBufferedEntitiesToAdd
{
const int MaximumNumberOfItemsPerFrameBeforeToClear = 100;

internal void Swap()
{
Swap(ref current, ref other);
Swap(ref currentEntitiesCreatedPerGroup, ref otherEntitiesCreatedPerGroup);
}

void Swap<T>(ref T item1, ref T item2)
{
var toSwap = item2;
item2 = item1;
item1 = toSwap;
}

public void ClearOther()
{
//do not clear the groups created so far, they will be reused, unless they are too many!
var otherCount = other.count;
if (otherCount > MaximumNumberOfItemsPerFrameBeforeToClear)
{
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues;
for (int i = 0; i < otherCount; ++i)
{
var safeDictionariesCount = otherValuesArray[i].count;
ITypeSafeDictionary[] safeDictionaries = otherValuesArray[i].unsafeValues;
{
for (int j = 0; j < safeDictionariesCount; ++j)
{
//clear the dictionary of entities create do far (it won't allocate though)
safeDictionaries[j].Dispose();
}
}
}
otherEntitiesCreatedPerGroup.FastClear();
other.FastClear();
return;
}

{
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues;
for (int i = 0; i < otherCount; ++i)
{
var safeDictionariesCount = otherValuesArray[i].count;
ITypeSafeDictionary[] safeDictionaries = otherValuesArray[i].unsafeValues;
//do not remove the dictionaries of entities per type created so far, they will be reused
if (safeDictionariesCount <= MaximumNumberOfItemsPerFrameBeforeToClear)
{
for (int j = 0; j < safeDictionariesCount; ++j)
{
//clear the dictionary of entities create do far (it won't allocate though)
safeDictionaries[j].FastClear();
}
}
else
{
for (int j = 0; j < safeDictionariesCount; ++j)
{
//clear the dictionary of entities create do far (it won't allocate though)
safeDictionaries[j].Dispose();
}

otherValuesArray[i].FastClear();
}
}

otherEntitiesCreatedPerGroup.FastClear();
}
}

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

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

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

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

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

public DoubleBufferedEntitiesToAdd()
{
currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA;
otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB;

current = _entityComponentsToAddBufferA;
other = _entityComponentsToAddBufferB;
}

public void Dispose()
{
{
var otherValuesArray = other.unsafeValues;
for (int i = 0; i < other.count; ++i)
{
var safeDictionariesCount = otherValuesArray[i].count;
var safeDictionaries = otherValuesArray[i].unsafeValues;
//do not remove the dictionaries of entities per type created so far, they will be reused
for (int j = 0; j < safeDictionariesCount; ++j)
{
//clear the dictionary of entities create do far (it won't allocate though)
safeDictionaries[j].Dispose();
}
}
}
{
var currentValuesArray = current.unsafeValues;
for (int i = 0; i < current.count; ++i)
{
var safeDictionariesCount = currentValuesArray[i].count;
var safeDictionaries = currentValuesArray[i].unsafeValues;
//do not remove the dictionaries of entities per type created so far, they will be reused
for (int j = 0; j < safeDictionariesCount; ++j)
{
//clear the dictionary of entities create do far (it won't allocate though)
safeDictionaries[j].Dispose();
}
}
}
}
}
}
}

+ 0
- 93
Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs View File

@@ -1,93 +0,0 @@
using System;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public partial class EnginesRoot
{
internal class DoubleBufferedEntitiesToAdd
{
const int MaximumNumberOfItemsPerFrameBeforeToClear = 100;

internal void Swap()
{
Swap(ref current, ref other);
Swap(ref currentEntitiesCreatedPerGroup, ref otherEntitiesCreatedPerGroup);
}

void Swap<T>(ref T item1, ref T item2)
{
var toSwap = item2;
item2 = item1;
item1 = toSwap;
}

public void ClearOther()
{
//do not clear the groups created so far, they will be reused, unless they are too many!
var otherCount = other.count;
if (otherCount > MaximumNumberOfItemsPerFrameBeforeToClear)
{
otherEntitiesCreatedPerGroup.FastClear();
other.FastClear();
return;
}
var otherValuesArray = other.unsafeValues;
for (int i = 0; i < otherCount; ++i)
{
var safeDictionariesCount = otherValuesArray[i].count;
var safeDictionaries = otherValuesArray[i].unsafeValues;
//do not remove the dictionaries of entities per type created so far, they will be reused
if (safeDictionariesCount <= MaximumNumberOfItemsPerFrameBeforeToClear)
{
for (int j = 0; j < safeDictionariesCount; ++j)
{
//clear the dictionary of entities create do far (it won't allocate though)
safeDictionaries[j].FastClear();
}
}
else
{
otherValuesArray[i].FastClear();
}
}

otherEntitiesCreatedPerGroup.FastClear();
}

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

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

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

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

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

public DoubleBufferedEntitiesToAdd()
{
currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA;
otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB;

current = _entityComponentsToAddBufferA;
other = _entityComponentsToAddBufferB;
}
}
}
}

+ 63
- 36
Svelto.ECS/EnginesRoot.Engines.cs View File

@@ -7,7 +7,7 @@ using Svelto.ECS.Schedulers;

namespace Svelto.ECS
{
public partial class EnginesRoot
public sealed partial class EnginesRoot
{
public struct EntitiesSubmitter
{
@@ -24,7 +24,7 @@ namespace Svelto.ECS

readonly Svelto.DataStructures.WeakReference<EnginesRoot> _weakReference;
}
public IEntitiesSubmissionScheduler scheduler { get; }

/// <summary>
@@ -37,34 +37,37 @@ namespace Svelto.ECS
/// </summary>
public EnginesRoot(IEntitiesSubmissionScheduler entitiesComponentScheduler)
{
_entitiesOperations = new ThreadSafeDictionary<ulong, EntitySubmitOperation>();
serializationDescriptorMap = new SerializationDescriptorMap();
_reactiveEnginesAddRemove = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>();
_reactiveEnginesSwap = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>();
_enginesSet = new FasterList<IEngine>();
_enginesTypeSet = new HashSet<Type>();
_disposableEngines = new FasterList<IDisposable>();
_entitiesOperations = new ThreadSafeDictionary<ulong, EntitySubmitOperation>();
serializationDescriptorMap = new SerializationDescriptorMap();
_reactiveEnginesAddRemove = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>();
_reactiveEnginesSwap = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>();
_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>>();
_groupEntityComponentsDB =
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>();
_groupsPerEntity = new FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>>();
_groupedEntityToAdd = new DoubleBufferedEntitiesToAdd();

_entitiesStream = new EntitiesStream();
_entitiesDB = new EntitiesDB(_groupEntityComponentsDB, _groupsPerEntity, _entitiesStream);
_entitiesDB = new EntitiesDB(_groupEntityComponentsDB, _groupsPerEntity, _entitiesStream);

scheduler = entitiesComponentScheduler;
scheduler = entitiesComponentScheduler;
scheduler.onTick = new EntitiesSubmitter(this);
#if UNITY_ECS
#if UNITY_BURST
AllocateNativeOperations();
#endif
}
public EnginesRoot(IEntitiesSubmissionScheduler entitiesComponentScheduler, bool isDeserializationOnly):this(entitiesComponentScheduler)

public EnginesRoot
(IEntitiesSubmissionScheduler entitiesComponentScheduler, bool isDeserializationOnly) :
this(entitiesComponentScheduler)
{
_isDeserializationOnly = isDeserializationOnly;
}
/// <summary>
/// Dispose an EngineRoot once not used anymore, so that all the
/// engines are notified with the entities removed.
@@ -74,21 +77,45 @@ namespace Svelto.ECS
{
using (var profiler = new PlatformProfiler("Final Dispose"))
{
foreach (var groups in _groupEntityComponentsDB)
foreach (var engine in _disposableEngines)
{
foreach (var entityList in groups.Value)
try
{
entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler,
new ExclusiveGroupStruct(groups.Key));
engine.Dispose();
}
catch (Exception e)
{
Svelto.Console.LogException(e);
}
}
foreach (FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>.
KeyValuePairFast groups in _groupEntityComponentsDB)
{
foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast entityList in
groups.Value)
try
{
entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler
, new ExclusiveGroupStruct(groups.Key));
}
catch (Exception e)
{
Svelto.Console.LogException(e);
}
}
foreach (FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>.
KeyValuePairFast groups in _groupEntityComponentsDB)
{
foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast entityList in
groups.Value)
entityList.Value.Dispose();
}

_groupEntityComponentsDB.Clear();
_groupsPerEntity.Clear();

foreach (var engine in _disposableEngines)
engine.Dispose();

_disposableEngines.Clear();
_enginesSet.Clear();
_enginesTypeSet.Clear();
@@ -97,13 +124,13 @@ namespace Svelto.ECS

_entitiesOperations.Clear();
_transientEntitiesOperations.Clear();
scheduler.Dispose();
#if DEBUG && !PROFILE_SVELTO
_idCheckers.Clear();
#endif
_groupedEntityToAdd = null;

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

GC.SuppressFinalize(this);
@@ -116,17 +143,16 @@ namespace Svelto.ECS
Dispose();
}


public void AddEngine(IEngine engine)
{
var type = engine.GetType();
var type = engine.GetType();
var refWrapper = new RefWrapper<Type>(type);
DBC.ECS.Check.Require(engine != null, "Engine to add is invalid or null");
DBC.ECS.Check.Require(
_enginesTypeSet.Contains(refWrapper) == false ||
type.ContainsCustomAttribute(typeof(AllowMultipleAttribute)) == true,
"The same engine has been added more than once, if intentional, use [AllowMultiple] class attribute "
.FastConcat(engine.ToString()));
_enginesTypeSet.Contains(refWrapper) == false
|| type.ContainsCustomAttribute(typeof(AllowMultipleAttribute)) == true
, "The same engine has been added more than once, if intentional, use [AllowMultiple] class attribute "
.FastConcat(engine.ToString()));
try
{
if (engine is IReactOnAddAndRemove viewEngine)
@@ -149,7 +175,8 @@ namespace Svelto.ECS
}
catch (Exception e)
{
throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " "), e);
throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " ")
, e);
}
}

@@ -169,8 +196,8 @@ namespace Svelto.ECS
}
}

static void AddEngine<T>(T engine, Type[] entityComponentTypes,
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines)
static void AddEngine<T>
(T engine, Type[] entityComponentTypes, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines)
where T : class, IEngine
{
for (var i = 0; i < entityComponentTypes.Length; i++)


+ 152
- 129
Svelto.ECS/EnginesRoot.Entities.cs View File

@@ -1,14 +1,14 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using DBC.ECS;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
using Svelto.ECS.Schedulers;

namespace Svelto.ECS
{
public partial class EnginesRoot : IDisposable
public partial class EnginesRoot : IDisposable, IUnitTestingInterface
{
///--------------------------------------------
///
@@ -16,25 +16,19 @@ 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,
IEnumerable<object> implementors = null)
EntityComponentInitializer BuildEntity
(EGID entityID, IComponentBuilder[] componentsToBuild, Type implementorType, IEnumerable<object> implementors = null)
{
CheckAddEntityID(entityID);
CheckAddEntityID(entityID, implementorType);
Check.Require(entityID.groupID != 0, "invalid group detected");

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

return new EntityComponentInitializer(entityID, dic);
}
@@ -42,85 +36,95 @@ namespace Svelto.ECS
///--------------------------------------------
void Preallocate<T>(ExclusiveGroupStruct groupID, uint size) where T : IEntityDescriptor, new()
{
var entityComponentsToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild;
var numberOfEntityComponents = entityComponentsToBuild.Length;
using (var profiler = new PlatformProfiler("Preallocate"))
{
var entityComponentsToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild;
var numberOfEntityComponents = entityComponentsToBuild.Length;

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

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

var refWrapper = new RefWrapper<Type>(entityComponentType);
if (group.TryGetValue(refWrapper, out var dbList) == false)
group[refWrapper] = entityComponentBuilder.Preallocate(ref dbList, size);
else
dbList.SetCapacity(size);
var refWrapper = new RefWrapper<Type>(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>();
if (_groupsPerEntity.TryGetValue(refWrapper, out var groupedGroup) == false)
groupedGroup = _groupsPerEntity[refWrapper] = new FasterDictionary<uint, ITypeSafeDictionary>();

groupedGroup[groupID] = dbList;
groupedGroup[groupID] = dbList;
}
}
}

///--------------------------------------------
///
void MoveEntityFromAndToEngines(IComponentBuilder[] entityBuilders, EGID fromEntityGID, EGID? toEntityGID)
void MoveEntityFromAndToEngines(IComponentBuilder[] componentBuilders, EGID fromEntityGID, EGID? toEntityGID)
{
using (var sampler = new PlatformProfiler("Move Entity From Engines"))
{
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>(EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW),
out var entityInfoViewDic) &&
(entityInfoViewDic as ITypeSafeDictionary<EntityInfoComponentView>).TryGetValue(fromEntityGID.entityID,
out var entityInfoView))
MoveEntityComponents(fromEntityGID, toEntityGID, entityInfoView.componentsToBuild, fromGroup, sampler);
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);
//otherwise it's a normal static entity descriptor
else
MoveEntityComponents(fromEntityGID, toEntityGID, entityBuilders, fromGroup, sampler);
MoveEntityComponents(fromEntityGID, toEntityGID, componentBuilders, fromGroup, sampler);
}
}

void MoveEntityComponents(EGID fromEntityGID, EGID? toEntityGID, IComponentBuilder[] entitiesToMove,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup,
PlatformProfiler sampler)
void MoveEntityComponents(EGID fromEntityGID, EGID? toEntityGID, IComponentBuilder[] entitiesToMove
, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup, in PlatformProfiler sampler)
{
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = null;

if (toEntityGID != null)
using (sampler.Sample("MoveEntityComponents"))
{
var toGroupID = toEntityGID.Value.groupID;
var length = entitiesToMove.Length;

toGroup = GetOrCreateGroup(toGroupID);
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = null;

//Add all the entities to the dictionary
for (var i = 0; i < entitiesToMove.Length; i++)
CopyEntityToDictionary(fromEntityGID, toEntityGID.Value, fromGroup, toGroup,
entitiesToMove[i].GetEntityComponentType());
}
if (toEntityGID != null)
{
var toGroupID = toEntityGID.Value.groupID;

toGroup = GetOrCreateGroup(toGroupID, sampler);

//Add all the entities to the dictionary
for (var i = 0; i < length; i++)
CopyEntityToDictionary(fromEntityGID, toEntityGID.Value, fromGroup, toGroup
, entitiesToMove[i].GetEntityComponentType(), sampler);
}

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

//then remove all the entities from the dictionary
for (var i = 0; i < entitiesToMove.Length; i++)
RemoveEntityFromDictionary(fromEntityGID, fromGroup, 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);
}
}

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

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

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

fromTypeSafeDictionary.AddEntityToDictionary(entityGID, toEntityGID, toEntitiesDictionary);
fromTypeSafeDictionary.AddEntityToDictionary(entityGID, toEntityGID, toEntitiesDictionary);
}
}

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

ITypeSafeDictionary toEntitiesDictionary = null;
if (toGroup != null)
toEntitiesDictionary = toGroup[refWrapper]; //this is guaranteed to exist by AddEntityToDictionary
ITypeSafeDictionary toEntitiesDictionary = null;
if (toGroup != null)
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);
#endif
fromTypeSafeDictionary.MoveEntityFromEngines(entityGID, toEntityGID, toEntitiesDictionary,
toEntityGID == null ? _reactiveEnginesAddRemove : _reactiveEnginesSwap, in profiler);
fromTypeSafeDictionary.MoveEntityFromEngines(entityGID, toEntityGID, toEntitiesDictionary
, toEntityGID == null
? _reactiveEnginesAddRemove
: _reactiveEnginesSwap, in profiler);
}
}

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

fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID);

//if (fromTypeSafeDictionary.Count == 0) //clean up
using (sampler.Sample("RemoveEntityFromDictionary"))
{
//todo: this must be unit tested properly
//_groupsPerEntity[refWrapper].Remove(entityGID.groupID);
//I don't remove the group if empty on purpose, in case it needs to be reused
var refWrapper = new RefWrapper<Type>(entityComponentType);
var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper);

fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID);
}
}

@@ -180,54 +187,63 @@ namespace Svelto.ECS
/// <param name="profiler"></param>
void SwapEntitiesBetweenGroups(uint fromIdGroupId, uint toGroupId, in PlatformProfiler profiler)
{
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup = GetGroup(fromIdGroupId);
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = GetOrCreateGroup(toGroupId);

foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast dictionaryOfEntities in
fromGroup)
using (profiler.Sample("SwapEntitiesBetweenGroups"))
{
//call all the MoveTo callbacks
dictionaryOfEntities.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove, dictionaryOfEntities.Value,
new ExclusiveGroupStruct(toGroupId), profiler);
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup = GetGroup(fromIdGroupId);
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = GetOrCreateGroup(toGroupId, profiler);

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

FasterDictionary<uint, ITypeSafeDictionary> groupsOfEntityType =
_groupsPerEntity[dictionaryOfEntities.Key];
ITypeSafeDictionary toEntitiesDictionary =
GetOrCreateTypeSafeDictionary(toGroupId, toGroup, dictionaryOfEntities.Key
, dictionaryOfEntities.Value);

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

ITypeSafeDictionary typeSafeDictionary = groupsOfEntityType[fromIdGroupId];
toEntitiesDictionary.AddEntitiesFromDictionary(typeSafeDictionary, toGroupId);
ITypeSafeDictionary typeSafeDictionary = groupsOfEntityType[fromIdGroupId];
toEntitiesDictionary.AddEntitiesFromDictionary(typeSafeDictionary, toGroupId);

typeSafeDictionary.FastClear();
typeSafeDictionary.FastClear();
}
}
}

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

return fromGroup;
}

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

return toGroup;
}
}

ITypeSafeDictionary GetOrCreateTypeSafeDictionary(uint groupId,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup,
RefWrapper<Type> type, ITypeSafeDictionary fromTypeSafeDictionary)
ITypeSafeDictionary GetOrCreateTypeSafeDictionary
(uint groupId, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, RefWrapper<Type> type
, ITypeSafeDictionary fromTypeSafeDictionary)
{
//be sure that the TypeSafeDictionary for the entity Type exists
if (toGroup.TryGetValue(type, out ITypeSafeDictionary toEntitiesDictionary) ==
false)
if (toGroup.TryGetValue(type, out ITypeSafeDictionary toEntitiesDictionary) == false)
{
toEntitiesDictionary = fromTypeSafeDictionary.Create();
toGroup.Add(type, toEntitiesDictionary);
@@ -235,15 +251,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<uint, ITypeSafeDictionary>();

groupedGroup[groupId] = toEntitiesDictionary;
return toEntitiesDictionary;
}

static ITypeSafeDictionary GetTypeSafeDictionary(uint groupID,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> @group, RefWrapper<Type> refWrapper)
static ITypeSafeDictionary GetTypeSafeDictionary
(uint groupID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> @group, RefWrapper<Type> refWrapper)
{
if (@group.TryGetValue(refWrapper, out ITypeSafeDictionary fromTypeSafeDictionary) == false)
{
@@ -258,10 +273,10 @@ namespace Svelto.ECS
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> dictionariesOfEntities =
_groupEntityComponentsDB[groupID];

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

FasterDictionary<uint, ITypeSafeDictionary> groupsOfEntityType =
@@ -275,7 +290,7 @@ namespace Svelto.ECS
return _entitiesStream.GenerateConsumer<T>(name, capacity);
}

internal Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity)
internal Consumer<T> GenerateConsumer<T>(ExclusiveGroupStruct group, string name, uint capacity)
where T : unmanaged, IEntityComponent
{
return _entitiesStream.GenerateConsumer<T>(group, name, capacity);
@@ -288,7 +303,8 @@ 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>> _groupEntityComponentsDB;
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, 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
@@ -298,5 +314,12 @@ namespace Svelto.ECS

readonly EntitiesDB _entitiesDB;
readonly EntitiesStream _entitiesStream;

EntitiesDB IUnitTestingInterface.entitiesForTesting => _entitiesDB;
}

public interface IUnitTestingInterface
{
EntitiesDB entitiesForTesting { get; }
}
}

+ 17
- 13
Svelto.ECS/EnginesRoot.GenericEntityFactory.cs View File

@@ -1,6 +1,6 @@
using System.Collections.Generic;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
using System;
using System.Collections.Generic;
using Svelto.Common;

namespace Svelto.ECS
{
@@ -10,7 +10,7 @@ namespace Svelto.ECS
{
public GenericEntityFactory(EnginesRoot weakReference)
{
_enginesRoot = new WeakReference<EnginesRoot>(weakReference);
_enginesRoot = new Svelto.DataStructures.WeakReference<EnginesRoot>(weakReference);
}

public EntityComponentInitializer BuildEntity<T>
@@ -19,26 +19,25 @@ namespace Svelto.ECS
{
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId)
, EntityDescriptorTemplate<T>.descriptor.componentsToBuild
, implementors);
, TypeCache<T>.type, implementors);
}

public EntityComponentInitializer BuildEntity<T>(EGID egid, IEnumerable<object> implementors = null)
where T : IEntityDescriptor, new()
{
return _enginesRoot.Target.BuildEntity(
egid, EntityDescriptorTemplate<T>.descriptor.componentsToBuild
, implementors);
egid, EntityDescriptorTemplate<T>.descriptor.componentsToBuild, TypeCache<T>.type, implementors);
}

public EntityComponentInitializer BuildEntity<T>
(EGID egid, T entityDescriptor, IEnumerable<object> implementors) where T : IEntityDescriptor
{
return _enginesRoot.Target.BuildEntity(egid, entityDescriptor.componentsToBuild, implementors);
return _enginesRoot.Target.BuildEntity(egid, entityDescriptor.componentsToBuild, TypeCache<T>.type, implementors);
}
#if UNITY_ECS
public NativeEntityFactory ToNative<T>(Unity.Collections.Allocator allocator) where T : IEntityDescriptor, new()
#if UNITY_BURST
public NativeEntityFactory ToNative<T>(string memberName) where T : IEntityDescriptor, new()
{
return _enginesRoot.Target.ProvideNativeEntityFactoryQueue<T>();
return _enginesRoot.Target.ProvideNativeEntityFactoryQueue<T>(memberName);
}
#endif
public EntityComponentInitializer BuildEntity<T>
@@ -46,7 +45,7 @@ namespace Svelto.ECS
where T : IEntityDescriptor
{
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId)
, descriptorEntity.componentsToBuild, implementors);
, descriptorEntity.componentsToBuild, TypeCache<T>.type, implementors);
}

public void PreallocateEntitySpace<T>(ExclusiveGroupStruct groupStructId, uint size)
@@ -54,10 +53,15 @@ namespace Svelto.ECS
{
_enginesRoot.Target.Preallocate<T>(groupStructId, size);
}
public EntityComponentInitializer BuildEntity(EGID egid, IComponentBuilder[] componentsToBuild, Type type, IEnumerable<object> implementors = null)
{
return _enginesRoot.Target.BuildEntity(egid, componentsToBuild, type, implementors);
}

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

+ 17
- 12
Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs View File

@@ -1,6 +1,6 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.CompilerServices;
using Svelto.Common;

namespace Svelto.ECS
{
@@ -27,7 +27,8 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntity<T>(EGID entityEGID) where T : IEntityDescriptor, new()
{
_enginesRoot.Target.CheckRemoveEntityID(entityEGID);
DBC.ECS.Check.Require(entityEGID.groupID != 0, "invalid group detected");
_enginesRoot.Target.CheckRemoveEntityID(entityEGID, TypeCache<T>.type);

_enginesRoot.Target.QueueEntitySubmitOperation<T>(
new EntitySubmitOperation(EntitySubmitOperationType.Remove, entityEGID, entityEGID,
@@ -50,6 +51,7 @@ namespace Svelto.ECS
public void RemoveGroupAndEntities(ExclusiveGroupStruct groupID)
{
_enginesRoot.Target.RemoveGroupID(groupID);
DBC.ECS.Check.Require(groupID != 0, "invalid group detected");

_enginesRoot.Target.QueueEntitySubmitOperation(
new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, new EGID(0, groupID), new EGID()));
@@ -103,15 +105,15 @@ namespace Svelto.ECS
SwapEntityGroup<T>(fromID, toID);
}

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

@@ -119,8 +121,11 @@ namespace Svelto.ECS
public void SwapEntityGroup<T>(EGID fromID, EGID toID)
where T : IEntityDescriptor, new()
{
_enginesRoot.Target.CheckRemoveEntityID(fromID);
_enginesRoot.Target.CheckAddEntityID(toID);
DBC.ECS.Check.Require(fromID.groupID != 0, "invalid group detected");
DBC.ECS.Check.Require(toID.groupID != 0, "invalid group detected");
_enginesRoot.Target.CheckRemoveEntityID(fromID, TypeCache<T>.type);
_enginesRoot.Target.CheckAddEntityID(toID, TypeCache<T>.type);

_enginesRoot.Target.QueueEntitySubmitOperation<T>(
new EntitySubmitOperation(EntitySubmitOperationType.Swap,
@@ -135,7 +140,7 @@ namespace Svelto.ECS
void QueueEntitySubmitOperation(EntitySubmitOperation entitySubmitOperation)
{
#if DEBUG && !PROFILE_SVELTO
entitySubmitOperation.trace = new StackFrame(1, true);
entitySubmitOperation.trace = new System.Diagnostics.StackFrame(1, true);
#endif
_entitiesOperations.Add((ulong) entitySubmitOperation.fromID, entitySubmitOperation);
}
@@ -143,7 +148,7 @@ namespace Svelto.ECS
void QueueEntitySubmitOperation<T>(EntitySubmitOperation entitySubmitOperation) where T : IEntityDescriptor
{
#if DEBUG && !PROFILE_SVELTO
entitySubmitOperation.trace = new StackFrame(1, true);
entitySubmitOperation.trace = new System.Diagnostics.StackFrame(1, true);

if (_entitiesOperations.TryGetValue((ulong) entitySubmitOperation.fromID, out var entitySubmitedOperation))
{


+ 12
- 10
Svelto.ECS/EnginesRoot.Submission.cs View File

@@ -29,10 +29,9 @@ namespace Svelto.ECS

void SingleSubmission(in PlatformProfiler profiler)
{
#if UNITY_ECS
#if UNITY_BURST
NativeOperationSubmission(profiler);
#endif
if (_entitiesOperations.Count > 0)
{
using (profiler.Sample("Remove and Swap operations"))
@@ -66,16 +65,19 @@ namespace Svelto.ECS
break;
}
}
catch (Exception e)
catch
{
var str = "Crash while executing Entity Operation "
.FastConcat(entitiesOperations[i].type.ToString());

throw new ECSException(str.FastConcat(" ")
Svelto.Console.LogError(str.FastConcat(" ")
#if DEBUG && !PROFILE_SVELTO
.FastConcat(entitiesOperations[i].trace.ToString())
.FastConcat(entitiesOperations[i].trace.ToString())
#endif
, e);
);

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

//add the entityComponents in the group
foreach (var entityComponentsToSubmit in _groupedEntityToAdd.other[groupID])
@@ -124,13 +126,13 @@ namespace Svelto.ECS
ITypeSafeDictionary dbDic = GetOrCreateTypeSafeDictionary(groupID, groupDB, wrapper,
targetTypeSafeDictionary);

//Fill the DB with the entity views generate this frame.
//Fill the DB with the entity components generate this frame.
dbDic.AddEntitiesFromDictionary(targetTypeSafeDictionary, groupID);
}
}
}

//then submit everything in the engines, so that the DB is up to date with all the entity views and struct
//then submit everything in the engines, so that the DB is up to date with all the entity components
//created by the entity built
using (profiler.Sample("Add entities to engines"))
{


+ 126
- 0
Svelto.ECS/EntitiesDB.FindGroups.cs View File

@@ -0,0 +1,126 @@
using System;
using System.Threading;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

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)
return result;
var result1Count = result1.count;
var fasterDictionaryNodes1 = result1.unsafeKeys;
for (int j = 0; j < result1Count; j++)
{
result.Add(new ExclusiveGroupStruct(fasterDictionaryNodes1[j].key));
}
return result;
}
public LocalFasterReadOnlyList<ExclusiveGroupStruct> FindGroups<T1, T2>() where T1 : IEntityComponent where T2 : IEntityComponent
{
FasterList<ExclusiveGroupStruct> result = groups.Value;
result.FastClear();
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T1>.wrapper
, out FasterDictionary<uint, ITypeSafeDictionary> result1) == false)
return result;
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T2>.wrapper
, out FasterDictionary<uint, ITypeSafeDictionary> result2) == false)
return result;
var result1Count = result1.count;
var result2Count = result2.count;
var fasterDictionaryNodes1 = result1.unsafeKeys;
var fasterDictionaryNodes2 = result2.unsafeKeys;

for (int i = 0; i < result1Count; i++)
{
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)
{
result.Add(new ExclusiveGroupStruct(fasterDictionaryNodes1[i].key));
break;
}
}
}

return result;
}

/// <summary>
/// Remember that this operation os O(N*M*P) where N,M,P are the number of groups where each component
/// is found.
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <typeparam name="T3"></typeparam>
/// <returns></returns>
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)
return result;
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T3>.wrapper
, out FasterDictionary<uint, ITypeSafeDictionary> result3) == 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 (fasterDictionaryNodes3[j].key == result[i])
break;

result.UnorderedRemoveAt(i);
i--;
}

return result;
}
struct GroupsList
{
static GroupsList()
{
groups = new FasterList<ExclusiveGroupStruct>();
}

static readonly FasterList<ExclusiveGroupStruct> groups;

public static implicit operator FasterList<ExclusiveGroupStruct>(in GroupsList list)
{
return list.reference;
}

FasterList<ExclusiveGroupStruct> reference => groups;
}
static readonly ThreadLocal<GroupsList> groups = new ThreadLocal<GroupsList>();
}
}

+ 123
- 174
Svelto.ECS/EntitiesDB.cs View File

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

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T QueryUniqueEntity<T>(ExclusiveGroupStruct group) where T : struct, IEntityComponent
{
var entities = QueryEntities<T>(group).ToFastAccess(out var count);
var entities = QueryEntities<T>(group);

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T QueryEntity<T>(EGID entityGID) where T : struct, IEntityComponent
{
T[] array;
if ((array = QueryEntitiesAndIndexInternal<T>(entityGID, out var index)) != null)
return ref array[(int) index];

throw new EntityNotFoundException(entityGID, typeof(T));
}

public ref T QueryEntity<T>(uint id, ExclusiveGroupStruct group) where T : struct, IEntityComponent
{
return ref QueryEntity<T>(new EGID(id, group));
}

/// <summary>
/// The QueryEntities<T> follows the rule that entities could always be iterated regardless if they
/// are 0, 1 or N. In case of 0 it returns an empty array. This allows to use the same for iteration
@@ -63,105 +49,119 @@ namespace Svelto.ECS
public EntityCollection<T> QueryEntities<T>(ExclusiveGroupStruct groupStructId)
where T : struct, IEntityComponent
{
T[] ret;
uint count = 0;
//object sentinel = default;
IBuffer<T> buffer;
uint count = 0;
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false)
ret = RetrieveEmptyEntityComponentArray<T>();
buffer = RetrieveEmptyEntityComponentArray<T>();
else
{
var safeDictionary = (typeSafeDictionary as ITypeSafeDictionary<T>);
ret = safeDictionary.GetValuesArray(out count);
// sentinel = safeDictionary.GenerateSentinel();
buffer = safeDictionary.GetValues(out count);
}

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

public EntityCollection<T1, T2> QueryEntities<T1, T2>(
ExclusiveGroupStruct groupStruct)
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 DEBUG && !PROFILE_SVELTO
if (T1entities.count != T2entities.count)
throw new ECSException("Entity views 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("'")));
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

return new EntityCollection<T1, T2>(T1entities, T2entities);
}

public EntityCollection<T1, T2, T3>
QueryEntities<T1, T2, T3>(ExclusiveGroupStruct groupStruct)
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 DEBUG && !PROFILE_SVELTO
if (T1entities.count != T2entities.count || T2entities.count != T3entities.count)
throw new ECSException("Entity views 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)));
return new EntityCollection<T1, T2, T3>(T1entities,
T2entities, T3entities);
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 new EntityCollection<T1, T2, T3>(T1entities, T2entities, T3entities);
}
public int IterateOverGroupsAndCount<T>
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T : struct, IEntityComponent
{
int count = 0;
for (int i = 0; i < groups.count; i++)
{
count += Count<T>(groups[i]);
}

public EntityCollections<T> QueryEntities<T>(ExclusiveGroup[] groups) where T : struct, IEntityComponent
return 0;
}

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

public EntityCollections<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup[] groups)
public TupleRef<T1, T2> QueryEntities<T1, T2>(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
{
return new EntityCollections<T1, T2>(this, groups);
return new TupleRef<T1, T2>(new EntityCollections<T1, T2>(this, groups)
, new GroupsEnumerable<T1, T2>(this, groups));
}
public EntityCollections<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroup[] groups)
public TupleRef<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 EntityCollections<T1, T2, T3>(this, groups);
return new TupleRef<T1, T2, T3>(new EntityCollections<T1, T2, T3>(this, groups)
, new GroupsEnumerable<T1, T2, T3>(this, groups));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroupStruct groupStructId)
where T : struct, IEntityComponent
public TupleRef<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
{
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false)
throw new EntityGroupNotFoundException(typeof(T));

return (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId);
return new TupleRef<T1, T2, T3, T4>(new EntityCollections<T1, T2, T3, T4>(this, groups)
, new GroupsEnumerable<T1, T2, T3, T4>(this, groups));
}

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

return (typeSafeDictionary as TypeSafeDictionary<T>).ToNativeEGIDMapper<T>(groupStructId);
return (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryQueryMappedEntities<T>(ExclusiveGroupStruct groupStructId,
out EGIDMapper<T> mapper)
where T : struct, IEntityComponent
public bool TryQueryMappedEntities<T>
(ExclusiveGroupStruct groupStructId, out EGIDMapper<T> mapper) where T : struct, IEntityComponent
{
mapper = default;
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false ||
typeSafeDictionary.Count == 0)
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false
|| typeSafeDictionary.count == 0)
return false;

mapper = (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId);
@@ -169,60 +169,11 @@ namespace Svelto.ECS
return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryQueryNativeMappedEntities<T>(ExclusiveGroupStruct groupStructId,
out NativeEGIDMapper<T> mapper)
where T : unmanaged, IEntityComponent
{
mapper = default;
if (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 T[] QueryEntitiesAndIndex<T>(EGID entityGID, out uint index) where T : struct, IEntityComponent
{
T[] array;
if ((array = QueryEntitiesAndIndexInternal<T>(entityGID, out index)) != null)
return array;

throw new EntityNotFoundException(entityGID, typeof(T));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryQueryEntitiesAndIndex<T>(EGID entityGid, out uint index, out T[] array)
where T : struct, IEntityComponent
{
if ((array = QueryEntitiesAndIndexInternal<T>(entityGid, out index)) != null)
return true;

return false;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] QueryEntitiesAndIndex<T>(uint id, ExclusiveGroupStruct @group, out uint index)
where T : struct, IEntityComponent
{
return QueryEntitiesAndIndex<T>(new EGID(id, @group), out index);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryQueryEntitiesAndIndex
<T>(uint id, ExclusiveGroupStruct group, out uint index, out T[] array)
where T : struct, IEntityComponent
{
return TryQueryEntitiesAndIndex(new EGID(id, @group), out index, out array);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists<T>(EGID entityGID) where T : struct, IEntityComponent
{
if (SafeQueryEntityDictionary<T>(entityGID.groupID, out var casted) == false) return false;
if (SafeQueryEntityDictionary<T>(entityGID.groupID, out var casted) == false)
return false;

return casted != null && casted.ContainsKey(entityGID.entityID);
}
@@ -230,7 +181,8 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists<T>(uint id, ExclusiveGroupStruct group) where T : struct, IEntityComponent
{
if (SafeQueryEntityDictionary<T>(group, out var casted) == false) return false;
if (SafeQueryEntityDictionary<T>(group, out var casted) == false)
return false;

return casted != null && casted.ContainsKey(id);
}
@@ -238,8 +190,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<RefWrapper<Type>, ITypeSafeDictionary> group) == true)
{
return group.count > 0;
}
@@ -250,73 +202,59 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasAny<T>(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent
{
return QueryEntities<T>(groupStruct).count > 0;
return Count<T>(groupStruct) > 0;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint Count<T>(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent
public int Count<T>(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent
{
return QueryEntities<T>(groupStruct).count;
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
{
_entityStream.PublishEntity(ref QueryEntity<T>(egid), egid);
_entityStream.PublishEntity(ref this.QueryEntity<T>(egid), egid);
}

[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)
{
var entities = (pair.Value as ITypeSafeDictionary<T>).GetValuesArray(out var count);
IBuffer<T> entities = (pair.Value as ITypeSafeDictionary<T>).GetValues(out var count);

if (count > 0)
action(entities, new ExclusiveGroupStruct(pair.Key), count, this);
}
}

[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
{
if (_groupsPerEntity.TryGetValue(TypeRefWrapper<T>.wrapper, out var dic))
foreach (var pair in dic)
{
var entities = (pair.Value as ITypeSafeDictionary<T>).GetValuesArray(out var innerCount);
IBuffer<T> entities = (pair.Value as ITypeSafeDictionary<T>).GetValues(out var innerCount);

if (innerCount > 0)
action(entities, new ExclusiveGroupStruct(pair.Key), innerCount, this,
ref value);
action(entities, new ExclusiveGroupStruct(pair.Key), innerCount, this, ref value);
}
}

public QueryGroups CreateQueryGroup<T>() where T : IEntityComponent
{
return new QueryGroups(FindGroups<T>());
}

public bool FoundInGroups<T1>() where T1 : IEntityComponent
{
return _groupsPerEntity.ContainsKey(TypeRefWrapper<T1>.wrapper);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
T[] QueryEntitiesAndIndexInternal<T>(EGID entityGID, out uint index) where T : struct, IEntityComponent
{
index = 0;
if (SafeQueryEntityDictionary<T>(entityGID.groupID, out var safeDictionary) == false)
return null;

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

return (safeDictionary as ITypeSafeDictionary<T>).unsafeValues;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
bool SafeQueryEntityDictionary<T>(uint group, out ITypeSafeDictionary typeSafeDictionary)
where T : struct, IEntityComponent
internal bool SafeQueryEntityDictionary<T>(uint group, out ITypeSafeDictionary typeSafeDictionary)
where T : IEntityComponent
{
if (UnsafeQueryEntityDictionary(group, TypeCache<T>.type, out var safeDictionary) == false)
{
@@ -345,32 +283,43 @@ namespace Svelto.ECS
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
static T[] RetrieveEmptyEntityComponentArray<T>()
static IBuffer<T> RetrieveEmptyEntityComponentArray<T>() where T : struct, IEntityComponent
{
return EmptyList<T>.emptyArray;
}

static class EmptyList<T>
static class EmptyList<T> where T : struct, IEntityComponent
{
internal static readonly T[] emptyArray = new T[0];
}
internal static readonly IBuffer<T> emptyArray;

internal FasterDictionary<uint, ITypeSafeDictionary> FindGroups<T1>() where T1 : IEntityComponent
{
if (_groupsPerEntity.ContainsKey(TypeRefWrapper<T1>.wrapper) == false)
return _emptyDictionary;
return _groupsPerEntity[TypeRefWrapper<T1>.wrapper];
static EmptyList()
{
if (ComponentBuilder<T>.IS_ENTITY_VIEW_COMPONENT)
{
MB<T> b = default;

emptyArray = b;
}
else
{
NB<T> b = default;

emptyArray = b;
}
}
}

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

readonly EntitiesStream _entityStream;

//grouped set of entity views, this is the standard way to handle entity views entity views are grouped per
//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 views 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;
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


+ 144
- 336
Svelto.ECS/EntityCollection.cs View File

@@ -1,69 +1,68 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public struct EntityCollection<T> where T : IEntityComponent
public readonly ref struct EntityCollection<T> where T : struct, IEntityComponent
{
public EntityCollection(T[] array, uint count) : this()
{
_buffer.Set(array, count);
_count = count;
}

public EntityCollection(MB<T> buffer, uint count)
static readonly bool IsUnmanaged = TypeSafeDictionary<T>._isUmanaged;
public EntityCollection(IBuffer<T> buffer, uint count):this()
{
_buffer = buffer;
if (IsUnmanaged)
_nativedBuffer = (NB<T>) buffer;
else
_managedBuffer = (MB<T>) buffer;
_count = count;
_buffer = buffer;
}

public uint count => _count;

readonly MB<T> _buffer;
readonly uint _count;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] ToFastAccess(out uint actualCount)
{
actualCount = _count;
return _buffer.ToManagedArray();
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NB<NT> ToNativeBuffer<NT>() where NT : unmanaged, T
{
return new NB<NT>(_buffer.Pin(), _count, _buffer.capacity);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MB<T> ToBuffer()
{
return _buffer;
}
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 => ref _buffer[i];
get
{
if (IsUnmanaged)
return ref _nativedBuffer[i];
else
return ref _managedBuffer[i];
}
}

public ref T this[int i]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _buffer[i];
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); }

public struct EntityIterator
//todo to remove
public ref struct EntityIterator
{
public EntityIterator(MB<T> array, uint count) : this()
public EntityIterator(IBuffer<T> array, uint count) : this()
{
_array = array.ToManagedArray();
_array = array;
_count = count;
_index = -1;
}
@@ -77,14 +76,13 @@ namespace Svelto.ECS
get => ref _array[_index];
}

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

public struct EntityCollection<T1, T2>
where T1 : IEntityComponent where T2 : IEntityComponent
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)
{
@@ -93,13 +91,14 @@ namespace Svelto.ECS
}

public uint count => _array1.count;

//todo to remove
public EntityCollection<T2> Item2
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array2;
}
//todo to remove
public EntityCollection<T1> Item1
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -108,55 +107,23 @@ namespace Svelto.ECS

readonly EntityCollection<T1> _array1;
readonly EntityCollection<T2> _array2;

public (T1[], T2[]) ToFastAccess(out uint count)
{
count = this.count;

return (_array1.ToFastAccess(out _), _array2.ToFastAccess(out _));
}

public BT<MB<T1>, MB<T2>> ToBuffers()
{
var bufferTuple = new BT<MB<T1>, MB<T2>>
(_array1.ToBuffer(), _array2.ToBuffer(), count);
return bufferTuple;
}

public BT<NB<NT1>, NB<NT2>> ToNativeBuffers<NT1, NT2>()
where NT2 : unmanaged, T2 where NT1 : unmanaged, T1
{
var bufferTuple = new BT<NB<NT1>, NB<NT2>>
(_array1.ToNativeBuffer<NT1>(), _array2.ToNativeBuffer<NT2>(), count);

return bufferTuple;
}

//todo to remove
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityIterator GetEnumerator()
{
return new EntityIterator(this);
}

public struct EntityIterator
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;
_count = array1.count;
_index = -1;
}

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

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

public ValueRef<T1, T2> Current
{
@@ -165,35 +132,36 @@ namespace Svelto.ECS
}

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

}

public struct EntityCollection<T1, T2, T3>
where T3 : IEntityComponent where T2 : IEntityComponent where T1 : IEntityComponent
public readonly ref struct EntityCollection<T1, T2, T3> where T3 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T1 : struct, IEntityComponent
{
public EntityCollection(
in EntityCollection<T1> array1, in EntityCollection<T2> array2,
in EntityCollection<T3> array3)
public 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
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array1;
}
//todo to remove
public EntityCollection<T2> Item2
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array2;
}
//todo to remove
public EntityCollection<T3> Item3
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -202,241 +170,69 @@ namespace Svelto.ECS

public uint count => Item1.count;

public (T1[], T2[], T3[]) ToFastAccess(out uint count)
{
count = this.count;

return (_array1.ToFastAccess(out _), _array2.ToFastAccess(out _), _array3.ToFastAccess(out _));
}

public BT<MB<T1>, MB<T2>, MB<T3>> ToBuffers()
{
var bufferTuple = new BT<MB<T1>, MB<T2>, MB<T3>>
(_array1.ToBuffer(), _array2.ToBuffer(), _array3.ToBuffer(), count);
return bufferTuple;
}

public BT<NB<NT1>, NB<NT2>, NB<NT3>> ToNativeBuffers<NT1, NT2, NT3>()
where NT2 : unmanaged, T2 where NT1 : unmanaged, T1 where NT3 : unmanaged, T3
{
var bufferTuple = new BT<NB<NT1>, NB<NT2>, NB<NT3>>
(_array1.ToNativeBuffer<NT1>(), _array2.ToNativeBuffer<NT2>(), _array3.ToNativeBuffer<NT3>(), count);

return bufferTuple;
}

readonly EntityCollection<T1> _array1;
readonly EntityCollection<T2> _array2;
readonly EntityCollection<T3> _array3;
}

public struct EntityCollections<T> where T : struct, IEntityComponent
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 EntityCollections(EntitiesDB db, ExclusiveGroup[] groups) : this()
public EntityCollection
(in EntityCollection<T1> array1, in EntityCollection<T2> array2, in EntityCollection<T3> array3, in EntityCollection<T4> array4)
{
_db = db;
_groups = groups;
_array1 = array1;
_array2 = array2;
_array3 = array3;
_array4 = array4;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityGroupsIterator GetEnumerator()
//todo to remove
public EntityCollection<T1> Item1
{
return new EntityGroupsIterator(_db, _groups);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array1;
}

readonly EntitiesDB _db;
readonly ExclusiveGroup[] _groups;

public struct EntityGroupsIterator
//todo to remove
public EntityCollection<T2> Item2
{
public EntityGroupsIterator(EntitiesDB db, ExclusiveGroup[] 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.Length)
{
_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 ExclusiveGroup[] _groups;

EntityCollection<T> _array;
uint _count;
int _index;
int _indexGroup;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array2;
}
}

public struct EntityCollections<T1, T2>
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
{
public EntityCollections(EntitiesDB db, ExclusiveGroup[] groups) : this()
//todo to remove
public EntityCollection<T3> Item3
{
_db = db;
_groups = groups;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array3;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityGroupsIterator GetEnumerator()
//todo to remove
public EntityCollection<T4> Item4
{
return new EntityGroupsIterator(_db, _groups);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array4;
}

readonly EntitiesDB _db;
readonly ExclusiveGroup[] _groups;

public struct EntityGroupsIterator
{
public EntityGroupsIterator(EntitiesDB db, ExclusiveGroup[] 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.Length)
{
_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 ExclusiveGroup[] _groups;
int _index;
int _indexGroup;
public uint count => _array1.count;

EntityCollection<T1, T2> _array1;
}
readonly EntityCollection<T1> _array1;
readonly EntityCollection<T2> _array2;
readonly EntityCollection<T3> _array3;
readonly EntityCollection<T4> _array4;
}
public struct EntityCollections<T1, T2, T3>
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent
{
public EntityCollections(EntitiesDB db, ExclusiveGroup[] groups) : this()
{
_db = db;
_groups = groups;
}

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

readonly EntitiesDB _db;
readonly ExclusiveGroup[] _groups;

public struct EntityGroupsIterator
{
public EntityGroupsIterator(EntitiesDB db, ExclusiveGroup[] 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.Length)
{
_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 ExclusiveGroup[] _groups;
uint _count;
int _index;
int _indexGroup;

EntityCollection<T1, T2, T3> _array1;
}
}
public readonly struct BT<BufferT1, BufferT2, BufferT3, BufferT4> : IDisposable where BufferT1 : IDisposable
where BufferT2 : IDisposable
where BufferT3 : IDisposable
where BufferT4 : IDisposable
public readonly struct BT<BufferT1, BufferT2, BufferT3, BufferT4>
{
public readonly BufferT1 buffer1;
public readonly BufferT2 buffer2;
public readonly BufferT3 buffer3;
public readonly BufferT4 buffer4;
public readonly uint count;
public readonly int count;

public BT(BufferT1 bufferT1, BufferT2 bufferT2, BufferT3 bufferT3, BufferT4 bufferT4, uint count) : this()
{
@@ -444,65 +240,76 @@ namespace Svelto.ECS
this.buffer2 = bufferT2;
this.buffer3 = bufferT3;
this.buffer4 = bufferT4;
this.count = count;
}

public void Dispose()
{
buffer1.Dispose();
buffer2.Dispose();
buffer3.Dispose();
buffer4.Dispose();
this.count = (int) count;
}
}

public readonly struct BT<BufferT1, BufferT2, BufferT3> : IDisposable where BufferT1 : IDisposable
where BufferT2 : IDisposable
where BufferT3 : IDisposable
public readonly struct BT<BufferT1, BufferT2, BufferT3>
{
public readonly BufferT1 buffer1;
public readonly BufferT2 buffer2;
public readonly BufferT3 buffer3;
public readonly uint count;
public readonly int count;

public BT(BufferT1 bufferT1, BufferT2 bufferT2, BufferT3 bufferT3, uint count) : this()
{
this.buffer1 = bufferT1;
this.buffer2 = bufferT2;
this.buffer3 = bufferT3;
this.count = count;
this.count = (int) count;
}

public void Dispose()
public void Deconstruct(out BufferT1 bufferT1, out BufferT2 bufferT2, out BufferT3 bufferT3, out int count)
{
bufferT1 = buffer1;
bufferT2 = buffer2;
bufferT3 = buffer3;
count = this.count;
}
}

public readonly struct BT<BufferT1>
{
public readonly BufferT1 buffer;
public readonly int count;

public BT(BufferT1 bufferT1, uint count) : this()
{
this.buffer = bufferT1;
this.count = (int) count;
}
public void Deconstruct(out BufferT1 bufferT1, out int count)
{
buffer1.Dispose();
buffer2.Dispose();
buffer3.Dispose();
bufferT1 = buffer;
count = this.count;
}
public static implicit operator BufferT1(BT<BufferT1> t) => t.buffer;
}

public readonly struct BT<BufferT1, BufferT2> : IDisposable
where BufferT1 : IDisposable where BufferT2 : IDisposable
public readonly struct BT<BufferT1, BufferT2>
{
public readonly BufferT1 buffer1;
public readonly BufferT2 buffer2;
public readonly uint count;
public readonly int count;

public BT(BufferT1 bufferT1, BufferT2 bufferT2, uint count) : this()
{
this.buffer1 = bufferT1;
this.buffer2 = bufferT2;
this.count = count;
this.count = (int) count;
}
public void Dispose()
public void Deconstruct(out BufferT1 bufferT1, out BufferT2 bufferT2, out int count)
{
buffer1.Dispose();
buffer2.Dispose();
bufferT1 = buffer1;
bufferT2 = buffer2;
count = this.count;
}
}

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

@@ -511,7 +318,7 @@ namespace Svelto.ECS
public ValueRef(in EntityCollection<T1, T2> entity2, uint i)
{
array1 = entity2;
index = i;
index = i;
}

public ref T1 entityComponentA
@@ -527,8 +334,9 @@ namespace Svelto.ECS
}
}

public ref struct ValueRef<T1, T2, T3>
where T2 : IEntityComponent where T1 : IEntityComponent where T3 : IEntityComponent
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;

@@ -551,11 +359,11 @@ namespace Svelto.ECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref array1.Item2[index];
}
public ref T3 entityComponentC
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref array1.Item3[index];
}
}
}
}

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

@@ -0,0 +1,266 @@
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;
}
}
}

+ 3
- 1
Svelto.ECS/EntityComponentInitializer.cs View File

@@ -12,6 +12,8 @@ namespace Svelto.ECS
_ID = id;
}

public EGID EGID => _ID;

public void Init<T>(T initializer) where T : struct, IEntityComponent
{
if (_group.TryGetValue(new RefWrapper<Type>(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE),
@@ -23,7 +25,7 @@ namespace Svelto.ECS
SetEGIDWithoutBoxing<T>.SetIDWithoutBoxing(ref initializer, _ID);

if (dictionary.TryFindIndex(_ID.entityID, out var findElementIndex))
dictionary.unsafeValues[findElementIndex] = initializer;
dictionary.GetDirectValueByRef(findElementIndex) = initializer;
}

public ref T GetOrCreate<T>() where T : struct, IEntityComponent


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

@@ -37,16 +37,16 @@ namespace Svelto.ECS.Internal

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

#if DEBUG && !PROFILE_SVELTO
HashSet<Type> types = new HashSet<Type>();
#endif
var count = entityBuilders.Length;
#if DEBUG && !PROFILE_SVELTO

for (var index = 0; index < count; ++index)
{
var entityComponentType = entityBuilders[index].GetEntityComponentType();
var entityComponentType = componentBuilders[index].GetEntityComponentType();
if (types.Contains(entityComponentType))
{
throw new ECSException("EntityBuilders must be unique inside an EntityDescriptor");
@@ -57,7 +57,7 @@ namespace Svelto.ECS.Internal
#endif
for (var index = 0; index < count; ++index)
{
var entityComponentBuilder = entityBuilders[index];
var entityComponentBuilder = componentBuilders[index];
var entityComponentType = entityComponentBuilder.GetEntityComponentType();

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


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

@@ -0,0 +1,11 @@
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 EntityInfoComponentView: IEntityComponent
struct EntityInfoViewComponent: IEntityComponent
{
public IComponentBuilder[] componentsToBuild;
}

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

@@ -5,7 +5,7 @@ namespace Svelto.ECS
public class EntityNotFoundException : Exception
{
public EntityNotFoundException(EGID entityEGID, Type entityType) : base(
$"entity of type '{entityType}' with ID '{entityEGID.entityID}', group '{(uint) entityEGID.groupID}' not found!")
$"entity of type '{entityType}' with ID '{entityEGID.entityID}', group '{entityEGID.groupID.ToName()}' not found!")
{
}
}

+ 9
- 10
Svelto.ECS/EntityStream.cs View File

@@ -5,12 +5,11 @@ using Svelto.DataStructures;
namespace Svelto.ECS
{
/// <summary>
/// Do not use this class in place of a normal polling.
/// I eventually realised than in ECS 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, so EntityStreams must be used
/// only if:
/// - you want to polling engine to be able to track all the entity changes happening in between polls and not
/// just the current state
/// 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
@@ -26,7 +25,7 @@ namespace Svelto.ECS
return (_streams[TypeRefWrapper<T>.wrapper] as EntityStream<T>).GenerateConsumer(name, capacity);
}

public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity)
public Consumer<T> GenerateConsumer<T>(ExclusiveGroupStruct group, string name, uint capacity)
where T : unmanaged, IEntityComponent
{
if (_streams.ContainsKey(TypeRefWrapper<T>.wrapper) == false)
@@ -107,7 +106,7 @@ namespace Svelto.ECS
return consumer;
}

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

@@ -140,7 +139,7 @@ namespace Svelto.ECS
}
}

internal Consumer(ExclusiveGroup group, string name, uint capacity) : this(name, capacity)
internal Consumer(ExclusiveGroupStruct group, string name, uint capacity) : this(name, capacity)
{
this.@group = @group;
hasGroup = true;
@@ -195,7 +194,7 @@ namespace Svelto.ECS

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

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



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

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

bool paused { get; set; }
}
}

+ 25
- 21
Svelto.ECS/EntityViewUtility.cs View File

@@ -48,38 +48,42 @@ namespace Svelto.ECS
entityComponentBlazingFastReflection, out var count);

//todo this should happen once per T, not once per Build<T>
foreach (var implementor in implementors)
if (implementors != null)
{
if (implementor != null)
foreach (var implementor in implementors)
{
var type = implementor.GetType();
if (implementor != null)
{
var type = implementor.GetType();

if (cachedTypeInterfaces.TryGetValue(type, out var interfaces) == false)
interfaces = cachedTypeInterfaces[type] = type.GetInterfacesEx();
if (cachedTypeInterfaces.TryGetValue(type, out var interfaces) == false)
interfaces = cachedTypeInterfaces[type] = type.GetInterfacesEx();

for (var iindex = 0; iindex < interfaces.Length; iindex++)
{
var componentType = interfaces[iindex];
#if DEBUG && !PROFILE_SVELTO
if (implementorsByType.TryGetValue(componentType, out var implementorData))
for (var iindex = 0; iindex < interfaces.Length; iindex++)
{
implementorData.numberOfImplementations++;
implementorsByType[componentType] = implementorData;
}
else
implementorsByType[componentType] = new ECSTuple<object, int>(implementor, 1);
var componentType = interfaces[iindex];
#if DEBUG && !PROFILE_SVELTO
if (implementorsByType.TryGetValue(componentType, out var implementorData))
{
implementorData.numberOfImplementations++;
implementorsByType[componentType] = implementorData;
}
else
implementorsByType[componentType] = new ECSTuple<object, int>(implementor, 1);
#else
implementorsByType[componentType] = implementor;
#endif
}
}
}
#if DEBUG && !PROFILE_SVELTO
else
{
Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat(" entityComponent ",
componentBuilder.GetEntityComponentType().ToString()));
}
else
{
Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat(" entityComponent "
, componentBuilder
.GetEntityComponentType().ToString()));
}
#endif
}
}

for (var i = 0; i < count; i++)


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

@@ -1,27 +1,11 @@
using System;
using System.Collections.Generic;
using Svelto.ECS.Internal;
using System.Runtime.CompilerServices;

#pragma warning disable 660,661

namespace Svelto.ECS
{
/// <summary>
/// still experimental alternative to ExclusiveGroup, use this like:
/// use this like:
/// public class TriggersGroup : ExclusiveGroup<TriggersGroup> {}
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class NamedExclusiveGroup<T>
{
public static ExclusiveGroup Group = new ExclusiveGroup();
public static string name = typeof(T).FullName;

// protected NamedExclusiveGroup() { }
// protected NamedExclusiveGroup(string recognizeAs) : base(recognizeAs) {}
// protected NamedExclusiveGroup(ushort range) : base(range) {}
}

/// <summary>
/// Exclusive Groups guarantee that the GroupID is unique.
///


+ 5
- 3
Svelto.ECS/ExclusiveGroupStruct.cs View File

@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
@@ -51,7 +51,7 @@ namespace Svelto.ECS

public override string ToString()
{
return _id.ToString();
return this.ToName();
}

internal static ExclusiveGroupStruct Generate(byte bitmask = 0)
@@ -109,6 +109,8 @@ namespace Svelto.ECS

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

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

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

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


Svelto.ECS/Extensions/Svelto/NativeAllGroupsEnumerable.cs → Svelto.ECS/Extensions/Svelto/AllGroupsEnumerable.cs View File

@@ -1,32 +1,37 @@
using System;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public readonly struct NativeAllGroupsEnumerable<T1> where T1 : unmanaged, IEntityComponent
/// <summary>
/// ToDo it would be interesting to have a version of this dedicated to unmanaged, IEntityComponent
/// that can be burstifiable
/// </summary>
/// <typeparam name="T1"></typeparam>
public readonly struct AllGroupsEnumerable<T1> where T1 : struct, IEntityComponent
{
public NativeAllGroupsEnumerable(EntitiesDB db)
public ref struct GroupCollection
{
internal EntityCollection<T1> collection;
internal ExclusiveGroupStruct group;

public void Deconstruct(out EntityCollection<T1> collection, out ExclusiveGroupStruct group)
{
collection = this.collection;
group = this.@group;
}
}
public AllGroupsEnumerable(EntitiesDB db)
{
_db = db;
}
public struct NativeGroupsIterator
public ref struct GroupsIterator
{
public struct CurrentGroup: IDisposable
{
public NB<T1> buffer;
public ExclusiveGroupStruct group;

public void Dispose()
{
buffer.Dispose();
}
}
public NativeGroupsIterator(EntitiesDB db) : this()
public GroupsIterator(EntitiesDB db) : this()
{
_db = db.FindGroups<T1>().GetEnumerator();
_db = db.FindGroups_INTERNAL<T1>().GetEnumerator();
}

public bool MoveNext()
@@ -35,13 +40,11 @@ namespace Svelto.ECS
while (_db.MoveNext() == true)
{
FasterDictionary<uint, ITypeSafeDictionary>.KeyValuePairFast group = _db.Current;

ITypeSafeDictionary<T1> typeSafeDictionary = @group.Value as ITypeSafeDictionary<T1>;
if (typeSafeDictionary.Count == 0) continue;
_array.buffer = new EntityCollection<T1>(typeSafeDictionary.GetValuesArray(out var count), count)
.ToNativeBuffer<T1>();
if (typeSafeDictionary.count == 0) continue;

_array.collection = new EntityCollection<T1>(typeSafeDictionary.GetValues(out var count), count);
_array.@group = new ExclusiveGroupStruct(group.Key);

return true;
@@ -50,19 +53,15 @@ namespace Svelto.ECS
return false;
}

public void Reset()
{
}

public CurrentGroup Current => _array;
public GroupCollection Current => _array;

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

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

readonly EntitiesDB _db;
@@ -99,7 +98,7 @@ namespace Svelto.ECS
if (typeSafeDictionary1.Count == 0) continue;
_array = new BT<NB<T1>, NB<T2>>()(new EntityCollection<T1>(typeSafeDictionary1.GetValuesArray(out var count), count)
.ToNativeBuffer<T1>();
.ToBuffer();

return true;
}

+ 162
- 0
Svelto.ECS/Extensions/Svelto/EntityCollectionExtension.cs View File

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

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
{
buffer = ec._nativedBuffer;
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
{
buffer1 = ec.Item1._nativedBuffer;
buffer2 = ec.Item2._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.Item1._nativedBuffer;
buffer2 = ec.Item2._nativedBuffer;
buffer3 = ec.Item3._nativedBuffer;
count = (int) ec.count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BT<NB<T1>> ToBuffer<T1>(in this EntityCollection<T1> ec) where T1 : unmanaged, IEntityComponent
{
return new BT<NB<T1>>(ec._nativedBuffer, ec.count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BT<NB<T1>, NB<T2>> ToBuffers<T1, T2>
(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);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BT<NB<T1>, NB<T2>, NB<T3>> ToBuffers<T1, T2, T3>
(in this EntityCollection<T1, T2, T3> ec)
where T2 : unmanaged, IEntityComponent
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);
}
}

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
{
buffer = ec._managedBuffer;
count = (int) ec.count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BT<MB<T1>> ToBuffer<T1>(in this EntityCollection<T1> ec) where T1 : struct, IEntityViewComponent
{
return new BT<MB<T1>>(ec._managedBuffer, ec.count);
}

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

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

public static class EntityCollectionExtensionC
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (NB<T1> buffer1, MB<T2> buffer2, uint count) ToBuffers<T1, T2>
(in this EntityCollection<T1, T2> ec)
where T1 : unmanaged, IEntityComponent where T2 : struct, IEntityViewComponent
{
return (ec.Item1._nativedBuffer, ec.Item2._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.Item1._nativedBuffer;
buffer2 = ec.Item2._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)
where T1 : unmanaged, IEntityComponent
where T2 : struct, IEntityViewComponent
where T3 : struct, IEntityViewComponent
{
return (ec.Item1._nativedBuffer, ec.Item2._managedBuffer, ec.Item3._managedBuffer, 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
{
buffer1 = ec.Item1._nativedBuffer;
buffer2 = ec.Item2._nativedBuffer;
buffer3 = ec.Item3._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)
where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : struct, IEntityViewComponent
{
return (ec.Item1._nativedBuffer, ec.Item2._nativedBuffer, ec.Item3._managedBuffer, ec.count);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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);
}
}
}

+ 72
- 64
Svelto.ECS/Extensions/Svelto/EntityDBExtensions.cs View File

@@ -1,77 +1,85 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public static class EntityDBExtensions
{
public static NativeGroupsEnumerable<T1, T2> NativeGroupsIterator<T1, T2>(this EntitiesDB db,
ExclusiveGroupStruct[] groups)
where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static AllGroupsEnumerable<T1> QueryEntities<T1>(this EntitiesDB db)
where T1 :struct, IEntityComponent
{
return new NativeGroupsEnumerable<T1, T2>(db, groups, (uint)groups.Length);
}
public static NativeGroupsEnumerable<T1, T2> NativeGroupsIterator<T1, T2>(this EntitiesDB db,
FasterList<ExclusiveGroupStruct> groups)
where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent
{
return new NativeGroupsEnumerable<T1, T2>(db, groups, groups.count);
return new AllGroupsEnumerable<T1>(db);
}
[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)
return array;

public static NativeGroupsEnumerable<T1, T2, T3> NativeGroupsIterator
<T1, T2, T3>(this EntitiesDB db, ExclusiveGroupStruct[] groups)
where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
{
return new NativeGroupsEnumerable<T1, T2, T3>(db, groups);
}
public static NativeGroupsEnumerable<T1, T2, T3, T4> NativeGroupsIterator
<T1, T2, T3, T4>(this EntitiesDB db, ExclusiveGroupStruct[] groups)
where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent where T4 : unmanaged, IEntityComponent
{
return new NativeGroupsEnumerable<T1, T2, T3, T4>(db, groups);
}
public static NativeGroupsEnumerable<T1> NativeGroupsIterator<T1>(this EntitiesDB db, ExclusiveGroupStruct[] groups)
where T1 : unmanaged, IEntityComponent
{
return new NativeGroupsEnumerable<T1>(db, groups);
}
throw new EntityNotFoundException(entityGID, typeof(T));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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)
return array;

public static NativeAllGroupsEnumerable<T1> NativeGroupsIterator<T1>(this EntitiesDB db)
where T1 : unmanaged, IEntityComponent
{
return new NativeAllGroupsEnumerable<T1>(db);
}
#if TO_BE_FINISHED
public static NativeAllGroupsEnumerable<T1, T2> NativeGroupsIterator<T1, T2>(this EntitiesDB db)
where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent
{
return new NativeAllGroupsEnumerable<T1, T2>(db);
}
#endif
public static NB<T> NativeEntitiesBuffer<T>(this EntitiesDB db, ExclusiveGroupStruct @group)
where T : unmanaged, IEntityComponent
{
return db.QueryEntities<T>(group).ToNativeBuffer<T>();
}
public static BT<NB<T1>, NB<T2>> NativeEntitiesBuffer<T1, T2>(this EntitiesDB db, ExclusiveGroupStruct @group)
where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
{
return db.QueryEntities<T1, T2>(group).ToNativeBuffers<T1, T2>();
}
throw new EntityNotFoundException(entityGID, typeof(T));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
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)
return true;

return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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)
return true;

return false;
}
public static BT<NB<T1>, NB<T2>, NB<T3>> NativeEntitiesBuffer<T1, T2, T3>(this EntitiesDB db, ExclusiveGroupStruct @group)
where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
{
return db.QueryEntities<T1, T2, T3>(group).ToNativeBuffers<T1, T2, T3>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool QueryEntitiesAndIndexInternal<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index, out NB<T> buffer) where T : unmanaged, IEntityComponent
{
index = 0;
buffer = default;
if (entitiesDb.SafeQueryEntityDictionary<T>(entityGID.groupID, out var safeDictionary) == false)
return false;

if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false)
return false;
buffer = (NB<T>) (safeDictionary as ITypeSafeDictionary<T>).GetValues(out _);

return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T QueryEntity<T>(this EntitiesDB entitiesDb, EGID entityGID) where T : unmanaged, IEntityComponent
{
var array = entitiesDb.QueryEntitiesAndIndex<T>(entityGID, out var index);
return ref array[(int) index];
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T QueryEntity<T>(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent
{
return ref entitiesDb.QueryEntity<T>(new EGID(id, group));
}
}
}

+ 69
- 0
Svelto.ECS/Extensions/Svelto/EntityDBExtensionsB.cs View File

@@ -0,0 +1,69 @@
using System.Runtime.CompilerServices;
using Svelto.DataStructures;
using Svelto.ECS.Hybrid;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public static class EntityDBExtensionsB
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static MB<T> QueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index) where T : struct, IEntityViewComponent
{
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(entityGID, out index, out MB<T> array) == true)
return array;

throw new EntityNotFoundException(entityGID, typeof(T));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryQueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index, out MB<T> array)
where T : struct, IEntityViewComponent
{
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(entityGID, out index, out array) == true)
return true;

return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryQueryEntitiesAndIndex<T>(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group, out uint index, out MB<T> array)
where T : struct, IEntityViewComponent
{
if (entitiesDb.QueryEntitiesAndIndexInternal<T>(new EGID(id, group), out index, out array) == true)
return true;

return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool QueryEntitiesAndIndexInternal<T>(this EntitiesDB entitiesDb, EGID entityGID, out uint index, out MB<T> buffer) where T : struct, IEntityViewComponent
{
index = 0;
buffer = default;
if (entitiesDb.SafeQueryEntityDictionary<T>(entityGID.groupID, out var safeDictionary) == false)
return false;

if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false)
return false;
buffer = (MB<T>) (safeDictionary as ITypeSafeDictionary<T>).GetValues(out _);

return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T QueryEntity<T>(this EntitiesDB entitiesDb, EGID entityGID) where T : struct, IEntityViewComponent
{
var array = entitiesDb.QueryEntitiesAndIndex<T>(entityGID, out var index);
return ref array[(int) index];
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T QueryEntity<T>(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group) where T : struct, IEntityViewComponent
{
return ref entitiesDb.QueryEntity<T>(new EGID(id, group));
}
}
}

+ 249
- 0
Svelto.ECS/Extensions/Svelto/GroupsEnumerable.cs View File

@@ -0,0 +1,249 @@
using DBC.ECS;
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
/// </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
{
readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
{
_db = db;
_groups = groups;
}

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

public bool MoveNext()
{
//attention, the while is necessary to skip empty groups
while (++_indexGroup < _groups.count)
{
var entityCollection1 = _entitiesDB.QueryEntities<T1, T2, T3>(_groups[_indexGroup]);
if (entityCollection1.count == 0)
continue;
var entityCollection2 = _entitiesDB.QueryEntities<T4>(_groups[_indexGroup]);
if (entityCollection2.count == 0)
continue;

Check.Assert(entityCollection1.count == entityCollection2.count
, "congratulation, you found a bug in Svelto, please report it");

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

return moveNext;
}

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

public EntityCollection<T1, T2, T3, T4> Current => _buffers;

readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

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

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

/// <summary>
/// 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
{
readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
{
_db = db;
_groups = groups;
}

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

public bool MoveNext()
{
//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]);
if (entityCollection.count == 0)
continue;

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

return moveNext;
}

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

public EntityCollection<T1, T2, T3> Current => _buffers;

readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

int _indexGroup;
EntityCollection<T1, T2, T3> _buffers;
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 GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups)
{
_db = db;
_groups = groups;
}

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

public bool MoveNext()
{
//attention, the while is necessary to skip empty groups
while (++_indexGroup < _groups.count)
{
var entityCollection = _db.QueryEntities<T1, T2>(_groups[_indexGroup]);
if (entityCollection.count == 0)
continue;

_buffers = entityCollection;
break;
}

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

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

public EntityCollection<T1, T2> Current => _buffers;

readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

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

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

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

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

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

public bool MoveNext()
{
//attention, the while is necessary to skip empty groups
while (++_indexGroup < _groups.count)
{
var entityCollection = _db.QueryEntities<T1>(_groups[_indexGroup]);
if (entityCollection.count == 0)
continue;

_buffer = entityCollection;
break;
}

return _indexGroup < _groups.count;
}

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

public EntityCollection<T1> Current => _buffer;

readonly EntitiesDB _db;
readonly LocalFasterReadOnlyList<ExclusiveGroupStruct> _groups;

int _indexGroup;
EntityCollection<T1> _buffer;
}

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

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

+ 0
- 229
Svelto.ECS/Extensions/Svelto/NativeGroupsEnumerable.cs View File

@@ -1,229 +0,0 @@
using DBC.ECS;
using Svelto.DataStructures;

namespace Svelto.ECS
{
public readonly struct NativeGroupsEnumerable<T1, T2, T3, T4> where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
where T4 : unmanaged, IEntityComponent
{
readonly EntitiesDB _db;
readonly ExclusiveGroupStruct[] _groups;

public NativeGroupsEnumerable(EntitiesDB db, ExclusiveGroupStruct[] groups)
{
_db = db;
_groups = groups;
}

public struct NativeGroupsIterator
{
public NativeGroupsIterator(EntitiesDB db, ExclusiveGroupStruct[] groups) : this()
{
_groups = groups;
_indexGroup = -1;
_entitiesDB = db;
}

public bool MoveNext()
{
//attention, the while is necessary to skip empty groups
while (++_indexGroup < _groups.Length)
{
var entityCollection1 = _entitiesDB.QueryEntities<T1, T2, T3>(_groups[_indexGroup]);
if (entityCollection1.count == 0)
continue;
var entityCollection2 = _entitiesDB.QueryEntities<T4>(_groups[_indexGroup]);
if (entityCollection2.count == 0)
continue;

Check.Assert(entityCollection1.count == entityCollection2.count
, "congratulation, you found a bug in Svelto, please report it");

var array = entityCollection1.ToNativeBuffers<T1, T2, T3>();
var array2 = entityCollection2.ToNativeBuffer<T4>();
_array = new BT<NB<T1>, NB<T2>, NB<T3>, NB<T4>>(array.buffer1, array.buffer2, array.buffer3, array2
, entityCollection1.count);
break;
}

return _indexGroup < _groups.Length;
}

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

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

readonly ExclusiveGroupStruct[] _groups;

int _indexGroup;
BT<NB<T1>, NB<T2>, NB<T3>, NB<T4>> _array;
readonly EntitiesDB _entitiesDB;
}

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

public readonly struct NativeGroupsEnumerable<T1, T2, T3> where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
where T3 : unmanaged, IEntityComponent
{
readonly EntitiesDB _db;
readonly ExclusiveGroupStruct[] _groups;

public NativeGroupsEnumerable(EntitiesDB db, ExclusiveGroupStruct[] groups)
{
_db = db;
_groups = groups;
}

public struct NativeGroupsIterator
{
public NativeGroupsIterator(EntitiesDB db, ExclusiveGroupStruct[] groups) : this()
{
_groups = groups;
_indexGroup = -1;
_entitiesDB = db;
}

public bool MoveNext()
{
//attention, the while is necessary to skip empty groups
while (++_indexGroup < _groups.Length)
{
var entityCollection = _entitiesDB.QueryEntities<T1, T2, T3>(_groups[_indexGroup]);
if (entityCollection.count == 0)
continue;

_array = entityCollection.ToNativeBuffers<T1, T2, T3>();
break;
}

return _indexGroup < _groups.Length;
}

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

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

readonly ExclusiveGroupStruct[] _groups;

int _indexGroup;
BT<NB<T1>, NB<T2>, NB<T3>> _array;
readonly EntitiesDB _entitiesDB;
}

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

public struct NativeGroupsEnumerable<T1, T2> where T1 : unmanaged, IEntityComponent
where T2 : unmanaged, IEntityComponent
{
public NativeGroupsEnumerable(EntitiesDB db, ExclusiveGroupStruct[] groups, uint groupsLength)
{
_db = db;
_groups = groups;
_groupsLength = groupsLength;
}

public NativeGroupsEnumerable(EntitiesDB db, FasterList<ExclusiveGroupStruct> groups, uint groupsLength)
{
_db = db;
_groups = groups.ToArrayFast(out _);
_groupsLength = groupsLength;
}

public struct NativeGroupsIterator
{
public NativeGroupsIterator(EntitiesDB db, ExclusiveGroupStruct[] groups) : this()
{
_db = db;
_groups = groups;
_indexGroup = -1;
}

public bool MoveNext()
{
//attention, the while is necessary to skip empty groups
while (++_indexGroup < _groups.Length)
{
var entityCollection = _db.QueryEntities<T1, T2>(_groups[_indexGroup]);
if (entityCollection.count == 0)
continue;

_array = entityCollection.ToNativeBuffers<T1, T2>();
break;
}

return _indexGroup < _groups.Length;
}

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

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

readonly EntitiesDB _db;
readonly ExclusiveGroupStruct[] _groups;

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

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

readonly EntitiesDB _db;
readonly ExclusiveGroupStruct[] _groups;
readonly uint _groupsLength;
}

public struct NativeGroupsEnumerable<T1> where T1 : unmanaged, IEntityComponent
{
public NativeGroupsEnumerable(EntitiesDB db, ExclusiveGroupStruct[] groups)
{
_db = db;
_groups = groups;
}

public struct NativeGroupsIterator
{
public NativeGroupsIterator(EntitiesDB db, ExclusiveGroupStruct[] groups) : this()
{
_db = db;
_groups = groups;
_indexGroup = -1;
}

public bool MoveNext()
{
//attention, the while is necessary to skip empty groups
while (++_indexGroup < _groups.Length)
{
var entityCollection = _db.QueryEntities<T1>(_groups[_indexGroup]);
if (entityCollection.count == 0)
continue;

_array = entityCollection.ToNativeBuffer<T1>();
break;
}

return _indexGroup < _groups.Length;
}

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

public NB<T1> Current => _array;

readonly EntitiesDB _db;
readonly ExclusiveGroupStruct[] _groups;

int _indexGroup;
NB<T1> _array;
}

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

readonly EntitiesDB _db;
readonly ExclusiveGroupStruct[] _groups;
}
}

+ 86
- 0
Svelto.ECS/Extensions/Svelto/SortedEnginesGroup.cs View File

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

namespace Svelto.ECS.Extensions
{
public interface IStepEngine : IEngine
{
void Step();
string name { get; }
}
public interface IGroupEngine : IStepEngine
{ }
public interface IStepEngine<T> : IEngine
{
void Step(ref T _param);
string name { get; }
}
public interface IStepGroupEngine<T> : IStepEngine<T>
{
}
/// <summary>
/// Note sorted jobs run in serial
/// </summary>
/// <typeparam name="Interface"></typeparam>
/// <typeparam name="SequenceOrder"></typeparam>
public abstract class SortedEnginesGroup<Interface, SequenceOrder> : IGroupEngine
where SequenceOrder : struct, ISequenceOrder where Interface : class, IStepEngine
{
protected SortedEnginesGroup(FasterList<Interface> engines)
{
_name = "SortedEnginesGroup - "+this.GetType().Name;
_instancedSequence = new Sequence<Interface, SequenceOrder>(engines);
}

public void Step()
{
var sequenceItems = _instancedSequence.items;
using (var profiler = new PlatformProfiler(_name))
{
for (var index = 0; index < sequenceItems.count; index++)
{
var engine = sequenceItems[index];
using (profiler.Sample(engine.name)) engine.Step();
}
}
}

public string name => _name;
readonly string _name;
readonly Sequence<Interface, SequenceOrder> _instancedSequence;
}
public abstract class SortedEnginesGroup<Interface, Parameter, SequenceOrder>: IStepGroupEngine<Parameter>
where SequenceOrder : struct, ISequenceOrder where Interface : class, IStepEngine<Parameter>
{
protected SortedEnginesGroup(FasterList<Interface> engines)
{
_name = "SortedEnginesGroup - "+this.GetType().Name;
_instancedSequence = new Sequence<Interface, SequenceOrder>(engines);
}

public void Step(ref Parameter param)
{
var sequenceItems = _instancedSequence.items;
using (var profiler = new PlatformProfiler(_name))
{
for (var index = 0; index < sequenceItems.count; index++)
{
var engine = sequenceItems[index];
using (profiler.Sample(engine.name)) engine.Step(ref param);
}
}
}

public string name => _name;
readonly string _name;
readonly Sequence<Interface, SequenceOrder> _instancedSequence;
}
}

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

@@ -1,30 +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 CopySveltoToUECSEnginesGroup : ComponentSystemGroup, IJobifiedEngine
{
public JobHandle Execute(JobHandle _jobHandle)
{
foreach (var engine in Systems)
(engine as ICopySveltoToUECSEngine).jobHandle = _jobHandle;
Update();
return _jobHandle;
}

readonly SimulationSystemGroup _simulationSystemGroup;
}

public interface ICopySveltoToUECSEngine:IEngine
{
JobHandle jobHandle { set; }
}
}
#endif

+ 35
- 116
Svelto.ECS/Extensions/Unity/DOTS/EnginesRoot.NativeOperation.cs View File

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

@@ -19,30 +19,30 @@ namespace Svelto.ECS
readonly AtomicNativeBags _swapOperationQueue =
new AtomicNativeBags(Common.Allocator.Persistent, JobsUtility.MaxJobThreadCount + 1);

NativeEntityRemove ProvideNativeEntityRemoveQueue<T>() where T : IEntityDescriptor, new()
NativeEntityRemove ProvideNativeEntityRemoveQueue<T>(string memberName) where T : IEntityDescriptor, new()
{
//todo: remove operation array and store entity descriptor hash in the return value
//todo I maybe able to provide a _nativeSwap.SwapEntity<entityDescriptor>
_nativeRemoveOperations.Add(
new NativeOperationRemove(EntityDescriptorTemplate<T>.descriptor.componentsToBuild));
new NativeOperationRemove(EntityDescriptorTemplate<T>.descriptor.componentsToBuild, TypeCache<T>.type, memberName));

return new NativeEntityRemove(_removeOperationQueue, _nativeRemoveOperations.count - 1);
}
NativeEntitySwap ProvideNativeEntitySwapQueue<T>() where T : IEntityDescriptor, new()
NativeEntitySwap ProvideNativeEntitySwapQueue<T>(string memberName) where T : IEntityDescriptor, new()
{
//todo: remove operation array and store entity descriptor hash in the return value
_nativeSwapOperations.Add(
new NativeOperationSwap(EntityDescriptorTemplate<T>.descriptor.componentsToBuild));
new NativeOperationSwap(EntityDescriptorTemplate<T>.descriptor.componentsToBuild, TypeCache<T>.type, memberName));

return new NativeEntitySwap(_swapOperationQueue, _nativeSwapOperations.count - 1);
}

NativeEntityFactory ProvideNativeEntityFactoryQueue<T>() where T : IEntityDescriptor, new()
NativeEntityFactory ProvideNativeEntityFactoryQueue<T>(string memberName) where T : IEntityDescriptor, new()
{
//todo: remove operation array and store entity descriptor hash in the return value
_nativeAddOperations.Add(
new NativeOperationBuild(EntityDescriptorTemplate<T>.descriptor.componentsToBuild));
new NativeOperationBuild(EntityDescriptorTemplate<T>.descriptor.componentsToBuild, TypeCache<T>.type));

return new NativeEntityFactory(_addOperationQueue, _nativeAddOperations.count - 1);
}
@@ -59,10 +59,10 @@ namespace Svelto.ECS
{
var componentsIndex = buffer.Dequeue<uint>();
var entityEGID = buffer.Dequeue<EGID>();
CheckRemoveEntityID(entityEGID);
CheckRemoveEntityID(entityEGID, _nativeRemoveOperations[componentsIndex].type);
QueueEntitySubmitOperation(new EntitySubmitOperation(
EntitySubmitOperationType.Remove, entityEGID, entityEGID
, _nativeRemoveOperations[componentsIndex].entityComponents));
, _nativeRemoveOperations[componentsIndex].components));
}
}

@@ -75,12 +75,12 @@ namespace Svelto.ECS
var componentsIndex = buffer.Dequeue<uint>();
var entityEGID = buffer.Dequeue<DoubleEGID>();
CheckRemoveEntityID(entityEGID.@from);
CheckAddEntityID(entityEGID.to);
CheckRemoveEntityID(entityEGID.@from, _nativeSwapOperations[componentsIndex].type, _nativeSwapOperations[componentsIndex].caller );
CheckAddEntityID(entityEGID.to, _nativeSwapOperations[componentsIndex].type, _nativeSwapOperations[componentsIndex].caller);
QueueEntitySubmitOperation(new EntitySubmitOperation(
EntitySubmitOperationType.Swap, entityEGID.@from, entityEGID.to
, _nativeSwapOperations[componentsIndex].entityComponents));
, _nativeSwapOperations[componentsIndex].components));
}
}
}
@@ -98,8 +98,9 @@ namespace Svelto.ECS
var componentCounts = buffer.Dequeue<uint>();
EntityComponentInitializer init =
BuildEntity(egid, _nativeAddOperations[componentsIndex].components);
BuildEntity(egid, _nativeAddOperations[componentsIndex].components, _nativeAddOperations[componentsIndex].type);

//only called if Init is called on the initialized (there is something to init)
while (componentCounts > 0)
{
componentCounts--;
@@ -140,125 +141,43 @@ namespace Svelto.ECS
}
}

public readonly struct NativeEntityRemove
{
readonly AtomicNativeBags _removeQueue;
readonly uint _indexRemove;

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

public void RemoveEntity(EGID egid, int threadIndex)
{
var simpleNativeBag = _removeQueue.GetBuffer(threadIndex);
simpleNativeBag.Enqueue(_indexRemove);
simpleNativeBag.Enqueue(egid);
}
}
public readonly struct NativeEntitySwap
{
readonly AtomicNativeBags _swapQueue;
readonly uint _indexSwap;

internal NativeEntitySwap(AtomicNativeBags EGIDsToSwap, uint indexSwap)
{
_swapQueue = EGIDsToSwap;
_indexSwap = indexSwap;
}

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

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

public readonly struct NativeEntityFactory
{
readonly AtomicNativeBags _addOperationQueue;
readonly uint _index;

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

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

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

return new NativeEntityComponentInitializer(unsafeBuffer, index);
}
}

public readonly ref struct NativeEntityComponentInitializer
{
readonly NativeBag _unsafeBuffer;
readonly UnsafeArrayIndex _index;

public NativeEntityComponentInitializer(in NativeBag unsafeBuffer, UnsafeArrayIndex index)
{
_unsafeBuffer = unsafeBuffer;
_index = index;
}

public void Init<T>(in T component) where T : unmanaged, IEntityComponent
{
uint id = EntityComponentID<T>.ID.Data;

_unsafeBuffer.AccessReserved<uint>(_index)++;

_unsafeBuffer.Enqueue(id);
_unsafeBuffer.Enqueue(component);
}
}

struct NativeOperationBuild
readonly struct NativeOperationBuild
{
internal readonly IComponentBuilder[] components;
internal readonly Type type;

public NativeOperationBuild(IComponentBuilder[] descriptorEntityComponentsToBuild)
public NativeOperationBuild(IComponentBuilder[] descriptorComponentsToBuild, Type entityType)
{
components = descriptorEntityComponentsToBuild;
type = entityType;
components = descriptorComponentsToBuild;
}
}

readonly struct NativeOperationRemove
{
internal readonly IComponentBuilder[] entityComponents;

public NativeOperationRemove(IComponentBuilder[] descriptorEntitiesToBuild)
internal readonly IComponentBuilder[] components;
internal readonly Type type;
internal readonly string caller;
public NativeOperationRemove(IComponentBuilder[] descriptorComponentsToRemove, Type entityType, string caller)
{
entityComponents = descriptorEntitiesToBuild;
this.caller = caller;
components = descriptorComponentsToRemove;
type = entityType;
}
}

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

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

+ 0
- 9
Svelto.ECS/Extensions/Unity/DOTS/IJobifiedEngine.cs View File

@@ -1,9 +0,0 @@
using Unity.Jobs;

namespace Svelto.ECS.Extensions.Unity
{
public interface IJobifiedEngine : IEngine
{
JobHandle Execute(JobHandle _jobHandle);
}
}

+ 77
- 11
Svelto.ECS/Extensions/Unity/DOTS/JobifedEnginesGroup.cs View File

@@ -1,31 +1,97 @@
#if UNITY_2019_2_OR_NEWER
using Svelto.Common;
using Svelto.DataStructures;
using Unity.Jobs;

namespace Svelto.ECS.Extensions.Unity
{
public abstract class JobifedEnginesGroup<Interface>
where Interface : class, IJobifiedEngine
public interface IJobifiedEngine : IEngine
{
protected JobifedEnginesGroup(FasterReadOnlyList<Interface> engines, bool completeEachJob = false)
JobHandle Execute(JobHandle _jobHandle);
string name { get; }
}
public interface IJobifiedGroupEngine : IJobifiedEngine
{ }
public interface IJobifiedEngine<T> : IEngine
{
JobHandle Execute(JobHandle _jobHandle, 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
{
protected JobifedEnginesGroup(FasterList<Interface> engines)
{
_name = "JobifiedEnginesGroup - "+this.GetType().Name;
_engines = engines;
_completeEachJob = completeEachJob;
}

public JobHandle Execute(JobHandle combinedHandles)
public JobHandle Execute(JobHandle inputHandles)
{
var fasterReadOnlyList = _engines;
for (var index = 0; index < fasterReadOnlyList.Count; index++)
var engines = _engines;
JobHandle combinedHandles = inputHandles;
using (var profiler = new PlatformProfiler(_name))
{
var engine = fasterReadOnlyList[index];
combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles));
if (_completeEachJob) combinedHandles.Complete();
for (var index = 0; index < engines.count; index++)
{
ref var engine = ref engines[index];
using (profiler.Sample(engine.name))
{
combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(inputHandles));
}
}
}

return combinedHandles;
}

public string name => _name;

readonly FasterReadOnlyList<Interface> _engines;
readonly bool _completeEachJob;
readonly string _name;
}
public abstract class JobifedEnginesGroup<Interface, Param>: IJobifiedGroupEngine<Param> where Interface : class, IJobifiedEngine<Param>
{
protected JobifedEnginesGroup(FasterList<Interface> engines)
{
_name = "JobifiedEnginesGroup - "+this.GetType().Name;
_engines = engines;
}

public JobHandle Execute(JobHandle combinedHandles, ref Param _param)
{
var engines = _engines;
using (var profiler = new PlatformProfiler(_name))
{
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));
}
}

return combinedHandles;
}
public string name => _name;
readonly string _name;

readonly FasterReadOnlyList<Interface> _engines;
}
}
}
#endif

+ 1
- 1
Svelto.ECS/Extensions/Unity/DOTS/JobifiedSveltoEngines.cs View File

@@ -1,4 +1,4 @@
#if UNITY_ECS
#if UNITY_BURST
namespace Svelto.ECS.Extensions.Unity
{
public enum JobifiedSveltoEngines


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

@@ -0,0 +1,83 @@
#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

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

@@ -0,0 +1,43 @@
#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

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

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

namespace Svelto.ECS
{
public readonly ref struct NativeEntityComponentInitializer
{
readonly NativeBag _unsafeBuffer;
readonly UnsafeArrayIndex _index;

public NativeEntityComponentInitializer(in NativeBag unsafeBuffer, UnsafeArrayIndex index)
{
_unsafeBuffer = unsafeBuffer;
_index = index;
}

public void Init<T>(in T component) where T : unmanaged, IEntityComponent
{
uint id = EntityComponentID<T>.ID.Data;

_unsafeBuffer.AccessReserved<uint>(_index)++;

_unsafeBuffer.Enqueue(id);
_unsafeBuffer.Enqueue(component);
}
}
}

+ 42
- 0
Svelto.ECS/Extensions/Unity/DOTS/NativeEntityFactory.cs View File

@@ -0,0 +1,42 @@
#if UNITY_BURST
using Svelto.ECS.DataStructures;
using Svelto.ECS.DataStructures.Unity;

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

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

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

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

return new NativeEntityComponentInitializer(unsafeBuffer, index);
}
public NativeEntityComponentInitializer BuildEntity(EGID egid, int threadIndex)
{
NativeBag unsafeBuffer = _addOperationQueue.GetBuffer(threadIndex + 1);

unsafeBuffer.Enqueue(_index);
unsafeBuffer.Enqueue(new EGID(egid.entityID, egid.groupID));
unsafeBuffer.ReserveEnqueue<uint>(out var index) = 0;

return new NativeEntityComponentInitializer(unsafeBuffer, index);
}
}
}
#endif

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

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

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

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

public void RemoveEntity(EGID egid, int threadIndex)
{
var simpleNativeBag = _removeQueue.GetBuffer(threadIndex);
simpleNativeBag.Enqueue(_indexRemove);
simpleNativeBag.Enqueue(egid);
}
}
}
#endif

+ 33
- 0
Svelto.ECS/Extensions/Unity/DOTS/NativeEntitySwap.cs View File

@@ -0,0 +1,33 @@
#if UNITY_BURST
using Svelto.ECS.DataStructures.Unity;

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

internal NativeEntitySwap(AtomicNativeBags EGIDsToSwap, uint indexSwap)
{
_swapQueue = EGIDsToSwap;
_indexSwap = indexSwap;
}

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

public void SwapEntity(EGID from, ExclusiveGroupStruct to, int threadIndex)
{
var simpleNativeBag = _swapQueue.GetBuffer(threadIndex);
simpleNativeBag.Enqueue(_indexSwap);
simpleNativeBag.Enqueue(new DoubleEGID(from, new EGID(from.entityID, to)));
}
}
}
#endif

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

@@ -21,6 +21,8 @@ namespace Svelto.ECS.Extensions.Unity
return _jobHandle;
}

public string name => nameof(PureUECSSystemsGroup);

readonly World _world;
}
}

+ 51
- 7
Svelto.ECS/Extensions/Unity/DOTS/SortedJobifedEnginesGroup.cs View File

@@ -5,26 +5,70 @@ using Svelto.Common;

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

public JobHandle Execute(JobHandle combinedHandles)
public JobHandle Execute(JobHandle inputHandles)
{
var fasterReadOnlyList = _instancedSequence.items;
for (var index = 0; index < fasterReadOnlyList.Count; index++)
var sequenceItems = _instancedSequence.items;
JobHandle combinedHandles = inputHandles;
using (var profiler = new PlatformProfiler(_name))
{
var engine = fasterReadOnlyList[index];
combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles));
for (var index = 0; index < sequenceItems.count; index++)
{
var engine = sequenceItems[index];
using (profiler.Sample(engine.name)) combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles));
}
}

return combinedHandles;
}

public string name => _name;
readonly string _name;
readonly Sequence<Interface, SequenceOrder> _instancedSequence;
}
public abstract class SortedJobifedEnginesGroup<Interface, Parameter, SequenceOrder>: IJobifiedGroupEngine<Parameter>
where SequenceOrder : struct, ISequenceOrder where Interface : class, IJobifiedEngine<Parameter>
{
protected SortedJobifedEnginesGroup(FasterList<Interface> engines)
{
_name = "SortedJobifedEnginesGroup - "+this.GetType().Name;
_instancedSequence = new Sequence<Interface, SequenceOrder>(engines);
}

public JobHandle Execute(JobHandle combinedHandles, ref Parameter param)
{
var sequenceItems = _instancedSequence.items;
using (var profiler = new PlatformProfiler(_name))
{
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));
}
}

return combinedHandles;
}

public string name => _name;
readonly string _name;
readonly Sequence<Interface, SequenceOrder> _instancedSequence;
}
}

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

@@ -0,0 +1,39 @@
#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

+ 90
- 2
Svelto.ECS/Extensions/Unity/DOTS/UnityEntityDBExtensions.cs View File

@@ -1,12 +1,45 @@
#if UNITY_2019_2_OR_NEWER
#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.Extensions.Unity
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
{
@@ -23,9 +56,64 @@ namespace Svelto.ECS.Extensions.Unity
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

+ 32
- 0
Svelto.ECS/Extensions/Unity/EGIDHolderImplementor.cs View File

@@ -0,0 +1,32 @@
#if UNITY_5 || UNITY_5_3_OR_NEWER
using Svelto.ECS.Hybrid;
using UnityEngine;

namespace Svelto.ECS.Extensions.Unity
{
public interface IEGIDHolder
{
EGID ID { set; }
}

public struct EGIDTrackerViewComponent : IEntityViewComponent
{
#pragma warning disable 649
public IEGIDHolder holder;
#pragma warning restore 649
EGID _ID;

public EGID ID
{
get => _ID;
set => _ID = holder.ID = value;
}
}
public class EGIDHolderImplementor : MonoBehaviour, IEGIDHolder, IImplementor
{
public EGID ID { get; set; }
}
}
#endif

+ 44
- 22
Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs View File

@@ -4,20 +4,47 @@ 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
{
public static T CreateFromPrefab<T>(ref uint startIndex, Transform contextHolder, IEntityFactory factory,
ExclusiveGroup group, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder
ExclusiveGroup group, bool searchImplementorsInChildren = false, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder
{
var holder = Create<T>(new EGID(startIndex++, group), contextHolder, factory);
var childs = contextHolder.GetComponentsInChildren<IEntityDescriptorHolder>(true);
Create<T>(new EGID(startIndex++, group), contextHolder, factory, out var holder);
var children = contextHolder.GetComponentsInChildren<IEntityDescriptorHolder>(true);

foreach (var child in childs)
foreach (var child in children)
{
IImplementor[] childImplementors;
if (child.GetType() != typeof(T))
{
var monoBehaviour = child as MonoBehaviour;
var childImplementors = monoBehaviour.GetComponents<IImplementor>();
if (searchImplementorsInChildren == false)
childImplementors = monoBehaviour.GetComponents<IImplementor>();
else
childImplementors = monoBehaviour.GetComponentsInChildren<IImplementor>(true);
startIndex = InternalBuildAll(
startIndex,
child,
@@ -31,27 +58,22 @@ namespace Svelto.ECS.Extensions.Unity
return holder;
}

public static T Create<T>(EGID ID, Transform contextHolder, IEntityFactory factory)
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);
DBC.ECS.Check.Assert(holder != null, $"`{nameof(holder)}` is null! No component of type " +
$"`{typeof(T)}` was found between its children.");

var implementors = holder.GetComponents<IImplementor>();

factory.BuildEntity(ID, holder.GetDescriptor(), implementors);
holder = contextHolder.GetComponentInChildren<T>(true);
var implementors = searchImplementorsInChildren == false ? holder.GetComponents<IImplementor>() : holder.GetComponentsInChildren<IImplementor>(true) ;

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

return factory.BuildEntity(ID, holder.GetDescriptor(), implementors);
}
@@ -97,8 +119,8 @@ namespace Svelto.ECS.Extensions.Unity
return startIndex;
}

/// <summary>
/// Works like CreateAll but only builds entities with holders that have the same group specfied
/// <summary>
/// Works like CreateAll but only builds entities with holders that have the same group specified
/// </summary>
/// <param name="startId"></param>
/// <param name="group">The group to match</param>


+ 22
- 18
Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs View File

@@ -5,7 +5,7 @@ using UnityEngine;

namespace Svelto.ECS.Schedulers.Unity
{
//The EntitySubmissionScheduler has been introduced to make the entity views submission logic platform independent
//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
{
@@ -27,17 +27,22 @@ namespace Svelto.ECS.Schedulers.Unity
{
yield return _wait;
onTick.Invoke();
onTick();
}
}

readonly WaitForEndOfFrame _wait = new WaitForEndOfFrame();
readonly IEnumerator _coroutine;
public EnginesRoot.EntitiesSubmitter onTick;
public System.Action onTick;
}

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

public void Dispose()
{
@@ -46,23 +51,22 @@ namespace Svelto.ECS.Schedulers.Unity
Object.Destroy(_scheduler.gameObject);
}
}

void SubmitEntities()
{
if (paused == false)
_onTick.Invoke();
}
public EnginesRoot.EntitiesSubmitter onTick
EnginesRoot.EntitiesSubmitter IEntitiesSubmissionScheduler.onTick
{
set
{
if (_scheduler == null)
{
_scheduler = new GameObject(_name).AddComponent<Scheduler>();
GameObject.DontDestroyOnLoad(_scheduler.gameObject);
}

_scheduler.onTick = value;
}
set => _onTick = value;
}
public bool paused { get; set; }

Scheduler _scheduler;
readonly string _name;
readonly Scheduler _scheduler;
EnginesRoot.EntitiesSubmitter _onTick;
}
}
#endif

+ 18
- 18
Svelto.ECS/GenericEntityDescriptor.cs View File

@@ -2,33 +2,33 @@
{
public abstract class GenericEntityDescriptor<T> : IEntityDescriptor where T : struct, IEntityComponent
{
static readonly IComponentBuilder[] _entityBuilders;
static GenericEntityDescriptor() { _entityBuilders = new IComponentBuilder[] {new ComponentBuilder<T>()}; }
static readonly IComponentBuilder[] _componentBuilders;
static GenericEntityDescriptor() { _componentBuilders = new IComponentBuilder[] {new ComponentBuilder<T>()}; }

public IComponentBuilder[] componentsToBuild => _entityBuilders;
public IComponentBuilder[] componentsToBuild => _componentBuilders;
}

public abstract class GenericEntityDescriptor<T, U> : IEntityDescriptor
where T : struct, IEntityComponent where U : struct, IEntityComponent
{
static readonly IComponentBuilder[] _entityBuilders;
static readonly IComponentBuilder[] _componentBuilders;

static GenericEntityDescriptor()
{
_entityBuilders = new IComponentBuilder[] {new ComponentBuilder<T>(), new ComponentBuilder<U>()};
_componentBuilders = new IComponentBuilder[] {new ComponentBuilder<T>(), new ComponentBuilder<U>()};
}

public IComponentBuilder[] componentsToBuild => _entityBuilders;
public IComponentBuilder[] componentsToBuild => _componentBuilders;
}

public abstract class GenericEntityDescriptor<T, U, V> : IEntityDescriptor
where T : struct, IEntityComponent where U : struct, IEntityComponent where V : struct, IEntityComponent
{
static readonly IComponentBuilder[] _entityBuilders;
static readonly IComponentBuilder[] _componentBuilders;

static GenericEntityDescriptor()
{
_entityBuilders = new IComponentBuilder[]
_componentBuilders = new IComponentBuilder[]
{
new ComponentBuilder<T>(),
new ComponentBuilder<U>(),
@@ -36,18 +36,18 @@
};
}

public IComponentBuilder[] componentsToBuild => _entityBuilders;
public IComponentBuilder[] componentsToBuild => _componentBuilders;
}

public abstract class GenericEntityDescriptor<T, U, V, W> : IEntityDescriptor
where T : struct, IEntityComponent where U : struct, IEntityComponent where V : struct, IEntityComponent
where W : struct, IEntityComponent
{
static readonly IComponentBuilder[] _entityBuilders;
static readonly IComponentBuilder[] _componentBuilders;

static GenericEntityDescriptor()
{
_entityBuilders = new IComponentBuilder[]
_componentBuilders = new IComponentBuilder[]
{
new ComponentBuilder<T>(),
new ComponentBuilder<U>(),
@@ -56,18 +56,18 @@
};
}

public IComponentBuilder[] componentsToBuild => _entityBuilders;
public IComponentBuilder[] componentsToBuild => _componentBuilders;
}

public abstract class GenericEntityDescriptor<T, U, V, W, X> : IEntityDescriptor
where T : struct, IEntityComponent where U : struct, IEntityComponent where V : struct, IEntityComponent
where W : struct, IEntityComponent where X : struct, IEntityComponent
{
static readonly IComponentBuilder[] _entityBuilders;
static readonly IComponentBuilder[] _componentBuilders;

static GenericEntityDescriptor()
{
_entityBuilders = new IComponentBuilder[]
_componentBuilders = new IComponentBuilder[]
{
new ComponentBuilder<T>(),
new ComponentBuilder<U>(),
@@ -77,18 +77,18 @@
};
}

public IComponentBuilder[] componentsToBuild => _entityBuilders;
public IComponentBuilder[] componentsToBuild => _componentBuilders;
}

public abstract class GenericEntityDescriptor<T, U, V, W, X, Y> : IEntityDescriptor
where T : struct, IEntityComponent where U : struct, IEntityComponent where V : struct, IEntityComponent
where W : struct, IEntityComponent where X : struct, IEntityComponent where Y : struct, IEntityComponent
{
static readonly IComponentBuilder[] _entityBuilders;
static readonly IComponentBuilder[] _componentBuilders;

static GenericEntityDescriptor()
{
_entityBuilders = new IComponentBuilder[]
_componentBuilders = new IComponentBuilder[]
{
new ComponentBuilder<T>(),
new ComponentBuilder<U>(),
@@ -99,6 +99,6 @@
};
}

public IComponentBuilder[] componentsToBuild => _entityBuilders;
public IComponentBuilder[] componentsToBuild => _componentBuilders;
}
}

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

@@ -15,7 +15,7 @@ namespace Svelto.ECS
return _enginesRoot.Target.GenerateConsumer<T>(name, capacity);
}

public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity)
public Consumer<T> GenerateConsumer<T>(ExclusiveGroupStruct @group, string name, uint capacity)
where T : unmanaged, IEntityComponent
{
return _enginesRoot.Target.GenerateConsumer<T>(group, name, capacity);
@@ -30,7 +30,7 @@ namespace Svelto.ECS
{
Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityComponent;

Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity)
Consumer<T> GenerateConsumer<T>(ExclusiveGroupStruct @group, string name, uint capacity)
where T : unmanaged, IEntityComponent;
}
}

+ 27
- 73
Svelto.ECS/GlobalTypeID.cs View File

@@ -1,95 +1,53 @@
#if UNITY_ECS
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.DataStructures;
using Unity.Burst;

namespace Svelto.ECS
{
public class GlobalTypeID
{
internal static uint NextID<T>()
{
return (uint) (Interlocked.Increment(ref value) - 1);
}
internal static uint NextID<T>() { return (uint) (Interlocked.Increment(ref value) - 1); }

static GlobalTypeID()
{
value = 0;
}
static GlobalTypeID() { value = 0; }

static int value;
}
static class EntityComponentID<T>
{
internal static readonly SharedStatic<uint> ID = SharedStatic<uint>.GetOrCreate<GlobalTypeID, T>();
}
interface IFiller

interface IFiller
{
void FillFromByteArray(EntityComponentInitializer init, NativeBag buffer);
}
static class UnmanagedTypeExtensions
{
private static Dictionary<Type, bool> cachedTypes =
new Dictionary<Type, bool>();

public static bool IsUnManaged<T>() { return typeof(T).IsUnManaged(); }

public static bool IsUnManaged(this Type t)
{
var result = false;
if (cachedTypes.ContainsKey(t))
return cachedTypes[t];
else if (t.IsPrimitive || t.IsPointer || t.IsEnum)
result = true;
else if (t.IsGenericType || !t.IsValueType)
result = false;
else
result = t.GetFields(BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.Instance)
.All(x => x.FieldType.IsUnManaged());
cachedTypes.Add(t, result);
return result;
}
}
delegate void ForceUnmanagedCast<T>(EntityComponentInitializer init, NativeBag buffer) where T : struct, IEntityComponent;

class Filler<T>: IFiller where T : struct, IEntityComponent
class Filler<T> : IFiller where T : struct, IEntityComponent
{
static readonly ForceUnmanagedCast<T> _action;

static Filler()
{
var method = typeof(Trick).GetMethod(nameof(Trick.ForceUnmanaged)).MakeGenericMethod(typeof(T));
_action = (ForceUnmanagedCast<T>) Delegate.CreateDelegate(typeof(ForceUnmanagedCast<T>), method);
DBC.ECS.Check.Require(UnmanagedTypeExtensions.IsUnmanaged<T>() == true, "invalid type used");
}

//it's an internal interface
void IFiller.FillFromByteArray(EntityComponentInitializer init, NativeBag buffer)
public void FillFromByteArray(EntityComponentInitializer init, NativeBag buffer)
{
DBC.ECS.Check.Require(UnmanagedTypeExtensions.IsUnManaged<T>() == true, "invalid type used");
var component = buffer.Dequeue<T>();

_action(init, buffer);
init.Init(component);
}
static class Trick
{
public static void ForceUnmanaged<U>(EntityComponentInitializer init, NativeBag buffer) where U : unmanaged, IEntityComponent
{
var component = buffer.Dequeue<U>();
}

init.Init(component);
}
static class EntityComponentID<T>
{
#if UNITY_BURST
internal static readonly Unity.Burst.SharedStatic<uint> ID =
Unity.Burst.SharedStatic<uint>.GetOrCreate<GlobalTypeID, T>();
#else
internal struct SharedStatic
{
public uint Data;
}

internal static SharedStatic ID;
#endif
}

static class EntityComponentIDMap
@@ -101,11 +59,7 @@ namespace Svelto.ECS
var location = EntityComponentID<T>.ID.Data = GlobalTypeID.NextID<T>();
TYPE_IDS.AddAt(location, entityBuilder);
}
internal static IFiller GetTypeFromID(uint typeId)
{
return TYPE_IDS[typeId];
}

internal static IFiller GetTypeFromID(uint typeId) { return TYPE_IDS[typeId]; }
}
}
#endif
}

+ 61
- 47
Svelto.ECS/GroupCompound.cs View File

@@ -1,82 +1,95 @@
using System;
using Svelto.ECS.Internal;
using Svelto.DataStructures;

namespace Svelto.ECS
{
public static class GroupCompound<G1, G2, G3>
public abstract class GroupCompound<G1, G2, G3>
where G1 : GroupTag<G1> where G2 : GroupTag<G2> where G3 : GroupTag<G3>
{
public static readonly ExclusiveGroupStruct[] Groups;
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)
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 ExclusiveGroupStruct[1];
_Groups = new FasterList<ExclusiveGroupStruct>(1);

var Group = new ExclusiveGroup();
Groups[0] = Group;
Console.LogDebug("<color=orange>".FastConcat(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "-").FastConcat(typeof(G3).ToString(), "- Initialized ", Groups[0].ToString()), "</color>"));
_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
}
else
Console.LogDebug(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "-").FastConcat(typeof(G3).ToString(), "-", Groups[0].ToString()));
}
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 static ExclusiveGroupStruct BuildGroup => new ExclusiveGroupStruct(_Groups[0]);
}

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

static GroupCompound()
{
Groups = GroupCompound<G2, G1>.Groups;
_Groups = GroupCompound<G2, G1>._Groups;
if (Groups == null)
if (_Groups == null)
{
Groups = new ExclusiveGroupStruct[1];
_Groups = new FasterList<ExclusiveGroupStruct>(1);
var Group = new ExclusiveGroup();
Groups[0] = Group;
_Groups.Add(Group);
Console.LogDebug("<color=orange>".FastConcat(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "- initialized ", Groups[0].ToString()), "</color>"));

//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
}
else
Console.LogDebug(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "-", Groups[0].ToString()));
}

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

public static void Add(ExclusiveGroupStruct @group)
{
for (int i = 0; i < Groups.Length; ++i)
if (Groups[i] == group)
for (int i = 0; i < _Groups.count; ++i)
if (_Groups[i] == group)
throw new Exception("temporary must be transformed in unit test");

Array.Resize(ref Groups, Groups.Length + 1);

Groups[Groups.Length - 1] = group;
GroupCompound<G2, G1>.Groups = Groups;
_Groups.Add(group);
Console.LogDebug(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "- Add ", group.ToString()));
//unit test this to check if it's necessary
// GroupCompound<G2, G1>._Groups = _Groups;
}
}

@@ -86,27 +99,28 @@ namespace Svelto.ECS
//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>
{
public static ExclusiveGroupStruct[] Groups = new ExclusiveGroupStruct[1];
static FasterList<ExclusiveGroupStruct> _Groups = new FasterList<ExclusiveGroupStruct>(1);
public static FasterReadOnlyList<ExclusiveGroupStruct> Groups => new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);

static GroupTag()
{
Groups[0] = new ExclusiveGroup();
Console.LogDebug(typeof(T).ToString() + "-" + Groups[0].ToString());
_Groups.Add(new ExclusiveGroup());
}

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

Array.Resize(ref Groups, Groups.Length + 1);

Groups[Groups.Length - 1] = group;
Console.LogDebug(typeof(T).ToString().FastConcat("- Add ", group.ToString()));
_Groups.Add(group);
#if DEBUG
GroupMap.idToName[(uint) group] = $"Compound: {typeof(T).Name}";
#endif
}

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

+ 4
- 2
Svelto.ECS/IEntitiesDB.cs View File

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

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

+ 7
- 4
Svelto.ECS/IEntityFactory.cs View File

@@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
@@ -34,7 +34,7 @@ namespace Svelto.ECS
/// itself in terms of EntityComponents to build. The Implementors are passed to fill the
/// references of the EntityComponents components. Please read the articles on my blog
/// to understand better the terminologies
/// Using this function is like building a normal entity, but the entity views
/// Using this function is like building a normal entity, but the entity components
/// are grouped by groupID to be more efficiently processed inside engines and
/// improve cache locality. Either class entityComponents and struct entityComponents can be
/// grouped.
@@ -65,8 +65,11 @@ namespace Svelto.ECS
EntityComponentInitializer BuildEntity<T>(EGID egid, T entityDescriptor, IEnumerable<object> implementors = null)
where T : IEntityDescriptor;

#if UNITY_ECS
NativeEntityFactory ToNative<T>(Unity.Collections.Allocator allocator) where T : IEntityDescriptor, new();
EntityComponentInitializer BuildEntity
(EGID egid, IComponentBuilder[] componentsToBuild, Type type, IEnumerable<object> implementors = null);

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

+ 3
- 3
Svelto.ECS/IEntityFunctions.cs View File

@@ -27,9 +27,9 @@ namespace Svelto.ECS

void SwapEntityGroup<T>(EGID fromID, EGID toId, ExclusiveGroupStruct mustBeFromGroup)
where T : IEntityDescriptor, new();
#if UNITY_ECS
NativeEntityRemove ToNativeRemove<T>() where T : IEntityDescriptor, new();
NativeEntitySwap ToNativeSwap<T>() where T : IEntityDescriptor, new();
#if UNITY_BURST
NativeEntityRemove ToNativeRemove<T>(string memberName) where T : IEntityDescriptor, new();
NativeEntitySwap ToNativeSwap<T>(string memberName) where T : IEntityDescriptor, new();
#endif
}
}

+ 0
- 3
Svelto.ECS/IReactOnSwap.cs View File

@@ -5,8 +5,5 @@ namespace Svelto.ECS
public interface IReactOnSwap<T> : IReactOnSwap where T : IEntityComponent
{
void MovedTo(ref T entityComponent, ExclusiveGroupStruct previousGroup, EGID egid);
#if SEEMS_UNNECESSARY
void MovedFrom(ref T entityComponent, EGID egid);
#endif
}
}

+ 23
- 0
Svelto.ECS/NamedExclusiveGroup.cs View File

@@ -0,0 +1,23 @@
namespace Svelto.ECS
{
/// <summary>
/// still experimental alternative to ExclusiveGroup, use this like:
/// use this like:
/// public class TriggersGroup : ExclusiveGroup<TriggersGroup> {}
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class NamedExclusiveGroup<T>
{
public static ExclusiveGroup Group = new ExclusiveGroup();
public static string name = typeof(T).FullName;

static NamedExclusiveGroup()
{
#if DEBUG
GroupMap.idToName[(uint) Group] = name;
#endif
}
// protected NamedExclusiveGroup(string recognizeAs) : base(recognizeAs) {}
// protected NamedExclusiveGroup(ushort range) : base(range) {}
}
}

+ 0
- 79
Svelto.ECS/NativeEGIDMapper.cs View File

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

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

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

public uint Count => map.count;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Entity(uint entityID)
{
unsafe
{
#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.unsafeValues[(int) findIndex];
}
}
public bool TryGetEntity(uint entityID, out T value)
{
if (map.TryFindIndex(entityID, out var index))
{
value = map.GetDirectValue(index);
return true;
}

value = default;
return false;
}
public unsafe NB<T>GetArrayAndEntityIndex(uint entityID, out uint index)
{
if (map.TryFindIndex(entityID, out index))
{
return new NB<T>(map.unsafeValues, map.count, map.capacity);
}

throw new ECSException("Entity not found");
}
public unsafe bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out NB<T> array)
{
if (map.TryFindIndex(entityID, out index))
{
array = new NB<T>(map.unsafeValues, map.count, map.capacity);
return true;
}

array = default;
return false;
}

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

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

+ 53
- 14
Svelto.ECS/QueryGroups.cs View File

@@ -1,38 +1,77 @@
using System.Threading;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

namespace Svelto.ECS
namespace Svelto.ECS.Experimental
{
public struct QueryGroups
struct GroupsList
{
public readonly FasterList<ExclusiveGroupStruct> groups;
static GroupsList()
{
groups = new FasterList<ExclusiveGroupStruct>();
}

static readonly FasterList<ExclusiveGroupStruct> groups;
public QueryGroups(FasterDictionary<uint, ITypeSafeDictionary> findGroups)
public FasterList<ExclusiveGroupStruct> reference => groups;
}

public ref struct QueryGroups
{
static readonly ThreadLocal<GroupsList> groups = new ThreadLocal<GroupsList>();

public QueryGroups(LocalFasterReadOnlyList<ExclusiveGroupStruct> findGroups)
{
var findGroupsCount = findGroups.count;
groups = new FasterList<ExclusiveGroupStruct>(findGroupsCount);
foreach (var keyvalue in findGroups)
var groupsValue = groups.Value;
var group = groupsValue.reference;

group.FastClear();
for (int i = 0; i < findGroups.count; i++)
{
groups.Add(new ExclusiveGroupStruct(keyvalue.Key));
group.Add(findGroups[i]);
}
}

public QueryGroups Except(ExclusiveGroupStruct[] groupsToIgnore)
public QueryResult Except(ExclusiveGroupStruct[] groupsToIgnore)
{
var groupsCount = groups.count;
var group = groups.Value.reference;
var groupsCount = group.count;

for (int i = 0; i < groupsToIgnore.Length; i++)
{
for (int j = 0; j < groupsCount; j++)
if (groupsToIgnore[i] == groups[j])
if (groupsToIgnore[i] == group[j])
{
groups.UnorderedRemoveAt(j);
group.UnorderedRemoveAt(j);
j--;
groupsCount--;
}
}

return this;
return new QueryResult(group);
}
public QueryResult Except(ExclusiveGroupStruct groupsToIgnore)
{
var group = groups.Value.reference;
var groupsCount = group.count;

for (int j = 0; j < groupsCount; j++)
if (groupsToIgnore == group[j])
{
group.UnorderedRemoveAt(j);
j--;
groupsCount--;
}

return new QueryResult(group);
}
}

public readonly ref struct QueryResult
{
readonly FasterReadOnlyList<ExclusiveGroupStruct> _group;
public QueryResult(FasterList<ExclusiveGroupStruct> @group) { _group = @group; }
public FasterReadOnlyList<ExclusiveGroupStruct> result => _group;
}
}

+ 5
- 2
Svelto.ECS/Serialization/DefaultSerializer.cs View File

@@ -9,9 +9,12 @@ namespace Svelto.ECS.Serialization
var _type = typeof(T);

foreach (var field in _type.GetFields())
if (field.FieldType.ContainsCustomAttribute(typeof(DoNotSerializeAttribute)) &&
{
var fieldFieldType = field.FieldType;
if (fieldFieldType.ContainsCustomAttribute(typeof(DoNotSerializeAttribute)) &&
field.IsPrivate == false)
throw new ECSException("field cannot be serialised ".FastConcat(_type.FullName));
throw new ECSException($"field cannot be serialised {fieldFieldType} in {_type.FullName}");
}

if (_type.GetProperties().Length > (ComponentBuilder<T>.HAS_EGID ? 1 : 0))
throw new ECSException("serializable entity struct must be property less ".FastConcat(_type.FullName));


+ 12
- 15
Svelto.ECS/Serialization/DefaultVersioningFactory.cs View File

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

namespace Svelto.ECS.Serialization
{
public class DefaultVersioningFactory<T> : IDeserializationFactory where T : IEntityDescriptor, new()
{
readonly IEntityFactory _factory;
readonly IEnumerable<object> _implementors;

public DefaultVersioningFactory(IEntityFactory factory)
{
_factory = factory;
}
public DefaultVersioningFactory() {}

public DefaultVersioningFactory(IEntityFactory factory, IEnumerable<object> implementors)
public DefaultVersioningFactory(IEnumerable<object> implementors)
{
_factory = factory;
_implementors = implementors;
}

public EntityComponentInitializer BuildDeserializedEntity(EGID egid,
ISerializationData serializationData,
ISerializableEntityDescriptor entityDescriptor,
int serializationType,
IEntitySerialization entitySerialization)
public EntityComponentInitializer BuildDeserializedEntity
(EGID egid, ISerializationData serializationData, ISerializableEntityDescriptor entityDescriptor
, int serializationType, IEntitySerialization entitySerialization, IEntityFactory factory
, bool enginesRootIsDeserializationOnly)
{
var initializer = _factory.BuildEntity<T>(egid, _implementors);
var entityDescriptorEntitiesToSerialize = enginesRootIsDeserializationOnly ? entityDescriptor.entitiesToSerialize : entityDescriptor.componentsToBuild;

var initializer = factory.BuildEntity(egid, entityDescriptorEntitiesToSerialize, TypeCache<T>.type, _implementors);

entitySerialization.DeserializeEntityComponents(serializationData, entityDescriptor, ref initializer,
serializationType);
entitySerialization.DeserializeEntityComponents(serializationData, entityDescriptor, ref initializer
, serializationType);

return initializer;
}


+ 19
- 22
Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs View File

@@ -43,25 +43,25 @@ namespace Svelto.ECS

uint descriptorHash = serializableEntityHeader.descriptorHash;
SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap;
var factory = serializationDescriptorMap.GetSerializationFactory(descriptorHash);
IDeserializationFactory factory = serializationDescriptorMap.GetSerializationFactory(descriptorHash);
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash);

//default factory
//todo: we have a default factory, why don't we always register that instead?
if (factory == null)
{
var initializer = _enginesRoot.BuildEntity(egid,
_enginesRoot._isDeserializationOnly ? entityDescriptor.entitiesToSerialize
: entityDescriptor.componentsToBuild);
DeserializeEntityComponents(serializationData, entityDescriptor, ref initializer, serializationType);
return initializer;
}
// //default factory
// //todo: we have a default factory, why don't we always register that instead?
// if (factory == null)
// {
// var initializer = _enginesRoot.BuildEntity(egid,
// _enginesRoot._isDeserializationOnly ? entityDescriptor.entitiesToSerialize
// : entityDescriptor.componentsToBuild, entityDescriptor.realType);
//
// DeserializeEntityComponents(serializationData, entityDescriptor, ref initializer, serializationType);
//
// return initializer;
// }

//custom factory
return factory.BuildDeserializedEntity(egid, serializationData, entityDescriptor, serializationType,
this);
this, this._enginesRoot.GenerateEntityFactory(), _enginesRoot._isDeserializationOnly);
}

public void DeserializeEntity(ISerializationData serializationData, int serializationType)
@@ -120,14 +120,11 @@ namespace Svelto.ECS
uint descriptorHash = serializableEntityComponent.descriptorHash;
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash);

var entitySubmitOperation = new EntitySubmitOperation(
EntitySubmitOperationType.Swap,
localEgid,
toEgid,
entityDescriptor.componentsToBuild);
var entitySubmitOperation = new EntitySubmitOperation(EntitySubmitOperationType.Swap,
localEgid, toEgid, entityDescriptor.componentsToBuild);

_enginesRoot.CheckRemoveEntityID(localEgid);
_enginesRoot.CheckAddEntityID(toEgid);
_enginesRoot.CheckRemoveEntityID(localEgid, entityDescriptor.realType);
_enginesRoot.CheckAddEntityID(toEgid, entityDescriptor.realType);

_enginesRoot.QueueEntitySubmitOperation(entitySubmitOperation);
}
@@ -141,7 +138,7 @@ namespace Svelto.ECS
SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap;
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash);

_enginesRoot.CheckRemoveEntityID(egid);
_enginesRoot.CheckRemoveEntityID(egid, entityDescriptor.realType);

var entitySubmitOperation = new EntitySubmitOperation(
EntitySubmitOperationType.Remove,


+ 9
- 4
Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs View File

@@ -26,6 +26,7 @@ namespace Svelto.ECS
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
// Assembly executingAssembly = Assembly.GetExecutingAssembly();

Type d1 = typeof(DefaultVersioningFactory<>);
foreach (Assembly assembly in assemblies)
{
// if (assembly.GetReferencedAssemblies().Contains(executingAssembly.GetName()))
@@ -38,7 +39,7 @@ namespace Svelto.ECS
{
var descriptor = Activator.CreateInstance(type) as ISerializableEntityDescriptor;

RegisterEntityDescriptor(descriptor);
RegisterEntityDescriptor(descriptor, type, d1);
}
}
}
@@ -60,7 +61,7 @@ namespace Svelto.ECS
}
}

void RegisterEntityDescriptor(ISerializableEntityDescriptor descriptor)
void RegisterEntityDescriptor(ISerializableEntityDescriptor descriptor, Type type, Type d1)
{
if (descriptor == null)
{
@@ -78,6 +79,10 @@ namespace Svelto.ECS
#endif

_descriptors[descriptorHash] = descriptor;
Type[] typeArgs = {type};
var makeGenericType = d1.MakeGenericType(typeArgs);
var instance = Activator.CreateInstance(makeGenericType);
_factories.Add(descriptorHash, instance as IDeserializationFactory);
}

public ISerializableEntityDescriptor GetDescriptorFromHash(uint descriptorID)
@@ -92,13 +97,13 @@ namespace Svelto.ECS

public IDeserializationFactory GetSerializationFactory(uint descriptorID)
{
return _factories.TryGetValue(descriptorID, out var factory) ? factory : null;
return _factories[descriptorID];
}

public void RegisterSerializationFactory<Descriptor>(IDeserializationFactory deserializationFactory)
where Descriptor : ISerializableEntityDescriptor, new()
{
_factories.Add(SerializationEntityDescriptorTemplate<Descriptor>.hash, deserializationFactory);
_factories[SerializationEntityDescriptorTemplate<Descriptor>.hash] = deserializationFactory;
}




+ 1
- 1
Svelto.ECS/Serialization/HashNameAttribute.cs View File

@@ -10,6 +10,6 @@ namespace Svelto.ECS.Serialization
_name = name;
}
internal string _name;
internal readonly string _name;
}
}

+ 4
- 3
Svelto.ECS/Serialization/IDeserializationFactory.cs View File

@@ -2,8 +2,9 @@ namespace Svelto.ECS.Serialization
{
public interface IDeserializationFactory
{
EntityComponentInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData,
ISerializableEntityDescriptor entityDescriptor, int serializationType,
IEntitySerialization entitySerialization);
EntityComponentInitializer BuildDeserializedEntity
(EGID egid, ISerializationData serializationData, ISerializableEntityDescriptor entityDescriptor
, int serializationType, IEntitySerialization entitySerialization, IEntityFactory factory
, bool enginesRootIsDeserializationOnly);
}
}

+ 3
- 0
Svelto.ECS/Serialization/ISerializableEntityDescriptor.cs View File

@@ -1,8 +1,11 @@
using System;

namespace Svelto.ECS.Serialization
{
public interface ISerializableEntityDescriptor : IEntityDescriptor
{
uint hash { get; }
ISerializableComponentBuilder[] entitiesToSerialize { get; }
Type realType { get; }
}
}

+ 5
- 3
Svelto.ECS/Serialization/PartialSerializer.cs View File

@@ -25,11 +25,13 @@ namespace Svelto.ECS.Serialization
{
if (myAttributes[j] is PartialSerializerFieldAttribute)
{
if (myMembers[i].FieldType == typeof(EGID))
throw new ECSException("EGID fields cannot be serialised ".FastConcat(myType.FullName));
var fieldType = myMembers[i].FieldType;
if (fieldType.ContainsCustomAttribute(typeof(DoNotSerializeAttribute)) &&
myMembers[i].IsPrivate == false)
throw new ECSException($"field cannot be serialised {fieldType} in {myType.FullName}");

var offset = Marshal.OffsetOf<T>(myMembers[i].Name);
var sizeOf = (uint)Marshal.SizeOf(myMembers[i].FieldType);
var sizeOf = (uint)Marshal.SizeOf(fieldType);
offsets.Add(((uint) offset.ToInt32(), sizeOf));
totalSize += sizeOf;
}


+ 3
- 5
Svelto.ECS/Serialization/SerializableComponentBuilder.cs View File

@@ -14,7 +14,7 @@ namespace Svelto.ECS.Serialization
public class SerializableComponentBuilder<T> : ComponentBuilder<T>, ISerializableComponentBuilder
where T : unmanaged, IEntityComponent
{
public static readonly uint SIZE = UnsafeUtils.SizeOf<T>();
public static readonly uint SIZE = (uint) MemoryUtilities.SizeOf<T>();
public void Serialize
(uint entityID, ITypeSafeDictionary dictionary, ISerializationData serializationData
@@ -28,8 +28,7 @@ namespace Svelto.ECS.Serialization
throw new ECSException("Entity Serialization failed");
}

var values = safeDictionary.unsafeValues;
ref T val = ref values[(int) index];
ref T val = ref safeDictionary.GetDirectValueByRef(index);

serializationData.dataPos = (uint) serializationData.data.count;

@@ -50,8 +49,7 @@ namespace Svelto.ECS.Serialization
throw new ECSException("Entity Deserialization failed");
}

var values = safeDictionary.unsafeValues;
ref T val = ref values[(int) index];
ref T val = ref safeDictionary.GetDirectValueByRef(index);

componentSerializer.DeserializeSafe(ref val, serializationData);
}


+ 19
- 18
Svelto.ECS/Serialization/SerializableEntityDescriptor.cs View File

@@ -18,14 +18,14 @@ namespace Svelto.ECS.Serialization
{
IComponentBuilder[] defaultEntities = EntityDescriptorTemplate<TType>.descriptor.componentsToBuild;

var hashNameAttribute = _type.GetCustomAttribute<HashNameAttribute>();
var hashNameAttribute = Type.GetCustomAttribute<HashNameAttribute>();
if (hashNameAttribute == null)
{
throw new Exception(
"HashName attribute not found on the serializable type ".FastConcat(_type.FullName));
"HashName attribute not found on the serializable type ".FastConcat(Type.FullName));
}

_hash = DesignatedHash.Hash(Encoding.ASCII.GetBytes(hashNameAttribute._name));
Hash = DesignatedHash.Hash(Encoding.ASCII.GetBytes(hashNameAttribute._name));

var (index, dynamicIndex) = SetupSpecialEntityComponent(defaultEntities, out ComponentsToBuild);
if (index == -1)
@@ -36,13 +36,13 @@ namespace Svelto.ECS.Serialization
// Stores the hash of this EntityDescriptor
ComponentsToBuild[index] = new ComponentBuilder<SerializableEntityComponent>(new SerializableEntityComponent
{
descriptorHash = _hash
descriptorHash = Hash
});

// If the current serializable is an ExtendibleDescriptor, I have to update it.
if (dynamicIndex != -1)
{
ComponentsToBuild[dynamicIndex] = new ComponentBuilder<EntityInfoComponentView>(new EntityInfoComponentView
ComponentsToBuild[dynamicIndex] = new ComponentBuilder<EntityInfoViewComponent>(new EntityInfoViewComponent
{
componentsToBuild = ComponentsToBuild
});
@@ -50,18 +50,18 @@ namespace Svelto.ECS.Serialization

/////
var entitiesToSerialize = new FasterList<ISerializableComponentBuilder>();
_entityComponentsToSerializeMap = new FasterDictionary<RefWrapper<Type>, ISerializableComponentBuilder>();
EntityComponentsToSerializeMap = new FasterDictionary<RefWrapper<Type>, ISerializableComponentBuilder>();
foreach (IComponentBuilder e in defaultEntities)
{
if (e is ISerializableComponentBuilder serializableEntityBuilder)
{
var entityType = serializableEntityBuilder.GetEntityComponentType();
_entityComponentsToSerializeMap[new RefWrapper<Type>(entityType)] = serializableEntityBuilder;
EntityComponentsToSerializeMap[new RefWrapper<Type>(entityType)] = serializableEntityBuilder;
entitiesToSerialize.Add(serializableEntityBuilder);
}
}

_entitiesToSerialize = entitiesToSerialize.ToArray();
EntitiesToSerialize = entitiesToSerialize.ToArray();
}

static (int indexSerial, int indexDynamic) SetupSpecialEntityComponent
@@ -75,13 +75,13 @@ namespace Svelto.ECS.Serialization

for (var i = 0; i < length; ++i)
{
if (defaultEntities[i].GetEntityComponentType() == _serializableStructType)
if (defaultEntities[i].GetEntityComponentType() == SerializableStructType)
{
indexSerial = i;
--newLenght;
}

if (defaultEntities[i].GetEntityComponentType() == EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW)
if (defaultEntities[i].GetEntityComponentType() == ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW)
{
indexDynamic = i;
}
@@ -94,16 +94,17 @@ namespace Svelto.ECS.Serialization
return (indexSerial, indexDynamic);
}

public IComponentBuilder[] componentsToBuild => ComponentsToBuild;
public uint hash => _hash;
public ISerializableComponentBuilder[] entitiesToSerialize => _entitiesToSerialize;
public IComponentBuilder[] componentsToBuild => ComponentsToBuild;
public uint hash => Hash;
public Type realType => Type;
public ISerializableComponentBuilder[] entitiesToSerialize => EntitiesToSerialize;

static readonly IComponentBuilder[] ComponentsToBuild;
static readonly FasterDictionary<RefWrapper<Type>, ISerializableComponentBuilder> _entityComponentsToSerializeMap;
static readonly ISerializableComponentBuilder[] _entitiesToSerialize;
static readonly FasterDictionary<RefWrapper<Type>, ISerializableComponentBuilder> EntityComponentsToSerializeMap;
static readonly ISerializableComponentBuilder[] EntitiesToSerialize;

static readonly uint _hash;
static readonly Type _serializableStructType = typeof(SerializableEntityComponent);
static readonly Type _type = typeof(TType);
static readonly uint Hash;
static readonly Type SerializableStructType = typeof(SerializableEntityComponent);
static readonly Type Type = typeof(TType);
}
}

+ 2
- 4
Svelto.ECS/Serialization/SerializerExt.cs View File

@@ -1,5 +1,3 @@
using System;

namespace Svelto.ECS.Serialization {
public static class SerializerExt
{
@@ -15,7 +13,7 @@ namespace Svelto.ECS.Serialization {
// size == 0 is a special case when we don't know the size in advance
if (componentSerializer.size != 0 && serializationData.dataPos != posBefore + componentSerializer.size)
{
throw new IndexOutOfRangeException(
throw new System.IndexOutOfRangeException(
$"Size mismatch when serializing {typeof(T).FullName} using {componentSerializer.GetType().FullName}, "
+ $"expected offset {posBefore + componentSerializer.size}, got {serializationData.dataPos}");
}
@@ -34,7 +32,7 @@ namespace Svelto.ECS.Serialization {
#if DEBUG && !PROFILE_SVELTO
if (componentSerializer.size != 0 && serializationData.dataPos != posBefore + componentSerializer.size)
{
throw new IndexOutOfRangeException(
throw new System.IndexOutOfRangeException(
$"Size mismatch when deserializing {typeof(T).FullName} using {componentSerializer.GetType().FullName}, "
+ $"expected offset {posBefore + componentSerializer.size}, got {serializationData.dataPos}");
}


+ 13
- 4
Svelto.ECS/SetEGIDWithoutBoxing.cs View File

@@ -1,4 +1,4 @@
using System;
using System.Runtime.CompilerServices;

namespace Svelto.ECS.Internal
{
@@ -14,16 +14,25 @@ namespace Svelto.ECS.Internal
{
if (ComponentBuilder<T>.HAS_EGID)
{
#if !ENABLE_IL2CPP
var method = typeof(Trick).GetMethod(nameof(Trick.SetEGIDImpl)).MakeGenericMethod(typeof(T));
return (SetEGIDWithoutBoxingActionCast<T>) Delegate.CreateDelegate(
return (SetEGIDWithoutBoxingActionCast<T>) System.Delegate.CreateDelegate(
typeof(SetEGIDWithoutBoxingActionCast<T>), method);
#else
return (ref T target, EGID egid) =>
{
var needEgid = (target as INeedEGID);
needEgid.ID = egid;
target = (T) needEgid;
};
#endif
}

return null;
}

static class Trick
{
{
public static void SetEGIDImpl<U>(ref U target, EGID egid) where U : struct, INeedEGID
{
target.ID = egid;


+ 4
- 1
Svelto.ECS/SimpleEntitiesSubmissionScheduler.cs View File

@@ -7,13 +7,16 @@ namespace Svelto.ECS
{
public void SubmitEntities()
{
_onTick.Invoke();
if (paused == false)
_onTick.Invoke();
}
EnginesRoot.EntitiesSubmitter IEntitiesSubmissionScheduler.onTick
{
set => _onTick = value;
}
public bool paused { get; set; }

public void Dispose() { }



+ 16
- 0
Svelto.ECS/Svelto.ECS.asmdef View File

@@ -4,6 +4,7 @@
"Unity.Entities",
"Unity.Collections",
"Unity.Burst",
"Unity.Jobs",
"Svelto.Common_3"
],
"includePlatforms": [],
@@ -20,6 +21,21 @@
"name": "com.unity.entities",
"expression": "",
"define": "UNITY_ECS"
},
{
"name": "com.unity.burst",
"expression": "",
"define": "UNITY_BURST"
},
{
"name": "com.unity.collections",
"expression": "",
"define": "UNITY_COLLECTIONS"
},
{
"name": "com.unity.jobs",
"expression": "",
"define": "UNITY_JOBS"
}
],
"noEngineReferences": false

+ 1
- 1
Svelto.ECS/Svelto.ECS.csproj View File

@@ -15,7 +15,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Svelto.Common\Svelto.Common.csproj" />
<ProjectReference Include="..\Svelto.Common_3\Svelto.Common.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.2" />


+ 58
- 0
Svelto.ECS/TupleRef.cs View File

@@ -0,0 +1,58 @@
namespace Svelto.ECS
{
public readonly ref struct TupleRef<T1> where T1 : struct, IEntityComponent
{
public readonly EntityCollections<T1> entities;
public readonly GroupsEnumerable<T1> groups;

public TupleRef(in EntityCollections<T1> entityCollections, in GroupsEnumerable<T1> groupsEnumerable)
{
this.entities = entityCollections;
groups = groupsEnumerable;
}
}

public readonly ref struct TupleRef<T1, T2> where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
{
public readonly EntityCollections<T1, T2> entities;
public readonly GroupsEnumerable<T1, T2> groups;

public TupleRef(in EntityCollections<T1, T2> entityCollections, in GroupsEnumerable<T1, T2> groupsEnumerable)
{
this.entities = entityCollections;
groups = groupsEnumerable;
}
}

public readonly ref struct TupleRef<T1, T2, T3> where T1 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T3 : struct, IEntityComponent
{
public readonly EntityCollections<T1, T2, T3> entities;
public readonly GroupsEnumerable<T1, T2, T3> groups;

public TupleRef
(in EntityCollections<T1, T2, T3> entityCollections, in GroupsEnumerable<T1, T2, T3> groupsEnumerable)
{
this.entities = entityCollections;
groups = groupsEnumerable;
}
}
public readonly ref struct TupleRef<T1, T2, T3, T4> where T1 : struct, IEntityComponent
where T2 : struct, IEntityComponent
where T3 : struct, IEntityComponent
where T4 : struct, IEntityComponent
{
public readonly EntityCollections<T1, T2, T3, T4> entities;
public readonly GroupsEnumerable<T1, T2, T3, T4> groups;

public TupleRef
(in EntityCollections<T1, T2, T3, T4> entityCollections
, in GroupsEnumerable<T1, T2, T3, T4> groupsEnumerable)
{
this.entities = entityCollections;
groups = groupsEnumerable;
}
}
}

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

@@ -6,9 +6,9 @@ namespace Svelto.ECS
{
public static ITypeSafeDictionary Create()
{
return new TypeSafeDictionary<T>();
return new TypeSafeDictionary<T>(1);
}
public static ITypeSafeDictionary Create(uint size)
{
return new TypeSafeDictionary<T>(size);


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

Loading…
Cancel
Save